Is it possible to execute an action after a response is returned in a Ballerina service?

18 Views Asked by At

In a ballerina HTTP method, is it possible to call an external API and return a response to the client and then handle the response from the external API? Here, the usecase is such that after the method returns it's response, if the external API successfully return a response, that response value needs to be added to a cache. Then when the HTTP method is called again, that response needs to be extracted from the Cache.

cache:Cache cache = new ({
    capacity: 1000,
    evictionFactor: 0.2,
    cleanupInterval: 3600
});

resource function post getEntitlementsWithoutOrgId(http:Caller caller, http:Request request) returns error? {
    string jsonString = check request.getTextPayload();
    json jsonObj = check value:fromJsonString(jsonString);
    string role = <string>check jsonObj.role;

    json returnPayload = <json>check cache.get(role);
    http:Response response = new;
    if (returnPayload != null) {
        response.statusCode = http:STATUS_OK;
    } else {
        http:Client httpEndpoint = check new ("https://test.com", {
            timeout: 0.9
        });
        http:Response getResponse = check httpEndpoint->get("/roles/" + role + "/entitlements", {"X_API_KEY": "X_API_KEY"});
        returnPayload = check getResponse.getJsonPayload();

        if (returnPayload != null) {
            check cache.put(role, returnPayload);
            response.statusCode = http:STATUS_OK;
        } else {
            response.statusCode = getResponse.statusCode;
        }
    }
    response.setJsonPayload(returnPayload);
    check caller->respond(response);
}
1

There are 1 best solutions below

0
On

When you do check cache.get(role), if the relevant entry is not present in the cache you are returning immediately. Following is a sample related to the above requirement.

import ballerina/http;
import ballerina/cache;

final cache:Cache cache = new ({
    capacity: 1000,
    evictionFactor: 0.2,
    cleanupInterval: 3600
});

final http:Client httpClient = check new ("https://test.com", {
    timeout: 0.9
});

service on new http:Listener(8080) {
    resource function post getEntitlementsWithoutOrgId(@http:Payload json jsonObj, http:Caller caller) returns error? {
        string role = check jsonObj.role;

        if cache.hasKey(role) {
            http:Response response = new;
            response.statusCode = http:STATUS_OK;

            // // Simplifying
            // any cacheEntry = check cache.get(role);
            // json respPayload = check cacheEntry.ensureType();
            // response.setJsonPayload(respPayload);
            response.setJsonPayload(check cache.get(role).ensureType());
            return caller->respond(response);
        }

        // We get here if there was no cache entry.
        // Then do the remote call asynchronously.
        future<json|error> respFuture = start httpClient->get("/roles/" + role + "/entitlements", {"X_API_KEY": "X_API_KEY"}, targetType = json);

        // Respond using `caller->respond()` here with appropriate payload.
        check caller->respond(<PAYLOAD>);

        // Wait for the response from the remote method call and add it to the cache.
        json|error respPayload = wait respFuture;

        if respPayload is json {
            cache:Error? putRes = cache.put(role, respPayload);
        } else {
            // Handle the error from the remote method call
        }
    }
}