Forwarding session basics

Real world applications often need to call another service from the initial one that is called. We have created ForwardingSession to make this easier. Forwarding session related tools help you with handling context, errors and following transactions. Below is a sample where we have clientSession that is used to create a request into registerServiceSession which in turn creates a ForwardingSession and calls messageServiceSession.

Creating a sample in stages

Let's go through the flow, starting from the clientSession. We want to register a new user with the handle "AwesomeUserHandle". First we create a pattern interceptor that sends a request to our account service, initialize the dispatcher and make a request.

// Create the client session with pattern interceptor
var clientSession = await Platform.Builder()
    .WithInterceptors(i => i
        .InterceptPattern(UniscaleDemo.Account.Patterns.Pattern, 
            async (input, ctx) =>
            {
                var requestJson = GatewayRequest.From(input, ctx).ToJson();
                // This call should end up in AcceptGatewayRequest of the account
                // session that we create in a later sample.
                var response = await SendToAccountService(requestJson);
                return response;
            }))
    .Build();

var dispatcher = clientSession.AsSolution(
    // This Guid is the id of your solution.
    Guid.Parse("ac43912b-50cf-439a-9652-b378b197d80a"));

// Make the call to create a new user
var createdUser = await dispatcher.Request(
    GetOrRegister.With("AwesomeUserHandle"));
Console.WriteLine(createdUser.Success
    ? $"We created user with id: {createdUser.Value.UserIdentifier}"
    : createdUser.Error.ToLongString());

accountServiceSession receives the client's request. We know that our account service will need to be forwarding the call on top of receiving it, so we will need separate sessions for each. The builder for the session which is forwarding calls to a message service (messageForwardingSessionBuilder) is created separately and can be reused if needed. Then in accountServiceSession we create the user, create a dispatcher based on messageForwardingSessionBuilder and make a request to message service.

// Initialize builder for forwarding and service sessions, keep them linked.
var sessionBuilder = Platform.Builder();

// Prepare for outgoing message calls from the account service
var messageForwardingSessionBuilder = await sessionBuilder
    .ForwardingSessionBuilder(
        // This Guid is the service id of your message service.
        Guid.Parse("60888f37-1665-416e-a70f-09332a24f7bd"))
    .WithInterceptors(i => i
        .InterceptPattern(UniscaleDemo.Messages.Patterns.Pattern, 
            async (input, ctx) =>
            {
                var requestJson = GatewayRequest.From(input, ctx).ToJson();
                // This call should end up in AcceptGatewayRequest of the message
                // session that we create in a later sample.
                var response = await SendToMessageService(requestJson);
                return response;
            }))
    .Build();

var accountServiceSession = await sessionBuilder
    .WithInterceptors(i => i
        .InterceptRequest(
            GetOrRegister.AllFeatureUsages,
            GetOrRegister.Handle(async (input, ctx) =>
                {
                    // For demo, we'll create a sample user as a variable.
                    var created = new UserFull
                    {
                        Handle = input,
                        UserIdentifier = Guid.NewGuid()
                    };

                    // Request message service to celebrate new user.
                    var message = $"we registered {created.Handle}";
                    var dispatcher = await messageForwardingSessionBuilder
                        .ForTransaction(ctx.TransactionId);
                    var sendResult = await dispatcher.Request(
                        WelcomeUser.With(new WelcomeUserInput
                        {
                            // For demo the created user is the message sender.
                            WelcomedUser = new UserTag
                            {
                                By = created.UserIdentifier, 
                                At = DateTime.Now
                            },
                            Message = message
                        }));
                    if (!sendResult.Success)
                        return Result<UserFull>.InternalServerError(
                            "Demo.GeneralError",
                            $"Failed calling feature: {nameof(WelcomeUser)}");

                    // Response
                    return Result<UserFull>.Ok(created);
                }
            )))
    .Build();


// The endpoint of SendToAccountService.
var response = accountServiceSession.AcceptGatewayRequest(requestJson);

And then the only thing left is the message service. We create a messageServiceSession. This service only needs to implement its features and then accept the incoming requests.

// Create a simple message service session that we can call later in a sample
var messageServiceSession = await Platform.Builder()
    .WithInterceptors(i => i
        .InterceptMessage(
            WelcomeUser.AllFeatureUsages,
            WelcomeUser.Handle((input, ctx) =>
            {
                // Send the message into console as a test.
                Console.WriteLine($"Message: {input.Message}; " +
                                  $"By: {input.WelcomedUser.By}");
            })
        ))
    .Build();
    
    
// The endpoint of SendToMessageService.
var response = messageServiceSession.AcceptGatewayRequest(requestJson);

Full working sample

Below is everything put together in a working sample with imports. In this sample the http calls have been simplified to direct references into the created sessions.

using Uniscale.Core;
using Uniscale.Designtime;
using UniscaleDemo.Account_1_0.Functionality.ServiceToModule.
    Account.Registration.ContinueToApplication;
using UniscaleDemo.Account.Account;
using UniscaleDemo.Messages_1_0.Functionality.ServiceToService.
    Messages.NotificationFunctionality.WelcomeMessage;
using UniscaleDemo.Messages.Messages;

// Create a simple message service session that we can call later in a sample
var messageServiceSession = await Platform.Builder()
    .WithInterceptors(i => i
        .InterceptMessage(
            WelcomeUser.AllFeatureUsages,
            WelcomeUser.Handle((input, ctx) =>
            {
                // Send the message into console as a test.
                Console.WriteLine($"Message: {input.Message}; " +
                                  $"By: {input.WelcomedUser.By}");
            })
        ))
    .Build();


// Initialize builder for forwarding and service sessions, keep them linked.
var sessionBuilder = Platform.Builder();

// Prepare for outgoing message calls from the account service
var messageForwardingSessionBuilder = await sessionBuilder
    .ForwardingSessionBuilder(
        // This Guid is the service id of your message service.
        Guid.Parse("60888f37-1665-416e-a70f-09332a24f7bd"))
    .WithInterceptors(i => i
        .InterceptPattern(UniscaleDemo.Messages.Patterns.Pattern,
            async (input, ctx) =>
            {
                var requestJson = GatewayRequest.From(input, ctx).ToJson();
                // This could just as easily be an HTTP call
                var response = await messageServiceSession
                    .AcceptGatewayRequest(requestJson);
                return response;
            }))
    .Build();

// Handle incoming calls for the account service
var accountServiceSession = await sessionBuilder
    .WithInterceptors(i => i
        .InterceptRequest(
            GetOrRegister.AllFeatureUsages,
            GetOrRegister.Handle(async (input, ctx) =>
                {
                    // For demo, we'll create a sample user as variable.
                    var created = new UserFull
                    {
                        Handle = input,
                        UserIdentifier = Guid.NewGuid()
                    };

                    // Request message service to celebrate new user.
                    var message = $"we registered {created.Handle}";
                    var dispatcher = await messageForwardingSessionBuilder
                        .ForTransaction(ctx.TransactionId);
                    var sendResult = await dispatcher.Request(
                        WelcomeUser.With(new WelcomeUserInput
                        {
                            // For demo the created user is the message sender.
                            WelcomedUser = new UserTag {
                                By = created.UserIdentifier, 
                                At = DateTime.Now
                            },
                            Message = message
                        }));
                    if (!sendResult.Success)
                        return Result<UserFull>.InternalServerError(
                            "Demo.GeneralError",
                            $"Failed calling message service: WelcomeUser");

                    // Success response
                    return Result<UserFull>.Ok(created);
                }
            )))
    .Build();


// And finally we can define the client
var clientSession = await Platform.Builder()
    .WithInterceptors(i => i
        .InterceptPattern(UniscaleDemo.Account.Patterns.Pattern,
            async (input, ctx) =>
            {
                var requestJson = GatewayRequest.From(input, ctx).ToJson();
                // This could just as easily be an HTTP call
                var response = await accountServiceSession
                    .AcceptGatewayRequest(requestJson);
                return response;
            }))
    .Build();

var dispatcher = clientSession.AsSolution(
    // This Guid is the id of your solution.
    Guid.Parse("ac43912b-50cf-439a-9652-b378b197d80a"));

// And for sample we make the call to create a new user
var createdUser = await dispatcher.Request(
    GetOrRegister.With("AwesomeUserHandle"));
Console.WriteLine(createdUser.Success
    ? $"We created user with id: {createdUser.Value.UserIdentifier}"
    : createdUser.Error.ToLongString());import uuid