SAP / cloud-sdk-js

Use the SAP Cloud SDK for JavaScript / TypeScript to reduce development effort when building applications on SAP Business Technology Platform that communicate with SAP solutions and services such as SAP S/4HANA Cloud, SAP SuccessFactors, and many others.
Apache License 2.0
161 stars 51 forks source link

Incosistent api initialization #4769

Closed lovis-ff closed 2 weeks ago

lovis-ff commented 2 weeks ago

Describe the bug The result of the initialisation of an odata api from the sdk is dependent on the apis initialised along-side. This has to be considered when using the sdk where the sdk could be initialised in different places with different apis being used.

To be precise, I am using the SalesQuotation odata api generated from the sap provided edmx file. I am using the sdk in different places to split my code for different responsibilities. This results in errors which are hard to understand/debug and navigation properties being omitted on deep create requests resulting in wrong data.

To Reproduce

1. Error on navigation properties Steps to reproduce the behavior:

  1. Set up the sales quotation api.
  2. Create a sample salesQuotation entity. This is just an example. The salesQuotation entity could be also retrieved via a get request or created from a JSON. In my particular case I retrieve a salesQuotation via a get request to modify it and than create a salesQuotation afterwards (with deep inserts).
    
    const apiSalesQuotationSrv =
    OP_API_SALES_QUOTATION_SRV_0001.opApiSalesQuotationSrv0001();

const salesQuotation = apiSalesQuotationSrv.salesQuotationApi .entityBuilder() .salesQuotation("ABC") .toItem([ apiSalesQuotationSrv.salesQuotationItemApi .entityBuilder() .salesQuotationItem("0001") .salesQuotationItemText("0001 text") .toSalesQuotation(null) .salesQuotationItemCategory("TAN") .material("AAA") .build(), ]) .build();

Note that the `toSalesQuotation` navigation property was retrieved as `null` in my get request via the same api so we are also setting it explictely to `null` in this example.

3. Initialise a second sdk to do the create request.

const apiSalesQuotationSrv2 = OP_API_SALES_QUOTATION_SRV_0001.opApiSalesQuotationSrv0001();

const rb = apiSalesQuotationSrv2.salesQuotationApi .requestBuilder() .create(salesQuotation);

4.  See error 

TypeError: Cannot read properties of undefined (reading '_fieldName')


5. Note the difference, when the `salesQuotationItemApi` gets initialised before as 

const apiSalesQuotationSrv2 = OP_API_SALES_QUOTATION_SRV_0001.opApiSalesQuotationSrv0001();

const { salesQuotationItemApi } = apiSalesQuotationSrv2;

const rb = apiSalesQuotationSrv2.salesQuotationApi .requestBuilder() .create(salesQuotation);

where no error will be thrown.

**2. Omitted navigation properties**
Steps to reproduce the behavior:

1. Set up the sales quotation api.
2. Create a sample salesQuotation entity. This is just an example. The salesQuotation entity could be also retrieved via a get request or created from a JSON.

const apiSalesQuotationSrv = OP_API_SALES_QUOTATION_SRV_0001.opApiSalesQuotationSrv0001();

const salesQuotation = apiSalesQuotationSrv.salesQuotationApi .entityBuilder() .salesQuotation("ABC") .toItem([ apiSalesQuotationSrv.salesQuotationItemApi .entityBuilder() .salesQuotationItem("0001") .salesQuotationItemText("0001 text") .salesQuotationItemCategory("TAN") .material("AAA") .toPartner([ apiSalesQuotationSrv.salesQuotationItemPartnerApi .entityBuilder() .partnerFunction("AB") .build(), ]) .build(), ]) .build();

Note the `toPartner` navigation property on the salesQuotationItem entities. 

3. Initialise a second sdk to do the create request.

const apiSalesQuotationSrv2 = OP_API_SALES_QUOTATION_SRV_0001.opApiSalesQuotationSrv0001();

const rb = apiSalesQuotationSrv2.salesQuotationApi .requestBuilder() .create(salesQuotation);

4.  Observe the payload of the request `rb.requestConfig.payload` missing the `to_Partner` navigation property.

{ SalesQuotation: 'ABC', to_Item: [ { SalesQuotationItem: '0001', SalesQuotationItemText: '0001 text', SalesQuotationItemCategory: 'TAN', Material: 'AAA' } ] }


5. Note the difference, when the `salesQuotationItemApi` and `salesQuotationItemPartnerApi` get initialised before as 

const apiSalesQuotationSrv2 = OP_API_SALES_QUOTATION_SRV_0001.opApiSalesQuotationSrv0001(); const { salesQuotationItemApi, salesQuotationItemPartnerApi } = apiSalesQuotationSrv2; const rb = apiSalesQuotationSrv2.salesQuotationApi .requestBuilder() .create(salesQuotation);

where the payload then looks as expected, being

{ "SalesQuotation": "ABC", "to_Item": [ { "SalesQuotationItem": "0001", "SalesQuotationItemText": "0001 text", "SalesQuotationItemCategory": "TAN", "Material": "AAA", "to_Partner": [ { "PartnerFunction": "AB" } ] } ] }



**Expected behavior**
I am expecting the navigation properties to be on the request regardless of the respective apis being initialised or not. Also, I do not expect to run into errors on the create request passing it a `salesQuotation` instance which has been retrieved via the library before-hand.

**Used Versions:**

- node version via `node -v`: v18.19.1
- pnpm version via `pnpm -v`: 7.33.7
- SAP Cloud SDK version you used as dependency: 3.15.0

**Impact / Priority**

I do know how to circumvent the issue now, but it took quite some time to understand what is happening. This has large potential for wrong data being transmitted if the navigation properties get omitted.

Affected development phase: Release.

Impact: Inconvenience
deekshas8 commented 2 weeks ago

Hi @lovis-ff ,

Destructuring the service function is how the API is designed to be used. Please refer to the docs here for more details.

lovis-ff commented 1 week ago

ok, I still find this very confusing to use properly but at least I know how to use it now. Thanks!