Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialization of generated oData API's does not properly add navigation properties #3538

Open
carlonnheim opened this issue Feb 18, 2023 · 4 comments
Labels
bug Something isn't working

Comments

@carlonnheim
Copy link

Describe the bug
Initialization of generated oData API's does not properly add navigation properties

To Reproduce
Generate an API and import only one of its api's like this (the generated service has a SalesOrder entity with SalesOrderItem's)

const { myservice } = require('./myservice');
const { salesOrderApi } = myservice();

This calls the generated getter, which looks like this

    get salesOrderApi(): SalesOrderApi<DeSerializersT> { 
        const api = this.initApi('salesOrderApi', SalesOrderApi);
          const linkedApis = [
            this.initApi('salesOrderItemApi', SalesOrderItemApi)
          ];
          api._addNavigationProperties(linkedApis);
          return api    
      }

The api of the navigation property is loaded directly with this.initApi('salesOrderItemApi', SalesOrderItemApi) and not through its getter. As a consequence, the navigation properties of the salesOrderItemApi are not loaded. I think it should be

    get salesOrderApi(): SalesOrderApi<DeSerializersT> { 
        const api = this.initApi('salesOrderApi', SalesOrderApi);
          const linkedApis = [
            this.SalesOrderItemApi
          ];
          api._addNavigationProperties(linkedApis);
          return api    
      }

Expected behavior
The child API's are propertly loaded

Screenshots
N/A

Used Versions:

  • SAP Cloud SDK version you used as dependency: 2.14

Code Examples
See above

Log file
See above

Impact / Priority

Affected development phase: Development

Impact: Inconvenience (we have to explicitly load API's also in scenarios where we otherwise rely on introspection)

Timeline: e.g. Go-Live is in 12 weeks.

Additional context
N/A

@carlonnheim carlonnheim added the bug Something isn't working label Feb 18, 2023
@marikaner
Copy link
Contributor

Hey @carlonnheim, we are aware of this inconvenience, but I would like to understand your issue a bit better first.

Could you share the code that you would like to write that does not work and the code that you have to write to make it work?

@carlonnheim
Copy link
Author

Hi @marikaner , thanks for the quick reply!

Of course. I have a few scenarios where I need to work with the cloud-sdk in a CAP-based project. In particular, CAP does not support batch requests, so in some of the handlers we use the oData api of the cloud-sdk to perform operations with auxiliary systems.

We end up repeating similar code patterns in this scenarios, which we try to eliminate by introspecting the data structures and forming the applicable calls in reuse routines. The below one takes a CAP CDS data type as input and pulls data into it from a generated oData client by dynamically determining the parameters which need to go into the .select() clause.

The affected part is the recursive return oField.select(...getSelectFromCdsType(oField._linkedEntityApi, oNestedType)); which descends into the nested entities of the given type.

The code works fine if we make sure the utilized api's are explicitly loaded first.

/** Gets a select array based on a cds type
 * 
 * @param {Object} api the cloud sdk api
 * @param {Object} type cds type
 * @returns selects which resolves the type
 */
function getSelectFromCdsType(api, type) {
    return _.map(type?.elements || [], (e, name) => {
        let oField = api.schema[util.upperCaseSnakeCase(e.name)];
        if (!oField) {
            LOG.debug(`${api.constructor.name} does not have element ${e.name}`);
            return;
        }

        if (e.items || e._isStructured) {
            // Expand into structs and associations
            let sType = e.items?.type || e.type;
            let oNestedType = cds.model.definitions[sType];
            if (!oNestedType) {
                LOG.warn(`${sType} is not known`);
                return;
            }
            return oField.select(...getSelectFromCdsType(oField._linkedEntityApi, oNestedType));
        } else {
            // Regular select
            return oField;
        }
    }).filter(x => x); // Discard any unknown fields
}

Thanks!
//Carl

@marikaner
Copy link
Contributor

Ok, I got it for now. We will look into this, but I am afraid that this might not be possible without bigger (maybe even major, aka version 4) changes. However, we will talk about solutions (for this and some similar inconveniences of this sort) after the currently ongoing release of version 3.
I hope until then, you can leverage your workaround.

In addition, I am interested in understanding your CAP + SDK batch issue a bit better - maybe there is even more we can do. If you are interested to share this and/or other feedback directly feel free to reach out to us through [email protected] to schedule a session.

@carlonnheim
Copy link
Author

Thanks @marikaner - yes, the workaround is perfectly fine for us, this is only an inconvenience, not a blocker.

I will be happy to share our experiences with CAP and cloud-sdk. Most of them are on the CAP side, Is [email protected] the right forum to discuss CAP aspects as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants