Skip to content

serverlessworkflow/sdk-net

Repository files navigation

Publish Packages Gitpod ready-to-code

Serverless Workflow Specification - .NET SDK

Provides .NET 7.0 API/SPI and Model Validation for the Serverless Workflow Specification

With the SDK, you can:

  • Read and write workflow JSON and YAML definitions
  • Programmatically build workflow definitions
  • Validate workflow definitions (both schema and DSL integrity validation)

Status

Latest Releases Conformance to spec version
0.8.7 0.8

Getting Started

dotnet nuget add package ServerlessWorkflow.Sdk
services.AddServerlessWorkflow();

Usage

Build workflows programatically

var workflow = WorkflowDefinition.Create("MyWorkflow", "MyWorkflow", "1.0")
  .StartsWith("inject", flow => 
      flow.Inject(new { username = "test", password = "123456" }))
  .Then("operation", flow =>
      flow.Execute("fakeApiFunctionCall", action =>
      {
          action.Invoke(function =>
              function.WithName("fakeFunction")
                  .SetOperationUri(new Uri("https://fake.com/swagger.json#fake")))
              .WithArgument("username", "${ .username }")
              .WithArgument("password", "${ .password }");
      })      
          .Execute("fakeEventTrigger", action =>
          {
               action
                    .Consume(e =>
                        e.WithName("fakeEvent")
                            .WithSource(new Uri("https://fakesource.com"))
                            .WithType("fakeType"))
                    .ThenProduce(e =>
                        e.WithName("otherEvent")
                            .WithSource(new Uri("https://fakesource.com"))
                            .WithType("fakeType"));
          }))
  .End()
  .Build();

Read workflows

var reader = WorkflowReader.Create();
using(Stream stream = File.OpenRead("myWorkflow.json"))
{
  var definition = reader.Read(stream, WorkflowDefinitionFormat.Json);
}

Write workflows

  var writer = WorkflowWriter.Create();
  using(Stream stream = new MemoryStream())
  {
      writer.Write(workflow, stream);
      stream.Flush();
      stream.Position = 0;
      using(StreamReader reader = new StreamReader(stream))
      {
          var yaml = reader.ReadToEnd();
          Console.WriteLine(yaml);
          Console.ReadLine();
      }
  }

Validate workflows

var validator = serviceProvider.GetRequiredService<IValidator<WorkflowDefinition>>();
var validationResult = validator.Validate(myWorkflow);

Extend Workflows

The SDK allows extending the Serverless Workflow in two ways, possibly combined: via metadata and via extensions.

Metadata

Workflow components that support metadata, such as WorkflowDefinition or StateDefinition, expose a metadata property, which is a dynamic name/value mapping of properties used to enrich the serverless workflow model with information beyond its core definitions.

It has the advantage of being an easy, cross-compatible way of declaring additional data, but lacks well-defined, well-documented schema of the data, thus loosing the ability to validate it without custom implementation.

Adding metadata to a workflow:

var workflow = new WorkflowBuilder()
    ...
    .WithMetadata(new Dictionary<string, object>() { { "metadataPropertyName", metadataPropertyValue } })
    ...
    .Build();

Resulting workflow:

id: sample-workflow
version: 1.0.0
specVersion: 0.8
metadata:
  metadataPropertyName: metadataPropertyValue #added to the metadata property of supporting components
...

Extension

Users have the ability to define extensions, providing the ability to extend, override or replace parts of the Serverless Workflow schema.

To do so, you must first create a file containing the JsonSchema of your extension, then reference it in your workflow definition.

Schema of a sample extension that adds a new greet functionType:

JSON YAML
{
  "$defs": {
    "functions": {
      "definitions": {
        "function": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "description": "Defines the function type. Is either `rest`, `asyncapi, `rpc`, `graphql`, `odata`, `expression` or `greet`. Default is `rest`",
              "enum": [
                "rest",
                "asyncapi",
                "rpc",
                "graphql",
                "odata",
                "expression",
                "custom",
                "greet"
              ],
              "default": "rest"
            }
          }
        }
      }
    }
  }
}
'$defs':
  functions:
    definitions:
      function:
        type: object
        properties:
          type:
            type: string
            description: Defines the function type. Is either `rest`, `asyncapi, `rpc`,
              `graphql`, `odata`, `expression` or `greet`. Default is `rest`
            enum:
            - rest
            - asyncapi
            - rpc
            - graphql
            - odata
            - expression
            - custom
            - greet
            default: rest

The above example refers to /$defs/functions, because upon validation the SDK bundles all the Serverless Workflow Specification's schemas into the $defs property of a single schema.

*In this case, functions is the extensionless name of the schema file we want to override (https://serverlessworkflow.io/schemas/latest/functions.json).

A Json Merge Patch is performed sequentially on the bundled schema with the defined extensions, in declaring order.

In this case, the above schema will patch the object defined at /functions/definitions/function in file https://serverlessworkflow.io/schemas/latest/functions.json

Extending a workflow:

var workflow = new WorkflowBuilder()
    .WithId("sample-extended")
    .WithName("Sample Extended Workflow")
    .WithVersion("1.0.0")
    .UseSpecVersion(ServerlessWorkflowSpecVersion.V08)
    .UseExtension("extensionId", new Uri("file://.../extensions/greet-function-type.json"))
    .StartsWith("do-work", flow => flow.Execute("greet", action => action
        .Invoke(function => function
            .OfType("greet")
            .WithName("greet")
            .ForOperation("#"))))
    .End()
    .Build();

Adding extension properties:

var workflow = new WorkflowBuilder()
    ...
    .WithExtensionProperty("extensionPropertyName", propertyValue } })
    ...
    .Build();

Resulting workflow:

id: sample-workflow
version: 1.0.0
specVersion: 0.8
extensionPropertyName: propertyValue #added as top level property of extended component, as opposed to metadata
...