cancel
Showing results for 
Search instead for 
Did you mean: 

Cannot create draft for Node.js CAP Service in custom handler

Steven-Mason
Explorer
0 Kudos

I have created a simple project to showcase the issue that I am having at repo: draft-concern.

In this example repo, I have created a very simple service with entity project that has been annotated with @odata.draft.enabled. In the same service, I have created an additional action (createDraft) to add my custom handler code.

using { Customers as schemaCustomers } from '../db/schema';

service CustomerService{
  entity Customers as projection on schemaCustomers;
  annotate Customers with @odata.draft.enabled;

  action createDraft() returns {ID: UUID};
  
}

If I run a simple post command to the Customer entity set, a draft is created as expected:

curl -X POST -H "Content-Type:application/json" -d '{}' http://localhost:4004/customer/Customers
{"@odata.context":"$metadata#Customers/$entity","ID":"1a8b20eb-c044-422e-a137-d7c724c6f91d","CustomerID":null,"CustomerName":null,"IsActiveEntity":false,"HasActiveEntity":false,"HasDraftEntity":false}%

Specifically, you will see that the IsActiveEntity field is set to false since the draft has not been activated yet.

The issue I am running into in my project is when I try to replicate this behavior in a custom handler. In this case I have a very simple handler for the "createDrafts" action defined on the service.

const cds = require("@sap/cds")

module.exports = cds.service.impl( async (srv) => {
  srv.on('createDraft', async (req) => {
    const { Customers } = srv.entities
    const { ID } =  await srv.post(Customers, {})
    return { ID }
  })
})

When I call this action against the service, the behavior is not as expected:

curl -X POST -H "Content-Type:application/json" -d '{}' http://localhost:4004/customer/createDraft
{"@odata.context":"$metadata#CustomerService.return_CustomerService_createDraft","ID":"9d552f07-275c-4a7f-a79f-0fb7e59d94f9"}%

curl -X GET http://localhost:4004/customer/Customers
{"@odata.context":"$metadata#Customers","value":[{"ID":"9d552f07-275c-4a7f-a79f-0fb7e59d94f9","CustomerID":null,"CustomerName":null,"IsActiveEntity":true,"HasActiveEntity":false,"HasDraftEntity":false}]}%
                                         

Here you will see that the IsActiveEntity field has been set to true even though the draft has not been activated.

While in this example, I am using the post convenience method for a CAP service in my custom handler, the same results have occurred regardless of the way I have attempted a post on the Customer entity set via a custom handler.

This appears broken to me due to the fact that an direct POST command to the Customers entity set is not behaving in the same manner as the post method within the custom action of the service.

Any help on how to properly mimic the POST command within a custom handler where a draft entry gets created (with IsActiveEntity is set to false) would be truly appreciated!

cds -v
@sap/cds: 6.3.2
@sap/cds-compiler: 3.4.2
@sap/cds-dk: 4.1.5
@sap/cds-foss: 4.0.0
@sap/cds-mtx: -- missing --
Node.js: v16.18.1
draft-concern: 1.0.0
home: /Users/stevenmason/dev/cap/draft-concern/node_modules/@sap/cds<br>
View Entire Topic
Youssef-kh1
Explorer
0 Kudos

Hi,

I see that you have resolved the problem in your code.
I've reviewed your approach, and it seems to be aligned with what I've implemented in my project. However, I'm encountering an unexpected behavior when executing the DUPLICATE action in my TypeScript-based project.

Here's a breakdown of my code:
In OutletsController.ts, I have a method for handling outlet duplication:

public async duplicateOutlet(req: Request): Promise<any> {
try {
let selectedItemID: string;
const firstParam = req.params[0] as { ID: string };
console.log('firstParam:', firstParam.ID);

selectedItemID = firstParam.ID;

const { Outlets } = cds.entities(Constants.CDS_NAMESPACE);
const sQuery = SELECT.from(Outlets).where`(ID) = ${selectedItemID}`;
let selectedItem = await cds.run(sQuery);
console.log('selectedItem:', selectedItem);

// Setting IsActiveEntity to true (unexpected behavior)
selectedItem[0].IsActiveEntity = true;
const newItemData = { ...selectedItem[0] };
delete newItemData.ID;
console.log('newItemData: ',newItemData);

const insertedItem = await INSERT.into(Outlets).entries(newItemData);
console.log(insertedItem);
return insertedItem;
} catch (error) {
console.error('Error duplicating outlet:', error);
throw error;
}
}
In service.cds, I've defined the Outlets entity with the DUPLICATE action:
service.cds

@odata.draft.enabled
entity Outlets as select ID, code @mandatory, description from planification.Outlets actions {
action DUPLICATE() returns Outlets;
}

In service.ts, I'm binding the DUPLICATE action to the duplicateOutlet method in my controller:
this.on(
"DUPLICATE",
this.outletsController.duplicateOutlet.bind(this.outletsController)
);

Despite following similar patterns to your solution, I'm facing issues where the IsActiveEntity field is unexpectedly set to true after executing the DUPLICATE action.
Your guidance on resolving this unexpected behavior would be greatly appreciated.

Youssef.