ballerina-guides / enterprise-integration-patterns

Enterprise Integration Pattern samples in Ballerina
Apache License 2.0
33 stars 8 forks source link

Add a sample for pipes and filters using interceptors #118

Open LakshanWeerasinghe opened 11 months ago

LakshanWeerasinghe commented 11 months ago

Description: $subject

LakshanWeerasinghe commented 11 months ago

Here is a sample code that can be used.

import ballerina/cache;
import ballerina/http;
import ballerina/uuid;

type NewOrder record {|
    *OrderRequest;
    string id;
|};

type OrderRequest record {|
    Item[] items;
    string deliveryAddress;
|};

type Item record {|
    string code;
    int quantity;
|};

service class AuthInterceptor {
   *http:RequestInterceptor;

    resource function 'default [string... path](http:RequestContext ctx, http:Request req) returns http:NextService|http:HeaderNotFoundError|error? {
        string csrfToken = check req.getHeader("X-CSRF-Token");
        if csrfTokenStore.hasKey(csrfToken) {
            return ctx.next();
        }
        return error http:ClientAuthError("Invalid csrf token");
    }   
}

service class RequestValidationInterceptor {
   *http:RequestInterceptor;

    resource function post 'order(http:RequestContext ctx, http:Request req) returns http:NextService|error? {
        json orderRequestJson = check req.getJsonPayload();
        OrderRequest orderRequest = check orderRequestJson.fromJsonWithType();
        if orderValidate(orderRequest) {
            return ctx.next();
        }
        return error ("Invalid request payload");
    }
}

final cache:Cache csrfTokenStore = new (capacity = 100, defaultMaxAge = 14400);
final http:Client firebaseClient = check new ("http://api.orders.firebase.com.balmock.io");

service http:InterceptableService /api/v1 on new http:Listener(8080) {

    public function createInterceptors() returns http:RequestInterceptor[] {
        return [new AuthInterceptor(), new RequestValidationInterceptor()];
    }

    resource function post 'order(OrderRequest orderRequest) returns NewOrder|error {
        string id = uuid:createType1AsString();
        NewOrder newOrder = {
            id,
            ...orderRequest
        };
        _ = check firebaseClient->/orders\.json.post(newOrder, targetType = http:Response);
        return newOrder;
    }
}

function orderValidate(OrderRequest orderRequest) returns boolean {
    foreach Item item in orderRequest.items {
        if item.quantity <= 0 {
            return false;
        }
    }
    return true;
}
LakshanWeerasinghe commented 11 months ago

Also we need to have a md5 signing based csrf token or Referer-based validation.

manuranga commented 1 month ago

We currently have one per pattern. This is to be done if and when we support multiple samples per pattern.