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

Calling function import from SAP Cloud SDK returns an empty object #682

Closed
1 task
miyasuta opened this issue Nov 6, 2020 · 21 comments
Closed
1 task

Calling function import from SAP Cloud SDK returns an empty object #682

miyasuta opened this issue Nov 6, 2020 · 21 comments
Assignees
Labels
feature request Requests for new functionality prio: medium

Comments

@miyasuta
Copy link

miyasuta commented Nov 6, 2020

Describe the bug
Calling a function import from SAP Cloud SDK returns an empty object, even though the call is successful.
You can reproduce the issue by using the following git repository.
https://github.com/miyasuta/sdk-function-import-test

To Reproduce
Steps to reproduce the behavior:

  1. Develop a custom odata service which includes a function import.
  2. In a Cloud SDK app, generate an odata client using below command as explained here.
    sap-cloud-sdk generate-odata-client -i service-specification/ -o odata-client/
  3. Call the function import from the Cloud SDK app.
  4. When the function import gets called, it falls into then() block, meaning that the call was successful.
    However, the response object is empty.

Expected behavior
Response object should be filled with returned data.

Screenshots
Calling the OData function import directly
image

Calling function import from Cloud SDK app (delivery data should be returned between {})
image

Used Versions:

  • node version via node -v: 12.16.1
  • npm version via npm -v: 6.14.4
  • SAP Cloud SDK version you used as dependency: 1.31.0

Code Examples
My controller code is as below.

import { Controller, Get } from '@nestjs/common';
import { functionImports } from 'odata-client/catalog-service';


@Controller('get-delivery')
export class GetDeliveryController {
    @Get()
    getDeliveryInfo() {
        return new Promise((resolve, reject) => {
            functionImports.getDeliveryDate({})
            .execute({
                url: 'http://localhost:4004'                
            })
            .then(res => {
                resolve('Function call successful: ' + JSON.stringify(res))
            })
            .catch(error => {
                resolve('Error occurred: ' + JSON.stringify(error))
            })
        })
    }
}

Log file
No error has occurred.

Additional context
None

Please check the following points

  • [X ] Have you provided detailed description about your issue?
  • [X ] Have you shared all the necessary information to help the cloud-sdk developer reproduce the issue?
  • [ X] Have you provided the expected behaviour?
  • [ X] Have you attached some useful screenshots?
  • [ X] Have you provided the node/npm/cloud-sdk versions?
  • [ X] Have you shared your code snippets as a minial example?
  • Have you provided you log file or related error messages?
  • [ X] Have you searched for related issues on Stackoverflow or answers.sap.com?
@miyasuta miyasuta added the bug Something isn't working label Nov 6, 2020
@jjtang1985
Copy link
Contributor

Hi @miyasuta , thank you for creating an issue in this repo.

We are working on a similar bug about the functional import.

We will let you know for the updates.

@jjtang1985
Copy link
Contributor

Hi @florian-richter , here is another example for the bug of the function import, where cap is used.

@marikaner
Copy link
Contributor

@miyasuta, please note that this repository has been moved and the link to this issue has changed.

@jjtang1985
Copy link
Contributor

Hi @miyasuta, we fixed this bug and made a new release last week.
Could you please try the latest version 1.32.1?
We are looking forward to your feedback.

@miyasuta
Copy link
Author

Hi @jjtang1985,

Thanks for the update.
I have updated the sdk version in package.json dependency. Is this what needs to be done?

  "dependencies": {
    "@sap-cloud-sdk/core": "^1.32.1",

I have tested in the new version, but unfortunaltelly, the result didn't change.
I have updated my GitHub repo.
https://github.com/miyasuta/sdk-function-import-test

@jjtang1985
Copy link
Contributor

Hi @miyasuta , I checked out your repository and tested.

I found out the root cause of your issue, which is the edmx file you used for the generator.

Your data provider is the CAP service that is using OData V4, while the edmx does not match.

I fixed the edmx file and re-generated the OData client again, the endpoint gave me the data:

Function call successful: {"deliveryDate":"2020-11-06T00:00:00.000Z"}

Please follow the steps below so you can have your application running:

  • call this URL to get the metadata
  • use it for generating your OData client again
  • make sure you are using the latest version of the generator.
  • make sure you use the new client instead of the old instance
  • try your endpoint

Please let me know, if this helps.

@miyasuta
Copy link
Author

Hi @jjtang1985

Thanks for your advice. My project is now working for OData v4 as well as v2.

I have another project which has the same issue. It is based on Gateway OData service (v2).
I have updated sdk version to 1.32.1 and regenerated odata client, but the issue persists.
I confirmed that the Gateway was returning correct data.
I noticed that the data format returned from the Geteway was different from the data returned from CAP.

Below information is for your reference.

From Geteway

{
   "d":{
      "getDeliveryDate":{
         "__metadata":{
            "type":"ZCAI_STO_SRV.CT_DeliveryDate"
         },
         "DeliveryDate":"/Date(1606348800000)/"
      }
   }
}

From CAP (v2)

{
   "d":{
      "deliveryDate":"/Date(1606348800000)/"
   }
}

edmx file

<edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:sap="http://www.sap.com/Protocols/SAPData" Version="1.0">
    <edmx:Reference xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Uri="https://webidetesting6354665-gda3ba2c0.dispatcher.jp1.hana.ondemand.com/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_COMMON',Version='0001',SAP__Origin='LOCAL')/$value">
        <edmx:Include Namespace="com.sap.vocabularies.Common.v1" Alias="Common" />
    </edmx:Reference>
    <edmx:Reference xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Uri="https://webidetesting6354665-gda3ba2c0.dispatcher.jp1.hana.ondemand.com/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_CAPABILITIES',Version='0001',SAP__Origin='LOCAL')/$value">
        <edmx:Include Namespace="Org.OData.Capabilities.V1" Alias="Capabilities" />
    </edmx:Reference>
    <edmx:Reference xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Uri="https://webidetesting6354665-gda3ba2c0.dispatcher.jp1.hana.ondemand.com/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_COMMUNICATION',Version='0001',SAP__Origin='LOCAL')/$value">
        <edmx:Include Namespace="com.sap.vocabularies.Communication.v1" Alias="Communication" />
    </edmx:Reference>
    <edmx:Reference xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Uri="https://webidetesting6354665-gda3ba2c0.dispatcher.jp1.hana.ondemand.com/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_AGGREGATION',Version='0001',SAP__Origin='LOCAL')/$value">
        <edmx:Include Namespace="Org.OData.Aggregation.V1" Alias="Aggregation" />
    </edmx:Reference>
    <edmx:Reference xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Uri="https://webidetesting6354665-gda3ba2c0.dispatcher.jp1.hana.ondemand.com/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_PERSONALDATA',Version='0001',SAP__Origin='LOCAL')/$value">
        <edmx:Include Namespace="com.sap.vocabularies.PersonalData.v1" Alias="PersonalData" />
    </edmx:Reference>
    <edmx:DataServices m:DataServiceVersion="2.0">
        <Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm" Namespace="ZCAI_STO_SRV" xml:lang="en" sap:schema-version="1">
            <EntityType Name="StockSransfer" sap:content-version="1">
                <Key>
                    <PropertyRef Name="PoNumber" />
                </Key>
                <Property Name="PoNumber" Type="Edm.String" Nullable="false" MaxLength="10" sap:unicode="false" sap:label="Purchasing Doc." sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="CompCode" Type="Edm.String" Nullable="false" MaxLength="4" sap:unicode="false" sap:label="Company Code" sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="PurchOrg" Type="Edm.String" Nullable="false" MaxLength="4" sap:unicode="false" sap:label="Purchasing Org." sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="PurGroup" Type="Edm.String" Nullable="false" MaxLength="3" sap:unicode="false" sap:label="Purch. Group" sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="SupplPlnt" Type="Edm.String" Nullable="false" MaxLength="4" sap:unicode="false" sap:label="Supplying Plant" sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="Material" Type="Edm.String" Nullable="false" MaxLength="18" sap:unicode="false" sap:label="Material" sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="Plant" Type="Edm.String" Nullable="false" MaxLength="4" sap:unicode="false" sap:label="Plant" sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="StgeLoc" Type="Edm.String" Nullable="false" MaxLength="4" sap:unicode="false" sap:label="Stor. Loc." sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="DeliveryDate" Type="Edm.String" Nullable="false" MaxLength="10" sap:unicode="false" sap:label="Delivery Date" sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="Quantity" Type="Edm.Decimal" Nullable="false" Precision="13" Scale="3" sap:unicode="false" sap:unit="PoUnit" sap:label="Scheduled Qty" sap:updatable="false" sap:sortable="false" sap:filterable="false" />
                <Property Name="PoUnit" Type="Edm.String" MaxLength="3" sap:unicode="false" sap:label="Order Unit" sap:updatable="false" sap:sortable="false" sap:filterable="false" sap:semantics="unit-of-measure" />
            </EntityType>
            <EntityType Name="ZC_MARDType" sap:label="Material Stock" sap:content-version="1">
                <Key>
                    <PropertyRef Name="Material" />
                    <PropertyRef Name="Plant" />
                    <PropertyRef Name="StorageLocation" />
                </Key>
                <Property Name="Material" Type="Edm.String" Nullable="false" MaxLength="40" sap:display-format="UpperCase" sap:label="Material" sap:quickinfo="Material Number" />
                <Property Name="Plant" Type="Edm.String" Nullable="false" MaxLength="4" sap:display-format="UpperCase" sap:label="Plant" />
                <Property Name="StorageLocation" Type="Edm.String" Nullable="false" MaxLength="4" sap:display-format="UpperCase" sap:label="Storage location" />
                <Property Name="CurrentStock" Type="Edm.Decimal" Precision="13" Scale="3" sap:unit="MaterialBaseUnit" />
                <Property Name="MaterialBaseUnit" Type="Edm.String" MaxLength="3" sap:label="Base Unit of Measure" sap:semantics="unit-of-measure" />
            </EntityType>
            <ComplexType Name="CT_DeliveryDate">
                <Property Name="DeliveryDate" Type="Edm.DateTime" Nullable="false" Precision="0" sap:label="Delivery Date" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false" />
            </ComplexType>
            <EntityContainer Name="ZCAI_STO_SRV_Entities" m:IsDefaultEntityContainer="true" sap:message-scope-supported="true" sap:supported-formats="atom json xlsx">
                <EntitySet Name="StockSransferSet" EntityType="ZCAI_STO_SRV.StockSransfer" sap:updatable="false" sap:deletable="false" sap:pageable="false" sap:content-version="1" />
                <EntitySet Name="ZC_MARD" EntityType="ZCAI_STO_SRV.ZC_MARDType" sap:creatable="false" sap:updatable="false" sap:deletable="false" sap:content-version="1" />
                <FunctionImport Name="getDeliveryDate" ReturnType="ZCAI_STO_SRV.CT_DeliveryDate" m:HttpMethod="GET">
                    <Parameter Name="Material" Type="Edm.String" Mode="In" />
                    <Parameter Name="SupplPlant" Type="Edm.String" Mode="In" />
                    <Parameter Name="DestPlant" Type="Edm.String" Mode="In" />
                </FunctionImport>
            </EntityContainer>
            <Annotations xmlns="http://docs.oasis-open.org/odata/ns/edm" Target="ZCAI_STO_SRV.ZCAI_STO_SRV_Entities">
                <Annotation Term="Aggregation.ApplySupported">
                    <Record>
                        <PropertyValue Property="Transformations">
                            <Collection>
                                <String>aggregate</String>
                                <String>groupby</String>
                                <String>filter</String>
                            </Collection>
                        </PropertyValue>
                        <PropertyValue Property="Rollup" EnumMember="None" />
                    </Record>
                </Annotation>
            </Annotations>
            <atom:link xmlns:atom="http://www.w3.org/2005/Atom" rel="self" href="https://webidetesting6354665-gda3ba2c0.dispatcher.jp1.hana.ondemand.com/sap/opu/odata/sap/ZCAI_STO_SRV/$metadata" />
            <atom:link xmlns:atom="http://www.w3.org/2005/Atom" rel="latest-version" href="https://webidetesting6354665-gda3ba2c0.dispatcher.jp1.hana.ondemand.com/sap/opu/odata/sap/ZCAI_STO_SRV/$metadata" />
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

function-import.ts

/*
 * Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved.
 *
 * This is a generated file powered by the SAP Cloud SDK for JavaScript.
 */
import { transformReturnValueForComplexTypeV2, deserializeComplexTypeV2, FunctionImportRequestBuilderV2, FunctionImportParameter } from '@sap-cloud-sdk/core';
import { CtDeliveryDate } from './CtDeliveryDate';

/**
 * Type of the parameters to be passed to [[getDeliveryDate]].
 */
export interface GetDeliveryDateParameters {
  /**
   * Material.
   */
  material: string;
  /**
   * Suppl Plant.
   */
  supplPlant: string;
  /**
   * Dest Plant.
   */
  destPlant: string;
}

/**
 * Get Delivery Date.
 *
 * @param parameters - Object containing all parameters for the function import.
 * @returns A request builder that allows to overwrite some of the values and execute the resultng request.
 */
export function getDeliveryDate(parameters: GetDeliveryDateParameters): FunctionImportRequestBuilderV2<GetDeliveryDateParameters, CtDeliveryDate> {
  const params = {
    material: new FunctionImportParameter('Material', 'Edm.String', parameters.material),
    supplPlant: new FunctionImportParameter('SupplPlant', 'Edm.String', parameters.supplPlant),
    destPlant: new FunctionImportParameter('DestPlant', 'Edm.String', parameters.destPlant)
  }

  return new FunctionImportRequestBuilderV2('get', '/sap/opu/odata/sap/ZCAI_STO_SRV', 'getDeliveryDate', (data) => transformReturnValueForComplexTypeV2(data, (data) => deserializeComplexTypeV2(data, CtDeliveryDate)), params);
}

export const functionImports = {
  getDeliveryDate
};

controller

import { Controller, Req, Post } from '@nestjs/common';
import { Request } from 'express';
import { functionImports } from 'odata-client/zcai-sto-service';

@Controller('get-delivery-date')
export class GetDeliveryDateController {
    @Post()
    getDeliveryDate(@Req() request: Request) {
        const memory = request.body.conversation.memory
        const parameter = {
            material: memory.item.material,
            supplPlant: '1520',
            destPlant: memory.office.plant
        }

        return new Promise((resolve, reject)=> {
            functionImports.getDeliveryDate(parameter)
            .execute({
                destinationName: 'CC_S4_1909_FPS01_500'
            })
            .then(res => { //res object is empty!
                console.log('received response object: ' + JSON.stringify(res))
                console.log('received deliveryDate: ' + res.deliveryDate)
                ...                                
            })
            .catch(error => {
              ...
            })
        })
}

@jjtang1985
Copy link
Contributor

Hi @miyasuta, I'm happy to hear that your previous application is running now.

The SDK expects that the response structure does not contain the function name just like the CAP v2 example.
Could you please share what is the Gateway service? I could not find it in the api hub.

@marikaner marikaner removed the pinned label Nov 26, 2020
@miyasuta
Copy link
Author

Hi @jjtang1985,

The Geteway service which I'm using is a custom OData service created at S/4HANA on-premise.
I followed the procedure in below link to create the function import.
https://help.sap.com/viewer/68bf513362174d54b58cddec28794093/7.5.9/en-US/c5dc22512c312314e10000000a44176d.html

@FrankEssenberger
Copy link
Contributor

FrankEssenberger commented Nov 26, 2020

@miyasuta I have made a little test on our dev ERP system and I can reproduce the issue and receive a payload like:

{
    "d": {
        "Test123": {
            "__metadata": {
                "type": "SRS_ML_SRV.TestType"
            },
            "propB": "1.234E3",
            "propA": "Hello World"
        }
    }
}

for a function import named Test123 returning a complex type. So I have to investigate if there is some button in the SEGW to define the return type or reach out to experts on this topic why the gateway builds the data like this.

Thanks for bringing this to our attention.

@FrankEssenberger
Copy link
Contributor

@FrankEssenberger
Copy link
Contributor

@miyasuta I also tried Entity as a return type and for this there is no extra key included. So if it is urgent you could create a dummy entity which has the same properties as the complex type you used. This should work. Or you wait until we implemented some fallback logic in the response parsing so that we can handle both cases with and without the additional key in the response.

@jjtang1985
Copy link
Contributor

@miyasuta , as Frank mentioned, only the complex type as return type is affected. I created a ticket in the back log for discussing this issue.

Before that, I also would like to ask some questions.

  • Is this blocking your live system or just a PoC project?
  • Could you please try to use Entity as return type as Frank mentioned and let us know the result?
  • What exactly is the function import you would like to introduce? If you you can share more details, maybe we can also find a way for building an OData query with e.g. filter/batch as workaround.

I will update here after our discussion.

@miyasuta
Copy link
Author

Hi all,

Thank you for your investigation. I will try using entity as return type, and update here.
This is not a program for production, but just a PoC.
The purpose of the function import to get delivery date of stock transfer order, based on material, shipping plant and destination plant.
As the delivery date is result of some calculation, I thought function import would better suit than get entity (read operation).

@miyasuta
Copy link
Author

Hi all,
After changing the return type to entity, the issue has been resolved.
Thank you very much for your help.

@FrankEssenberger
Copy link
Contributor

good to hear. Of course it should also work with the complex type. I will keep this issue open and add either the PR from us that we handle the complex type case or how to adjust the S/4 system so that it does not add this extra key.

@FrankEssenberger
Copy link
Contributor

Since there is a work around we will close this one and see if we see more customer demand for a proper solution.

@gregorwolf
Copy link

Dear @FrankEssenberger ,

I've posted today the question Structure returned by V2 adapter proxy does not match V2 result of S/4HANA backend. The service used there is the standard S/4HANA (Cloud) API: API_CV_ATTACHMENT_SRV. It has the function import GetAttachmentCount which is returning:

{
  "d": {
    "GetAttachmentCount": {
      "AttachmentCount": 0
    }
  }
}

As I've tested against our S/4HANA system and as it is documented on api.sap.com. So yes, I would like to have a proper solution for this issue if it still exists for the SAP Cloud SDK for JS.

If needed I can also file an SAP incident.

Best regards
Gregor

@jjtang1985 jjtang1985 reopened this May 26, 2021
@jjtang1985
Copy link
Contributor

Hi @gregorwolf ,

thanks for raising this up.

Although the execute function does not work with the GetAttachmentCount as part of the response, you still can try executeRaw.
After that, you can deserialise the response like the example below:

deserializeComplexType(yourResponse.data.d.GetAttachmentCount, YourComplexType)

Please share, whether this can solve your issue.

I created a backlog item for the API design, as this seems to be a common issue.

Best reagards,
Junjie

@marikaner marikaner added feature request Requests for new functionality and removed bug Something isn't working labels Nov 4, 2021
@zfrk
Copy link

zfrk commented Feb 3, 2022

Hi @jjtang1985 ,

I had the same kind of issue as @gregorwolf on prem system.

deserializeComplexType(yourResponse.data.d.GetAttachmentCount, YourComplexType)

This is solved my problem, I got the result as I wanted.

image

Do you consider applying this to the "execute Function" ?

Regards,

Zafer

@ZhongpinWang
Copy link
Contributor

Hi @miyasuta, @gregorwolf, @zfrk,

We implemented the feature in @sap-cloud-sdk/odata-common: 2.3.0 to support the modification of the response
data before deserialization.

Take the following response as an example:

{
  "d": {
    "GetAttachmentCount": {
      "AttachmentCount": 0
    }
  }
}

Now, you can pass dataAccessor function as a parameter to execute() to modify the response data before the deserialization:

functionImportsRequestBuilder.execute(
  destination,
  data => data.d.GetAttachmentCount
);

I hope it can help you solve the problem. Please re-open the issue if you have any further questions.

Best regards,
Zhongpin Wang

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Requests for new functionality prio: medium
Projects
None yet
Development

No branches or pull requests

8 participants