diff --git a/README.md b/README.md index 39992cf56..bc849fdd6 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,6 @@ docker run -t -i -e ASPNETCORE_ENVIRONMENT='Development' -e HTTP_PORTS=8080 -p 1 - [Features](#features) - [Roadmap](#roadmap) - [Use Cases](#use-cases) -- [Console Example](#console-example) -- [ASP.NET Example](#aspnet-example) -- [Elsa Server + Elsa Studio](#elsa-server--elsa-studio) -- [Building from Source](#building-from-source) ## Documentation @@ -99,143 +95,12 @@ Elsa can be used in a variety of scenarios, including: - Scheduled workflows such as sending daily reports. - Event-driven workflows such as sending welcome emails when a user signs up. -## Console Example +## Programmatic Workflows -Let's explore a simple example demonstrating how to create and run a workflow in a console application. In this example, we'll create a workflow that writes "Hello World!" to the console. - -First, create a new console application and add the following NuGet packages: - -```shell -dotnet new console -n "ElsaConsole" -f net8.0 -cd ElsaConsole -dotnet add package Elsa -``` - -Open Program.cs and replace its contents with the following code: +Elsa allows you to define workflows in code using C#. The following example shows how to receive HTTP requests and send an email in response: ```csharp -using Elsa.Extensions; -using Elsa.Workflows.Core.Activities; -using Elsa.Workflows.Core.Contracts; -using Microsoft.Extensions.DependencyInjection; - -// Set up the service container. -var services = new ServiceCollection(); - -// Add Elsa services. -services.AddElsa(); - -// Build the service container. -var serviceProvider = services.BuildServiceProvider(); - -// Create a workflow. -var workflow = new WriteLine("Hello World!"); - -// Resolve a workflow runner to run the workflow. -var workflowRunner = serviceProvider.GetRequiredService(); - -// Run the workflow. -await workflowRunner.RunAsync(workflow); -``` - -Run the application using the following command: - -```shell -dotnet run -``` - -The application should output the following text: - -```shell -Hello World! -``` - -You can also create more complex workflows. For example, the following demonstrates how to create a workflow that writes two messages to the console: - -```csharp -var workflow = new Sequence -{ - Activities = - { - new WriteLine("Hello World!"), - new WriteLine("Goodbye cruel world...") - } -}; -``` - -Which will output the following text: - -```shell -Hello World! -Goodbye cruel world... -``` - -# ASP.NET Example - -When working with workflows that involve timers, messages and other events, running a simple Console application is not enough. -In this case, we need a proper host that can run the workflows in the background and handle events. - -ASP.NET Core is a great host for this purpose. The following example demonstrates how to create a simple ASP.NET Core application that acts as a workflow server. - -First, create a new ASP.NET Core application and add the following NuGet packages: - -```shell -dotnet new web -n "ElsaWeb" -f net8.0 -cd ElsaWeb -dotnet add package Elsa --prerelease -dotnet add package Elsa.Http --prerelease -``` - -```csharp -using Elsa.Extensions; - -var builder = WebApplication.CreateBuilder(args); -var services = builder.Services; - -// Add Elsa services. -services.AddElsa(elsa => elsa - - // Add workflows from this program. - .AddWorkflowsFrom() - - // Enable Elsa HTTP module for HTTP related activities. - .UseHttp() -); - -// Configure ASP.NET's middleware pipeline. -var app = builder.Build(); - -if (app.Environment.IsDevelopment()) - app.UseDeveloperExceptionPage(); - -// Add Elsa HTTP middleware to handle requests mapped to HTTP Endpoint activities. -app.UseWorkflows(); - -// Start accepting requests. -app.Run(); -``` - -The above example demonstrates how to: - -- Add workflows from the current program. -- Enable the HTTP module to handle HTTP related activities. - -Next, let's define a workflow that handles HTTP requests. - -### HTTP Workflow - -Create a new file called `HelloWorldHttpWorkflow.cs` and add the following code: - -```csharp -using System.Net; -using Elsa.Http; -using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities; -using Elsa.Workflows.Core.Contracts; - -namespace ElsaWeb; - -public class HelloWorldHttpWorkflow : WorkflowBase +public class SendEmailWorkflow : WorkflowBase { protected override void Build(IWorkflowBuilder builder) { @@ -245,14 +110,16 @@ public class HelloWorldHttpWorkflow : WorkflowBase { new HttpEndpoint { - Path = new("/hello-world"), - SupportedMethods = new(new[] { HttpMethods.Get }), + Path = new("/send-email"), + SupportedMethods = new(new[] { HttpMethods.Post }), CanStartWorkflow = true }, - new WriteHttpResponse + new SendEmail { - StatusCode = new(HttpStatusCode.OK), - Content = new("Hello world!") + From = new("alic@acme.com"), + To = new(new[]{ "bob@acme.com" }), + Subject = new("Your workflow has been triggered!"), + Body = new("Hello!") } } }; @@ -260,405 +127,8 @@ public class HelloWorldHttpWorkflow : WorkflowBase } ``` -The above example demonstrates how to: +## Designed Workflows -- Create an HTTP endpoint that listens for GET requests on the `/workflows/hello-world` path. -- Respond to the request with a `200 OK` status code and a `Hello world!` message. -- The `CanStartWorkflow` property is set to `true` to indicate that this endpoint can start a workflow. -- The `HttpEndpoint` activity is followed by a `WriteHttpResponse` activity that writes the response to the client. +Elsa allows you to define workflows using a visual designer. The following example shows how to receive HTTP requests and send an email in response: -Run the application using the following command: - -```shell -dotnet run -``` - -The application should now be accessible at https://localhost:5001/workflows/hello-world. The port number might vary based on your configuration. -The response should look like this: - -```shell -Hello world! -``` - -> The prefix `/workflows` is added by the Elsa HTTP module. This prefix can be configured via the `ElsaOptions.Http` property. For example: -> `elsa.UseHttp(http => http.ConfigureHttpOptions = options => options.BasePath = "/my-workflows");` - -### Timer - -Let's take a look at another workflow that automatically executes every 5 seconds. - -First, add the following package: - -```shell -dotnet add package Elsa.Scheduling --prerelease -``` - -Then update the `Program.cs` file as follows by adding the `UseScheduling()` method inside the `AddElsa()` method: - -```csharp -// Enable timer based activities. -.UseScheduling() -``` - -Next, create a new file called `HeartbeatWorkflow.cs` and add the following code: - -```csharp -using Elsa.Common.Contracts; -using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities; -using Elsa.Workflows.Core.Contracts; - -namespace ElsaWeb; - -public class HeartbeatWorkflow : WorkflowBase -{ - protected override void Build(IWorkflowBuilder builder) - { - builder.Root = new Sequence - { - Activities = - { - new Elsa.Scheduling.Activities.Timer(TimeSpan.FromSeconds(5)) - { - CanStartWorkflow = true - }, - new WriteLine(context => $"Heartbeat at {context.GetRequiredService().UtcNow}"), - } - }; - } -} -``` - -Run the application using the following command: - -```shell -dotnet run -``` - -You should see the output similar to the following: - -```shell -Heartbeat at 2021-10-10 12:00:00Z -Heartbeat at 2021-10-10 12:00:05Z -Heartbeat at 2021-10-10 12:00:10Z -... -``` - -The above example demonstrates how to: - -- Create a timer that executes every 5 seconds. -- The `CanStartWorkflow` property is set to `true` to indicate that this timer can start a workflow. -- The `Timer` activity is followed by a `WriteLine` activity that writes the current time to the console. -- The `ISystemClock` service is used to get the current time. -- The `context` parameter is used to access the service. - -## Elsa Server + Elsa Studio - -While we've explored how to run workflows using simple Console and ASP.NET Core applications, these applications don't provide a way to design workflows. To bridge this gap, we introduce: - -Elsa Server: An ASP.NET Core application that exposes API endpoints for designing workflows. -Elsa Studio: A Blazor application tailored for designing workflows with ease. -To set up a simple Elsa Server application, follow these step-by-step instructions: - -1. Create a new ASP.NET Core application. -2. Add the necessary packages -3. Make the necessary changes in Program.cs - -Let's go through the above steps in detail. - -### Create Elsa Server - -Create a new ASP.NET Core application using the following command: - -```shell -dotnet new web -n "ElsaServer" -f net8.0 -cd ElsaServer -dotnet add package Elsa --prerelease -dotnet add package Elsa.EntityFrameworkCore --prerelease -dotnet add package Elsa.EntityFrameworkCore.Sqlite --prerelease -dotnet add package Elsa.Identity --prerelease -dotnet add package Elsa.Scheduling --prerelease -dotnet add package Elsa.Workflows.Api --prerelease -dotnet add package Elsa.CSharp --prerelease -``` - -Next, open Program.cs file and replace its contents with the following code: - -```csharp -using Elsa.EntityFrameworkCore.Modules.Management; -using Elsa.EntityFrameworkCore.Modules.Runtime; -using Elsa.Extensions; - -var builder = WebApplication.CreateBuilder(args); -builder.Services.AddElsa(elsa => -{ - // Configure Management layer to use EF Core. - elsa.UseWorkflowManagement(management => management.UseEntityFrameworkCore()); - - // Configure Runtime layer to use EF Core. - elsa.UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore()); - - // Default Identity features for authentication/authorization. - elsa.UseIdentity(identity => - { - identity.TokenOptions = options => options.SigningKey = "sufficiently-large-secret-signing-key"; //This key needs to be at least 256 bits long. - identity.UseAdminUserProvider(); - }); - - // Configure ASP.NET authentication/authorization. - elsa.UseDefaultAuthentication(auth => auth.UseAdminApiKey()); - - // Expose Elsa API endpoints. - elsa.UseWorkflowsApi(); - - // Setup a SignalR hub for real-time updates from the server. - elsa.UseRealTimeWorkflows(); - - // Enable C# workflow expressions - elsa.UseCSharp(); - - // Enable HTTP activities. - elsa.UseHttp(); - - // Use timer activities. - elsa.UseScheduling(); - - // Register custom activities from the application, if any. - elsa.AddActivitiesFrom(); - - // Register custom workflows from the application, if any. - elsa.AddWorkflowsFrom(); -}); - -// Configure CORS to allow designer app hosted on a different origin to invoke the APIs. -builder.Services.AddCors(cors => cors - .AddDefaultPolicy(policy => policy - .AllowAnyOrigin() // For demo purposes only. Use a specific origin instead. - .AllowAnyHeader() - .AllowAnyMethod() - .WithExposedHeaders("x-elsa-workflow-instance-id"))); // Required for Elsa Studio in order to support running workflows from the designer. Alternatively, you can use the `*` wildcard to expose all headers. - -// Add Health Checks. -builder.Services.AddHealthChecks(); - -// Build the web application. -var app = builder.Build(); - -// Configure web application's middleware pipeline. -app.UseCors(); -app.UseRouting(); // Required for SignalR. -app.UseAuthentication(); -app.UseAuthorization(); -app.UseWorkflowsApi(); // Use Elsa API endpoints. -app.UseWorkflows(); // Use Elsa middleware to handle HTTP requests mapped to HTTP Endpoint activities. -app.UseWorkflowsSignalRHubs(); // Optional SignalR integration. Elsa Studio uses SignalR to receive real-time updates from the server. - -app.Run(); -``` - -To run the application on port 5001, execute the following command: - -```shell -dotnet run --urls "https://localhost:5001" -``` - -The above example demonstrates how to: - -- Configure Elsa to use Entity Framework Core for persistence. -- Configure Elsa to use ASP.NET Core Identity for authentication/authorization. -- Configure Elsa to use ASP.NET Core authentication/authorization. -- Expose Elsa API endpoints. -- Setup a SignalR hub for real-time updates from the server. -- Enable C# workflow expressions. -- Enable HTTP activities. -- Enable timer activities. -- Register custom activities from the application, if any. -- Register custom workflows from the application, if any. -- Configure CORS to allow designer app hosted on a different origin to invoke the APIs. -- Add Health Checks. -- Configure ASP.NET's middleware pipeline. -- Use Elsa API endpoints. -- Use Elsa middleware to handle HTTP requests mapped to HTTP Endpoint activities. -- Use SignalR integration. Elsa Studio uses SignalR to receive real-time updates from the server. -- Run the application on port 5001. - -Now that we have an Elsa Server running, let's create an Elsa Studio application. - -### Create Elsa Studio - -Create a new Blazor WebAssembly application using the following command: - -```shell -dotnet new blazorwasm-empty -n "ElsaStudio" -f net8.0 -cd ElsaStudio -dotnet add package Elsa.Studio --prerelease -dotnet add package Elsa.Studio.Core.BlazorWasm --prerelease -dotnet add package Elsa.Studio.Login.BlazorWasm --prerelease -``` - -Next, open Program.cs file and replace its contents with the following code: - -```csharp -using Elsa.Studio.Dashboard.Extensions; -using Elsa.Studio.Shell; -using Elsa.Studio.Shell.Extensions; -using Elsa.Studio.Workflows.Extensions; -using Elsa.Studio.Contracts; -using Elsa.Studio.Core.BlazorWasm.Extensions; -using Elsa.Studio.Extensions; -using Elsa.Studio.Login.BlazorWasm.Extensions; -using Elsa.Studio.Login.HttpMessageHandlers; -using Elsa.Studio.Workflows.Designer.Extensions; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; - -// Build the host. -var builder = WebAssemblyHostBuilder.CreateDefault(args); - -// Register root components. -builder.RootComponents.Add("#app"); -builder.RootComponents.Add("head::after"); -builder.RootComponents.RegisterCustomElsaStudioElements(); - -// Register shell services and modules. -builder.Services.AddCore(); -builder.Services.AddShell(); -builder.Services.AddRemoteBackend( - options => configuration.GetSection("Backend").Bind(options), - configureElsaClientBuilderOptions: elsaClient => elsaClient.ConfigureHttpClientBuilder = httpClientBuilder => httpClientBuilder.AddHttpMessageHandler()); -builder.Services.AddLoginModule(); -builder.Services.AddDashboardModule(); -builder.Services.AddWorkflowsModule(); - -// Build the application. -var app = builder.Build(); - -// Run each startup task. -var startupTaskRunner = app.Services.GetRequiredService(); -await startupTaskRunner.RunStartupTasksAsync(); - -// Run the application. -await app.RunAsync(); -``` - -For a cleaner project structure, delete the following directories and files: - -- wwwroot/css -- Pages -- App.razor -- MainLayout.razor - -Within the wwwroot directory, create a new appsettings.json file and populate it with the subsequent content: - -```json -{ - "Backend": { - "Url": "https://localhost:5001/elsa/api" - } -} -``` - -Finally, open the wwwroot/index.html file and replace its content with the code showcased below: - -```html - - - - - - - MyApplication - - - - - - - - - - - - - - - - - - - -
-
-
Loading...
-
-
- -
- An unhandled error has occurred. - Reload - 🗙 -
- - - - - - - - - - -``` - -Run the following command to start the application on port 6001: - -```shell -dotnet run --urls "https://localhost:6001" -``` - -Your application should now be accessible at https://localhost:6001. - -![Elsa Studio Login](./design/screenshots/elsa-studio-login.png) - -By default, you can log in using: - -``` -Username: admin -Password: password -``` - -In the above example, we have: - -- Created and configured an Elsa Studio Blazor WebAssembly application. -- Configured the backend URL to point to the Elsa Server application. -- Configured the application to use the `AuthenticatingApiHttpMessageHandler` to authenticate requests to the backend. -- Configured the application to use the `Elsa.Studio.Login.BlazorWasm` module for authentication. -- Configured the application to use the `Elsa.Studio.Dashboard` module for the dashboard. -- Configured the application to use the `Elsa.Studio.Workflows` module for workflows. -- Run the application on port 6001. - -## Building from Source - -To build Elsa from source, you'll need: - -- .NET 6 SDK or later - -To build the solution, run the following command: - -```shell -dotnet build Elsa.sln -``` - -To run the tests, run the following command: - -```shell -dotnet test Elsa.sln -``` - -To run the Elsa.AllInOne.Web project, run the following command: - -```shell -dotnet run --project src/Elsa.AllInOne.Web/Elsa.AllInOne.Web.csproj -``` - -The AllInOne project is a sample application that demonstrates how to use Elsa Server + Elsa Studio in a single application. \ No newline at end of file +![Elsa ships with a powerful visual designer](./design/screenshots/http-send-email-workflow-designer.png) \ No newline at end of file diff --git a/design/screenshots/http-send-email-workflow-designer.png b/design/screenshots/http-send-email-workflow-designer.png new file mode 100644 index 000000000..994a03ebf Binary files /dev/null and b/design/screenshots/http-send-email-workflow-designer.png differ