Skip to content

Commit

Permalink
Add workflow definition management services
Browse files Browse the repository at this point in the history
Added new services to manage workflow definitions, their history, and importing mechanisms. Refactored existing workflow definition functionalities into specific services to streamline code and improve modularity. Updated file imports, notifications, and dependency injection setups accordingly.
  • Loading branch information
sfmskywalker committed Oct 18, 2024
1 parent 5fa3e33 commit 783e2d3
Show file tree
Hide file tree
Showing 22 changed files with 374 additions and 144 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Responses;
using Elsa.Api.Client.Shared.Models;
using Elsa.Studio.Models;
using Elsa.Studio.Workflows.Domain.Models;

namespace Elsa.Studio.Workflows.Domain.Contracts;

/// <summary>
/// A service that can be used to manage workflow definitions.
/// </summary>
public interface IWorkflowDefinitionEditorService
{
/// <summary>
/// Saves a workflow definition.
/// </summary>
Task<Result<SaveWorkflowDefinitionResponse, ValidationErrors>> SaveAsync(WorkflowDefinition workflowDefinition, bool publish, Func<WorkflowDefinition, Task>? workflowSavedCallback = null, CancellationToken cancellationToken = default);

/// <summary>
/// Publishes a workflow definition.
/// </summary>
Task<SaveWorkflowDefinitionResponse> PublishAsync(WorkflowDefinition workflowDefinition, Func<WorkflowDefinition, Task>? workflowPublishedCallback = null, CancellationToken cancellationToken = default);


/// <summary>
/// Retracts a workflow definition.
/// </summary>
Task<Result<WorkflowDefinition, ValidationErrors>> RetractAsync(WorkflowDefinition workflowDefinition, Func<WorkflowDefinition, Task>? workflowRetractedCallback = null, CancellationToken cancellationToken = default);

/// <summary>
/// Exports a workflow definition.
/// </summary>
Task<FileDownload> ExportAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Studio.Models;
using Elsa.Studio.Workflows.Domain.Models;

namespace Elsa.Studio.Workflows.Domain.Contracts;

/// <summary>
/// A service that can be used to manage the history of workflow definitions.
/// </summary>
public interface IWorkflowDefinitionHistoryService
{
/// <summary>
/// Retracts a workflow definition.
/// </summary>
Task<Result<WorkflowDefinition, ValidationErrors>> RetractAsync(WorkflowDefinition workflowDefinition, Func<WorkflowDefinition, Task>? workflowRetractedCallback = null, CancellationToken cancellationToken = default);

/// <summary>
/// Reverts the specified workflow definition to the specified version.
/// </summary>
Task<WorkflowDefinitionSummary> RevertAsync(WorkflowDefinitionVersion workflowDefinitionVersion, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Refit;

namespace Elsa.Studio.Workflows.Domain.Contracts;

/// <summary>
/// A service that imports workflow definitions.
/// </summary>
public interface IWorkflowDefinitionImporter
{
/// <summary>
/// Imports a workflow definition.
/// </summary>
Task<WorkflowDefinition> ImportAsync(WorkflowDefinitionModel definitionModel, CancellationToken cancellationToken = default);

/// <summary>
/// Imports a set of files containing workflow definitions.
/// </summary>
Task<int> ImportAsync(IEnumerable<StreamPart> streamParts, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,6 @@ public interface IWorkflowDefinitionService
/// </summary>
Task<GetPathSegmentsResponse?> GetPathSegmentsAsync(string id, string? childNodeId = null, CancellationToken cancellationToken = default);

/// <summary>
/// Saves a workflow definition.
/// </summary>
Task<Result<SaveWorkflowDefinitionResponse, ValidationErrors>> SaveAsync(WorkflowDefinition workflowDefinition, bool publish, Func<WorkflowDefinition, Task>? workflowSavedCallback = null, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a workflow definition.
/// </summary>
Expand Down Expand Up @@ -108,11 +103,6 @@ public interface IWorkflowDefinitionService
/// </summary>
Task<FileDownload> ExportDefinitionAsync(string definitionId, VersionOptions? versionOptions = default, CancellationToken cancellationToken = default);

/// <summary>
/// Imports a workflow definition.
/// </summary>
Task<WorkflowDefinition> ImportDefinitionAsync(WorkflowDefinitionModel definitionModel, CancellationToken cancellationToken = default);

/// <summary>
/// Exports a set of workflow definitions.
/// </summary>
Expand All @@ -123,18 +113,8 @@ public interface IWorkflowDefinitionService
/// </summary>
Task<UpdateConsumingWorkflowReferencesResponse> UpdateReferencesAsync(string definitionId, CancellationToken cancellationToken = default);

/// <summary>
/// Reverts the specified workflow definition to the specified version.
/// </summary>
Task<WorkflowDefinitionSummary> RevertVersionAsync(WorkflowDefinitionVersion workflowDefinitionVersion, CancellationToken cancellationToken = default);

/// <summary>
/// Executes a workflow definition.
/// </summary>
Task<string> ExecuteAsync(string definitionId, ExecuteWorkflowDefinitionRequest? request, CancellationToken cancellationToken = default);

/// <summary>
/// Imports a set of files containing workflow definitions.
/// </summary>
Task<int> ImportFilesAsync(IEnumerable<StreamPart> streamParts, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Elsa.Api.Client.Shared.Models;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Studio.Contracts;
using Elsa.Studio.Workflows.Domain.Models;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionExported(string WorkflowDefinitionId, VersionOptions? VersionOptions, FileDownload FileDownload) : INotification;
public record WorkflowDefinitionExported(WorkflowDefinition WorkflowDefinition, FileDownload FileDownload) : INotification;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Elsa.Api.Client.Shared.Models;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Studio.Contracts;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionExporting(string WorkflowDefinitionId, VersionOptions? VersionOptions) : INotification;
public record WorkflowDefinitionExporting(WorkflowDefinition WorkflowDefinition) : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Elsa.Api.Client.Shared.Models;
using Elsa.Studio.Contracts;
using Elsa.Studio.Workflows.Domain.Models;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionIdExported(string WorkflowDefinitionId, VersionOptions? VersionOptions, FileDownload FileDownload) : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Elsa.Api.Client.Shared.Models;
using Elsa.Studio.Contracts;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionIdExporting(string WorkflowDefinitionId, VersionOptions? VersionOptions) : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Elsa.Studio.Contracts;

namespace Elsa.Studio.Workflows.Domain.Notifications;

/// <summary>
/// Represents a notification sent when a workflow definition is about to be published by its ID.
/// </summary>
public record WorkflowDefinitionIdPublishing(string WorkflowDefinitionId) : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Elsa.Studio.Contracts;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionIdRetracting(string WorkflowDefinitionId) : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Elsa.Studio.Contracts;
using Elsa.Studio.Workflows.Domain.Models;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionIdRetractingFailed(string WorkflowDefinitionId, ValidationErrors Errors) : INotification;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Studio.Contracts;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionRetracted(string WorkflowDefinitionId) : INotification;
public record WorkflowDefinitionRetracted(WorkflowDefinition WorkflowDefinition) : INotification;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Studio.Contracts;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionRetracting(string WorkflowDefinitionId) : INotification;
public record WorkflowDefinitionRetracting(WorkflowDefinition WorkflowDefinition) : INotification;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Studio.Contracts;
using Elsa.Studio.Workflows.Domain.Models;

namespace Elsa.Studio.Workflows.Domain.Notifications;

public record WorkflowDefinitionRetractingFailed(string WorkflowDefinitionId, ValidationErrors Errors) : INotification;
public record WorkflowDefinitionRetractingFailed(WorkflowDefinition WorkflowDefinition, ValidationErrors Errors) : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using Elsa.Api.Client.Extensions;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Requests;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Responses;
using Elsa.Api.Client.Shared.Models;
using Elsa.Studio.Contracts;
using Elsa.Studio.Models;
using Elsa.Studio.Workflows.Domain.Contracts;
using Elsa.Studio.Workflows.Domain.Extensions;
using Elsa.Studio.Workflows.Domain.Models;
using Elsa.Studio.Workflows.Domain.Notifications;
using Refit;

namespace Elsa.Studio.Workflows.Domain.Services;

/// <summary>
/// A workflow definition service that uses a remote backend to retrieve workflow definitions.
/// </summary>
public class WorkflowDefinitionEditorService(IRemoteBackendApiClientProvider remoteBackendApiClientProvider, IMediator mediator) : IWorkflowDefinitionEditorService
{
/// <inheritdoc />
public async Task<Result<SaveWorkflowDefinitionResponse, ValidationErrors>> SaveAsync(WorkflowDefinition workflowDefinition, bool publish, Func<WorkflowDefinition, Task>? workflowSavedCallback = null, CancellationToken cancellationToken = default)
{
var request = new SaveWorkflowDefinitionRequest
{
Model = new WorkflowDefinitionModel
{
Id = workflowDefinition.Id,
Description = workflowDefinition.Description,
Name = workflowDefinition.Name,
ToolVersion = workflowDefinition.ToolVersion,
Inputs = workflowDefinition.Inputs,
Options = workflowDefinition.Options,
Outcomes = workflowDefinition.Outcomes,
Outputs = workflowDefinition.Outputs,
Variables = workflowDefinition.Variables.Select(x => new VariableDefinition
{
Id = x.Id,
Name = x.Name,
TypeName = x.TypeName,
Value = x.Value?.ToString(),
IsArray = x.IsArray,
StorageDriverTypeName = x.StorageDriverTypeName
}).ToList(),
Version = workflowDefinition.Version,
CreatedAt = workflowDefinition.CreatedAt,
CustomProperties = workflowDefinition.CustomProperties,
DefinitionId = workflowDefinition.DefinitionId,
IsLatest = workflowDefinition.IsLatest,
IsPublished = workflowDefinition.IsPublished,
Root = workflowDefinition.Root
},
Publish = publish,
};

var api = await GetApiAsync(cancellationToken);

try
{
if (request.Publish == true) await mediator.NotifyAsync(new WorkflowDefinitionPublishing(workflowDefinition), cancellationToken);
await mediator.NotifyAsync(new WorkflowDefinitionSaving(workflowDefinition), cancellationToken);
var response = await api.SaveAsync(request, cancellationToken);

if(workflowSavedCallback != null)
await workflowSavedCallback(response.WorkflowDefinition);

await mediator.NotifyAsync(new WorkflowDefinitionSaved(response.WorkflowDefinition), cancellationToken);
if (request.Publish == true) await mediator.NotifyAsync(new WorkflowDefinitionPublished(response.WorkflowDefinition), cancellationToken);
return new(response);
}
catch (ValidationApiException e)
{
var errors = e.GetValidationErrors();
await mediator.NotifyAsync(new WorkflowDefinitionSavingFailed(workflowDefinition, errors), cancellationToken);
if (request.Publish == true) await mediator.NotifyAsync(new WorkflowDefinitionPublishingFailed(workflowDefinition, errors), cancellationToken);
return new(errors);
}
}

/// <inheritdoc />
public async Task<SaveWorkflowDefinitionResponse> PublishAsync(WorkflowDefinition workflowDefinition, Func<WorkflowDefinition, Task>? workflowPublishedCallback = null, CancellationToken cancellationToken = default)
{
var api = await GetApiAsync(cancellationToken);
await mediator.NotifyAsync(new WorkflowDefinitionPublishing(workflowDefinition), cancellationToken);
var response = await api.PublishAsync(workflowDefinition.DefinitionId, new PublishWorkflowDefinitionRequest(), cancellationToken);
if(workflowPublishedCallback != null) await workflowPublishedCallback(workflowDefinition);
await mediator.NotifyAsync(new WorkflowDefinitionPublished(workflowDefinition), cancellationToken);
return response;
}

/// <inheritdoc />
public async Task<Result<WorkflowDefinition, ValidationErrors>> RetractAsync(WorkflowDefinition workflowDefinition, Func<WorkflowDefinition, Task>? workflowRetractedCallback = null, CancellationToken cancellationToken = default)
{
try
{
var api = await GetApiAsync(cancellationToken);
await mediator.NotifyAsync(new WorkflowDefinitionRetracting(workflowDefinition), cancellationToken);
var definition = await api.RetractAsync(workflowDefinition.DefinitionId, new RetractWorkflowDefinitionRequest(), cancellationToken);
if(workflowRetractedCallback != null) await workflowRetractedCallback(definition);
await mediator.NotifyAsync(new WorkflowDefinitionRetracted(definition), cancellationToken);
return new(definition);
}
catch (ValidationApiException e)
{
var errors = e.GetValidationErrors();
await mediator.NotifyAsync(new WorkflowDefinitionRetractingFailed(workflowDefinition, errors), cancellationToken);
return new(errors);
}
}

/// <inheritdoc />
public async Task<FileDownload> ExportAsync(WorkflowDefinition workflowDefinition, CancellationToken cancellationToken = default)
{
var api = await GetApiAsync(cancellationToken);
await mediator.NotifyAsync(new WorkflowDefinitionExporting(workflowDefinition), cancellationToken);
var response = await api.ExportAsync(workflowDefinition.DefinitionId, VersionOptions.SpecificVersion(workflowDefinition.Version), cancellationToken);
var fileName = response.GetDownloadedFileNameOrDefault($"workflow-definition-{workflowDefinition.DefinitionId}-v{workflowDefinition.Version}.json");
var fileDownload = new FileDownload(fileName, response.Content!);
await mediator.NotifyAsync(new WorkflowDefinitionExported(workflowDefinition, fileDownload), cancellationToken);

return fileDownload;
}

/// <inheritdoc />
public async Task<WorkflowDefinitionSummary> RevertAsync(WorkflowDefinitionVersion workflowDefinitionVersion, CancellationToken cancellationToken = default)
{
var api = await GetApiAsync(cancellationToken);
await mediator.NotifyAsync(new WorkflowDefinitionReverting(workflowDefinitionVersion), cancellationToken);
var newWorkflowDefinitionSummary = await api.RevertVersionAsync(workflowDefinitionVersion.WorkflowDefinitionId, workflowDefinitionVersion.Version, cancellationToken);
var newWorkflowDefinitionVersion = WorkflowDefinitionVersion.FromDefinitionSummary(newWorkflowDefinitionSummary);
await mediator.NotifyAsync(new WorkflowDefinitionReverted(newWorkflowDefinitionVersion), cancellationToken);
return newWorkflowDefinitionSummary;
}

private async Task<IWorkflowDefinitionsApi> GetApiAsync(CancellationToken cancellationToken = default)
{
return await remoteBackendApiClientProvider.GetApiAsync<IWorkflowDefinitionsApi>(cancellationToken);
}
}
Loading

0 comments on commit 783e2d3

Please sign in to comment.