#Payload data binding doesn't work when the return is a union type?

1 messages · Page 1 of 1 (latest)

pastel ferry
#

Here's my service;

import ballerina/http;

type Album readonly & record {|
    string title;
    string artist;
|};

type IdNotFound record {|
    *http:NotFound;
    record {
        string message;
    } body;
|};

table<Album> key(title) albums = table [
    {title: "Blue Train", artist: "John Coltrane"},
    {title: "Jeru", artist: "Gerry Mulligan"}
];

service / on new http:Listener(8022) {

    resource function get album/[string id]() returns Album|IdNotFound {
        if (!albums.hasKey(id)) {
            return {body: {message: "No matching resource found."}};
        }
        return albums.get(id);
    }
}

Here's the main.bal ;

import ballerina/http;
import ballerina/io;

type Album readonly & record {
    string title;
    string artist;
};

type IdNotFound record {|
    *http:NotFound;
    record {
        string message;
    } body;
|};

public function main() returns error? {
    // Creates a new client with the Basic REST service URL.
    http:Client albumClient = check new ("localhost:8022");

    string id = "Blue Train";
    Album|IdNotFound album = check albumClient->/album/[id];
    io:println("GET request:" + album.toJsonString());
}

There's a build error at the Album|IdNotFound album = check albumClient->/album/[id]; line;

incompatible type for parameter 'targetType' with inferred typedesc value: expected 'typedesc<(ballerina/http:2.8.0:Response|anydata)>', found 'typedesc<(wso2healthcare/abc:0.2.3:Album|wso2healthcare/abc:0.2.3:IdNotFound)>'(BCE3931)

How can I get this working?

acoustic bough
#

You can only bind the response payload (which is expected to be a subtype of anydata). The error is because you are also using IdNotFound (which is not anydata) to try and bind to it.

You can do something like

Album|http:ClientError album = albumClient->/album/[id];

if album is Album {
    string title = album.title;
    io:println("Title: ", title);
    return;
}

if album !is http:ClientRequestError {
    return album;
}

// album is http:ClientRequestError here
// 4xx errors
record {
    string message;
} body = check album.detail().body.ensureType();
io:println("Message: ", body.message);

Looping in @smoky forge for suggestions too.

pastel ferry
#

thanks @acoustic bough . Your first line returns a ClientError and then you handle ClientRequestError - are there any other types of ClientErrors too?

smoky forge
#

@pastel ferry Yes, there are other types of ClientErrors (all of them are handled automatically by returning appropriate status code responses)
May I know the reason for binding a 404 Not Found response to a record? Do you want to handle them separately or you just want to add a log when data-binding failed for non-success responses?

pastel ferry
#

From the client code I don't need to do that. I fixed the code based on Maryam's response. But we have a requirement to wrap all errors using a spec-defined error message structure. Is there a better way to achieve it?

smoky forge
#

You can use a ResponseErrorInterceptor to change the error structure like this:

type ErrorDetails record {|
    string timestamp;
    string message;
|};

// This can handle all the interceptable errors
service class ErrorInterceptor {
    *http:ResponseErrorInterceptor;

    // We can access error and the already built response wrt to the error type
    remote function interceptResponseError(error err, http:Response errResponse) returns http:Response {
        // Changing all the errors
        ErrorDetails errDetails = {
            timestamp: time:utcToString(time:utcNow()),
            message: err.message()
        };
        // Only changing the payload from the original error response
        errResponse.setPayload(errDetails, mime:APPLICATION_JSON);
        return errResponse;
    }
}

service http:InterceptableService / on new http:Listener(8022) {
    public function createInterceptors() returns ErrorInterceptor {
        return new ErrorInterceptor();
    }

    ...
}

Currently in order to give the correct status code error response, you have to access it via the already built error response in the interceptor and modify the payload. We are working on an improvement on this by introducing Status Code errors (This may be available in the next release)

pastel ferry
#

Thanks @smoky forge - but setting the correct status code is also important. Could you please share a sample on setting the correct status code?