ballerina-platform / ballerina-library

The Ballerina Library
https://ballerina.io/learn/api-docs/ballerina/
Apache License 2.0
136 stars 58 forks source link

Salesforce databinding always return a specific record even when user gives the type description #6727

Closed niveathika closed 4 weeks ago

niveathika commented 2 months ago

Description:

public function main() returns error?{
   stream<SalesforceOpportunityItem, error?> sfOpportunityItems = check sfClient->query(
        string `SELECT ProductCode, Name, Quantity FROM OpportunityLineItem 
        WHERE OpportunityId='006J1000002Ql9uIAC'`);

    check from var sfOpportunityItem in sfOpportunityItems
        do {
            io:println((typeof sfOpportunityItem).toString());
        };
}
salesforce:SoqlRecordData

The result stream consist of salesforce:SoqlRecordData and not SalesforceOpportunityItem as the user wanted

Shadow-Devil commented 1 month ago

I currently also run into the same issue. Any updates?

niveathika commented 1 month ago

@Shadow-Devil As a workaround, You can use following query expression to change it to required type.

from var item in sfOpportunityItems
        select {...item}

@aashikam Could you give the ETA for this fix?

aashikam commented 1 month ago

Hi all, This is planned for the current sprint.

aashikam commented 1 month ago

I checked this out and it is not very straightforward as it seems :/

Code I tried:

type Account record {
    salesforce:Attribute attributes?;
    string Id;
    string Name;
    string Type;
    string Industry;
};

public function main() returns error?{

salesforce:Client salesforce = check new (config);
   stream<Account, error?> sfOpportunityItems = 
   check salesforce->query(string `SELECT Id, Name, Type, Industry FROM Account`);

    check from Account account in sfOpportunityItems
        do {
            io:println((typeof account).toString());
            io:println(account.toJsonString());
        };
}

The query call gets processed in the following order due to the typedesc value being passed.

(ballerina) query->(native) query->(ballerina) http call->(native) databinding

1. Ballerina

    isolated remote function query(string soql, typedesc<record {}> returnType = <>)
                                    returns stream<returnType, error?>|error = @java:Method {
        'class: "io.ballerinax.salesforce.ReadOperationExecutor",
        name: "getQueryResult"
    } external;

2. Java

    public static Object getQueryResult(Environment env, BObject client, BString receivedQuery, BTypedesc targetType) {
        Object[] paramFeed = {targetType, true, receivedQuery, true};
        return invokeClientMethod(env, client, "processGetQueryResult", paramFeed);
    }

3. Ballerina

    private isolated function processGetQueryResult(typedesc<record {}> returnType, string receivedQuery)
                                                    returns stream<record {}, error?>|error {
        string path = utils:prepareQueryUrl([API_BASE_PATH, QUERY], [Q], [receivedQuery]);
        SOQLQueryResultStream objectInstance = check new (self.salesforceClient, path);
        stream<record {}, error?> finalStream = new (objectInstance);
        return self.streamConverter(finalStream, returnType);
    }

Note: here the SOQLQueryResultStream internally processes and creates a stream of type SoqlQueryResult.

https://github.com/ballerina-platform/module-ballerinax-salesforce/blob/master/ballerina/stream_implementer.bal

4. Java

    public static BStream streamConverter(Environment env, BObject client, BStream data, BTypedesc returnType) {

        RecordType recordType = (RecordType) returnType.getDescribingType();
        StreamType bStream = TypeCreator.createStreamType(recordType, PredefinedTypes.TYPE_NULL);
        return ValueCreator.createStreamValue(bStream, data.getIteratorObj());
    }

Even though the java final stream is stream<listry:Account>, io:println((typeof account).toString()); prints typedesc salesforce:SoqlRecordData

Screenshot 2024-08-08 at 21 32 16 Screenshot 2024-08-08 at 21 19 38
github-actions[bot] commented 4 weeks ago

This issue is NOT closed with a proper Reason/ label. Make sure to add proper reason label before closing. Please add or leave a comment with the proper reason label now.

      - Reason/EngineeringMistake - The issue occurred due to a mistake made in the past.
      - Reason/Regression - The issue has introduced a regression.
      - Reason/MultipleComponentInteraction - Issue occured due to interactions in multiple components.
      - Reason/Complex - Issue occurred due to complex scenario.
      - Reason/Invalid - Issue is invalid.
      - Reason/Other - None of the above cases.