on 11-01-2023 12:38 PM
Hi community,
I am new to CAP and working on a learning project that displays employee appointments in a calendar app.
I am trying to add some data to my appointment entity upon a read request. Within my custom service implementation I try to do 2 things:
(1) Add some calendar type data that is randomly calculated (determines the color of the calendar entry in the app).
(2) The appointment entity contains the employee id. During the service implementation I would like to query some more employee data and add it to the appointment entity.
schema:
entity Vacations {
key ID : UUID;
startDate : Date;
endDate : Date;
employeeID: String;
employee : Association to Employees;
note : String;
}
entity Employees {
key ID : UUID;
firstName : String;
lastName : String;
vacations : Composition of many Vacations on vacations.employeeID = $self.ID;
}
service.cds
@impl: 'srv/appointments-service.js'
service appointments {
entity appointments as projection on praxissuite.Vacations {
*,
null as type : String,
null as description : String
};
entity Employee as projection on praxissuite.Employees;
}
service.js
module.exports = (srv) => {
const { appointments, Employees } = cds.entities("pd.praxissuite");
srv.after("READ", "appointments", (each) => {
each.type = createAppointmentType();
console.log(each.type);
let q1 = readEmployee();
each.description = q1.firstname;
console.log(q1);
console.log(each);
});
async function readEmployee(employeeID) {
const q1 = await SELECT.one.from(Employees);
return q1;
}
function createAppointmentType() {
const max = 20;
const randomNumber = Math.floor(Math.random() * max);
let type = "";
switch (randomNumber) {
case 1:
type = "Type01";
break;
case 2:
type = "Type02";
break;
case 3:
type = "Type03";
break;
case 4:
type = "Type04";
break;
case 5:
type = "Type05";
break;
case 6:
type = "Type06";
break;
case 7:
type = "Type07";
break;
case 8:
type = "Type08";
break;
case 9:
type = "Type09";
break;
case 10:
type = "Type10";
break;
case 11:
type = "Type11";
break;
case 12:
type = "Type12";
break;
case 13:
type = "Type13";
break;
case 14:
type = "Type14";
break;
case 15:
type = "Type15";
break;
case 16:
type = "Type16";
break;
case 17:
type = "Type17";
break;
case 18:
type = "Type18";
break;
case 19:
type = "Type19";
break;
case 20:
type = "Type20";
break;
default:
type = "Type06";
break;
}
return type;
}
};
Adding the type to the appointment works fine. I can't manage to query employee data and add it to the appointment as description. The select only returns the query, not its result.
The documentation says to return the query result I need to await the Select call, but I am not sure how to do this properly, since when I change the srv.on... part and the function within to also be async, the type data is not added to the appointment entity anymore.
Hope someone can bring some light into the darkness. Thanks a lot in advance.
Br,
Philipp
Hi Philipp,
the issue may be caused by parameter "each" in your srv.AFTER event handler that you use like an idividual appointment. Acc'dg to docs (below) the event handler is a function with params "req" and "results", the latter being a collection.
CAP - Method Srv.After (request)
Instead of "each", try "appointments" and access individual records in the function body either with a for-loop or by calling
appointments.map( appt => { <do sth.> } )
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Ulrich,
thank you a lot for your answer.
Ahh shame on me, of course you're right. However I still don't get it to work. Here's what my service implementation looks like now:
module.exports = (srv) => {
const {
appointments,
Employees
} = cds.entities('pd.praxissuite');
srv.after('READ', 'appointments', async (appointments) => {
console.log(appointments);
await appointments.map( async appt => {
appt.type = createAppointmentType();
const employee = await readEmployee(appt.employeeID);
appt.description = employee.firstName;
})
console.log(appointments);
});
async function readEmployee(employeeID) {
const employee = await SELECT.one.from(Employees).where({ID:employeeID});
console.log(employee);
return employee;
};
I left out the createAppointmentType. The console output is:
[
{
ID: '5c7486d2-4ad0-4310-a852-a2dc231e1728',
description: null,
employeeID: 'feab1bd0-575a-4712-bf2e-4fd034650324',
endDate: '2023-10-14',
startDate: '2023-10-01',
type: null
}
]
[
{
ID: '5c7486d2-4ad0-4310-a852-a2dc231e1728',
description: null,
employeeID: 'feab1bd0-575a-4712-bf2e-4fd034650324',
endDate: '2023-10-14',
startDate: '2023-10-01',
type: 'Type02'
}
]
{
ID: 'feab1bd0-575a-4712-bf2e-4fd034650324',
firstName: 'Philipp',
lastName: 'Doelker'
}
So getting the appointment type and adding it to the result array works.
Also fetching the employee data works, but I still can't manage to get in into my response. It seems the response is returned before the select is finished and the query result is handed over to my "after READ" implementation.
You don't need custom code for that, you can just model it in the service:
entity appointments as projection on praxissuite.Vacations {
*,
employee.ID as employeeID : String,
null as type : String,
employee.firstName as description : String
};
srv.after('READ', 'appointments', each => {
each.type = createAppointmentType();
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Very good hint, thank a lot!
I tried:
@impl: 'srv/appointments-service.js'
service appointments {
entity appointments as projection on praxissuite.Vacations {
*,
employee.ID as employeeID : String,
null as type : String,
employee.firstName as description : String
};
entity Employee as projection on praxissuite.Employees;
}
Leading to syntax error:@impl: 'srv/appointments-service.js'
service appointments {
entity appointments as projection on praxissuite.Vacations {
*,
null as type : String,
employee : redirected to Employee,
employee.firstName as description : String
};
entity Employee as projection on praxissuite.Employees;
}
Which resolves the syntax error but doesn't fill the description attribute...
I feel you're making this far more complex than it has to be. I tested the below on my side, and it works great.
Try to use managed associations where possible, they have implicit foreign keys so is less work to manage. There is now also a new feature allowing you to use Calculated Elements, so no need to have a custom service layer for non-database elements.
cds:
using {cuid} from '@sap/cds/common';
context praxissuite {
entity Vacations : cuid {
startDate : Date;
endDate : Date;
note : String;
employee : Association to Employees;
description : String = employee.firstName;
type : String = null;
}
entity Employees : cuid {
firstName : String;
lastName : String;
vacations : Composition of many Vacations
on vacations.employee = $self;
}
}
service appointments {
entity appointments as projection on praxissuite.Vacations;
}
js:module.exports = srv => {
srv.after('READ', srv.entities.appointments, each => {
const max = 20;
each.type = `Type${Math.floor(Math.random() * max + 1).toString()}`;
});
}
User | Count |
---|---|
80 | |
9 | |
9 | |
7 | |
7 | |
6 | |
6 | |
6 | |
5 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.