cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

CAP - Node.js - Before Handler

yuliya_seliukova
Associate
Associate
0 Likes
4,328

Environment

  • cds => 4.6.5
  • node => v12.18.3

Steps to reproduce:

I create simple CAP application with the following structure

schema.cds

using {
    User
} from '@sap/cds/common';

namespace example;

entity Project {
    key ID          : UUID;
        name        : String;
        owner       : Composition of many ProjectOwner
                          on owner.project = $self;
}

entity ProjectOwner {
    key user    : User;
    key project : Association to Project;
}

srv/service.cds

using {example as custom} from '../db/schema';

service Example {

    entity Projects as projection on custom.Project;

    entity ProjectOwners as projection on custom.ProjectOwner {
        * , project
    };

}

srv/service.js

const cds = require("@sap/cds");
module.exports = (srv) => {
    srv.before(['UPDATE', 'DELETE', 'CREATE'], 'ProjectOwners', async (req) => {
        var projectId = req.data.project_ID;
        console.log(`Project id: ${projectId}`);
    });
}; 

The task is the following: on create, update and delete we need to check amount of owners for project. If amount of owners is less than 2 - we need to throw error.

I adopted service in the following way

const cds = require("@sap/cds");
module.exports = (srv) => {
    srv.before(['UPDATE', 'DELETE', 'CREATE'], 'ProjectOwners', async (req) => {
        var projectId = req.data.project_ID;
        console.log(`Project id: ${projectId}`);
        if (projectId) {
            const tx = cds.transaction(req);
            const { ProjectOwner } = srv.entities("example");
            const foundProjectOwners = await tx.run(
                SELECT.from(ProjectOwner).where({ project_ID: projectId })
            );
            // depending on amount of project owners do validation
            // ...
        } else {
            req.reject(400, 'No value found for project_ID!');
        }     
    });
    srv.before(['UPDATE', 'DELETE', 'CREATE'], "Projects", async (req) => {
        const foundProjectOwners = req.data.owners;
        // depending on amount of project owners do validation
        // ...
    });
};

--- --- --- --- ---

Questions:

Case 1:

Owners can be added in 2 ways:

1) POST http://localhost:4004/example/Projects

Body:

{
    "name": "alice project",
    "owner": [{
        "user": "alice"
    }]
}

2) POST http://localhost:4004/example/Projects({PROJECT_ID})/owner

Body:

{
    "user": "alice"
}

In first case handler is srv.before(['UPDATE', 'DELETE', 'CREATE'], "Projects",

in second case srv.before(['UPDATE', 'DELETE', 'CREATE'], 'ProjectOwners'

So the logic

// depending on amount of project owners do validation
// ...

will be duplicated in both handlers.

Is there a way to avoid this ?

--- --- --- --- ---

Case 2:

When I delete owner through the following request

DELETE http://localhost:4004/example/Projects({PROJECT_ID})/owner/alice

projectId is equal to undefined.


Strange that projectId is recognized for CREATE and UPDATE, but not for DELETE.

What I do wrong here ? Or it is an issue in CAP itself ?

Thank for advance !

View Entire Topic
david_kunz2
Product and Topic Expert
Product and Topic Expert
0 Likes

Hi,

Case 1:
Yes, there are multiple ways to create an entity, either directly or through the composition.
Therefore you need to run the logic for both handlers.
Case 2:
The DELETE request is sent to the `ProjectOwners` entity, therefore `req.data` doesn't contain the project ID. Can you look into `req.query.DELETE`? I expect the project ID to be present there.

Best regards,
David