Skip to content

Latest commit

 

History

History
290 lines (233 loc) · 10.1 KB

programmatic.md

File metadata and controls

290 lines (233 loc) · 10.1 KB

Programmatic

Before you start

For this guide, we will need the following:

Please return here when you are ready.

Workflow Overview

We will define a new workflow called GetUser. The purpose of the workflow is to handle inbound HTTP requests by fetching a user by a given user ID from a backend API and writing them back to the client in JSON format.

For the backend API, we will use reqres.in, which returns fake data using real HTTP responses.

Our workflow will parse the inbound HTTP request by getting the desired user ID from a route parameter and use that value to make an API call to reqres.

The following is an example of such an HTTP request that you can try right now from your browser: https://reqres.in/api/users/2

The response should look similar to this:

{
    "data": {
        "id": 2,
        "email": "[email protected]",
        "first_name": "Janet",
        "last_name": "Weaver",
        "avatar": "https://reqres.in/img/faces/2-image.jpg"
    },
    "support": {
        "url": "https://reqres.in/#support-heading",
        "text": "To keep ReqRes free, contributions towards server costs are appreciated!"
    }
}

Our workflow will essentially be a proxy sitting in front of the reqres API and return a portion of the response.

Create C# Workflow

Follow these steps to create the workflow from code.

{% stepper %} {% step %} Create Workflow

Create GetUser.cs and add the following code:

{% code title="GetUser.cs" %}

using System.Dynamic;
using System.Net;
using Elsa.Http;
using Elsa.Http.Models;
using Elsa.Workflows;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Contracts;

namespace WorkflowApp.Web.Workflows;

public class GetUser : WorkflowBase
{
    protected override void Build(IWorkflowBuilder builder)
    {
        var routeDataVariable = builder.WithVariable<IDictionary<string, object>>();
        var userIdVariable = builder.WithVariable<string>();
        var userVariable = builder.WithVariable<ExpandoObject>();

        builder.Root = new Sequence
        {
            Activities =
            {
                new HttpEndpoint
                {
                    Path = new("users/{userid}"),
                    SupportedMethods = new(new[] { HttpMethods.Get }),
                    CanStartWorkflow = true,
                    RouteData = new(routeDataVariable)
                },
                new SetVariable
                {
                    Variable = userIdVariable,
                    Value = new(context =>
                    {
                        var routeData = routeDataVariable.Get(context)!;
                        var userId = routeData["userid"].ToString();
                        return userId;
                    })
                },
                new SendHttpRequest
                {
                    Url = new(context =>
                    {
                        var userId = userIdVariable.Get(context);
                        return new Uri($"https://reqres.in/api/users/{userId}");
                    }),
                    Method = new(HttpMethods.Get),
                    ParsedContent = new(userVariable),
                    ExpectedStatusCodes =
                    {
                        new HttpStatusCodeCase
                        {
                            StatusCode = StatusCodes.Status200OK,
                            Activity = new WriteHttpResponse
                            {
                                Content = new(context =>
                                {
                                    var user = (dynamic)userVariable.Get(context)!;
                                    return user.data;
                                }),
                                StatusCode = new(HttpStatusCode.OK)
                            }
                        },
                        new HttpStatusCodeCase
                        {
                            StatusCode = StatusCodes.Status404NotFound,
                            Activity = new WriteHttpResponse
                            {
                                Content = new("User not found"),
                                StatusCode = new(HttpStatusCode.NotFound)
                            }
                        }
                    }
                }
            }
        };
    }
}

{% endcode %} {% endstep %} {% endstepper %}

Let's go over this workflow section by section.

Workflow Variables

var routeDataVariable = builder.WithVariable<IDictionary<string, object>>();
var userIdVariable = builder.WithVariable<string>();
var userVariable = builder.WithVariable<ExpandoObject>();

Here, we defined 3 workflow variables.

The routeDataVariable variable is used to capture route data output from the HTTP endpoint activity. This variable is a dictionary.

The userIdVariable variable is used to store the user ID value that we get from the routeDataVariable dictionary.

The userVariable variable is used to capture the parsed response from the reqres API call. Since reqres returns JSON content and the capturing variable is of type ExpandoObject, the SendHttpRequest activity will parse the received JSON response into an ExpandoObject.

HttpEndpoint Activity

new HttpEndpoint
{
    Path = new("users/{userid}"),
    SupportedMethods = new(new[] { HttpMethods.Get }),
    CanStartWorkflow = true,
    RouteData = new(routeDataVariable)
},

Here we see the HttpEndpoint activity being defined and configured to be a trigger by setting CanStartWorkflow to true.

We set its Path property to respond to users/{userid}. Notice that we are using a route parameter using the name userid. This is the key we will use to grab the provided user ID from the inbound URL path.

To capture the route data, we assign the routeDataVariable variable to the RouteData output of the activity.

SetVariable Activity

new SetVariable
{
    Variable = userIdVariable,
    Value = new(context =>
    {
        var routeData = routeDataVariable.Get(context)!;
        var userId = routeData["userid"].ToString();
        return userId;
    })
},

Here we see the SetVariable activity defined and configured to set the userIdVariable variable to the dictionary entry with key "userid".

We set its Variable property to reference the userIdVariable variable and its Value property to a callback that returns the received user ID from the route data dictionary.

SendHttpRequest Activity

new SendHttpRequest
{
    Url = new(context =>
    {
        var userId = userIdVariable.Get(context);
        return new Uri($"https://reqres.in/api/users/{userId}");
    }),
    Method = new(HttpMethods.Get),
    ParsedContent = new(userVariable),
    ExpectedStatusCodes =
    {
        new HttpStatusCodeCase
        {
            StatusCode = StatusCodes.Status200OK,
            Activity = new WriteHttpResponse
            {
                Content = new(context =>
                {
                    var user = (dynamic)userVariable.Get(context)!;
                    return user.data;
                }),
                StatusCode = new(HttpStatusCode.OK)
            }
        },
        new HttpStatusCodeCase
        {
            StatusCode = StatusCodes.Status404NotFound,
            Activity = new WriteHttpResponse
            {
                Content = new("User not found"),
                StatusCode = new(HttpStatusCode.NotFound)
            }
        }
    }
}

The SendHttpRequest activity is configured to send an HTTP request to the reqres API endpoint.

We set its Url property to a URL that includes the received user ID.

To capture the response, we assign its ParsedContent output to the userVariable variable.

Since the caller of the workflow might provide user IDs that don't correspond to a user record in the reqres backend, we configure the activity to handle two possible HTTP status codes:

  • 200 OK
  • 404 Not Found

For each of these possible status codes, we assign an appropriate WriteHttpResponse activity.

for the 200 case, the WriteHttpResponse activity access the data field of the user response object received from reqres:

new HttpStatusCodeCase
{
    StatusCode = StatusCodes.Status200OK,
    Activity = new WriteHttpResponse
    {
        Content = new(context =>
        {
            var user = (dynamic)userVariable.Get(context)!;
            return user.data;
        }),
        StatusCode = new(HttpStatusCode.OK)
    }
},

Run Workflow

Since the workflow uses the HttpEndpoint activity, it will trigger when we send an HTTP request to the /workflows/users/{userId} path.

Try it out by navigating to https://localhost:5001/workflows/users/2.

The response should look similar to this:

{
    "id": 2,
    "email": "[email protected]",
    "first_name": "Janet",
    "last_name": "Weaver",
    "avatar": "https://reqres.in/img/faces/2-image.jpg"
}

Summary

In this guide, we learned how to define a workflow from code.

We leveraged the HttpEndpoint activity and used is as a trigger to start the workflow.

The workflow is able to read route parameters and store it in a variable, which we then used as an input to send an API call to the reqres API that in turn returns the requested user.

We have also seen how to handle various responses from reqres: 200 OK and 404 Not Found

The source code for this guide can be found here.