Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement/runtime options provider#4491 #4614

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/clients/Elsa.Api.Client/Contracts/IElsaClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
using Elsa.Api.Client.Resources.Scripting.Contracts;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
using Elsa.Api.Client.Resources.WorkflowInstances.Contracts;
Expand All @@ -13,13 +14,17 @@ public interface IElsaClient
/// <summary>
/// Gets the workflow definitions API.
/// </summary>
IWorkflowDefinitionsApi WorkflowDefinitions { get; }
IWorkflowDefinitionsApi WorkflowDefinitions { get; }
/// <summary>
/// Gets the activity descriptors API.
/// </summary>
IActivityDescriptorsApi ActivityDescriptors { get; }

IActivityDescriptorsApi ActivityDescriptors { get; }

/// <summary>
/// Get the activity descriptor Options API
/// </summary>
IActivityDescriptorOptionsApi ActivityDescriptorOptions { get; }
/// <summary>
/// Gets the workflow instances API.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Elsa.Api.Client.Converters;
using Elsa.Api.Client.HttpMessageHandlers;
using Elsa.Api.Client.Options;
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
using Elsa.Api.Client.Resources.ActivityExecutions.Contracts;
using Elsa.Api.Client.Resources.Features.Contracts;
Expand Down Expand Up @@ -45,6 +46,7 @@ public static IServiceCollection AddElsaClient(this IServiceCollection services,
services.AddApi<IWorkflowDefinitionsApi>(builderOptions);
services.AddApi<IWorkflowInstancesApi>(builderOptions);
services.AddApi<IActivityDescriptorsApi>(builderOptions);
services.AddApi<IActivityDescriptorOptionsApi>(builderOptions);
services.AddApi<IActivityExecutionsApi>(builderOptions);
services.AddApi<IStorageDriversApi>(builderOptions);
services.AddApi<IVariableTypesApi>(builderOptions);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Requests;
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Responses;
using Refit;

namespace Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;

/// <summary>
/// Represents a client for the workflow definitions API.
/// </summary>
public interface IActivityDescriptorOptionsApi
{
/// <summary>
/// Lists activity descriptors options.
/// </summary>
/// <param name="activityTypeName">The TypeName of the activity </param>
/// <param name="propertyName">The name of the property</param>
/// <param name="request">The context request.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The response containing the activity descriptors.</returns>
[Get("/descriptors/activities/{activityTypeName}/options/{propertyName}")]
Task<GetActivityDescriptorOptionsResponse> GetAsync(string activityTypeName, string propertyName, [Body]GetActivityDescriptorOptionsRequest request, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Refit;

namespace Elsa.Api.Client.Resources.ActivityDescriptorOptions.Requests;

/// <summary>
/// Represents a request to Get Activity Descriptor Options.
/// </summary>
public class GetActivityDescriptorOptionsRequest
{
/// <summary>
/// Object context use to pass custom information
/// </summary>
public object? Context { get; set; } = default;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Elsa.Api.Client.Resources.ActivityDescriptorOptions.Responses;
/// <summary>
/// Represents a response from get activity descriptors Options.
/// </summary>
/// <param name="Items">The options elements.</param>
public record GetActivityDescriptorOptionsResponse(IDictionary<string,object> Items);
12 changes: 9 additions & 3 deletions src/clients/Elsa.Api.Client/Services/ElsaClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Elsa.Api.Client.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
using Elsa.Api.Client.Resources.Scripting.Contracts;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
Expand All @@ -15,20 +16,25 @@ public class ElsaClient : IElsaClient
public ElsaClient(IWorkflowDefinitionsApi workflowDefinitions
, IWorkflowInstancesApi workflowInstances
, IActivityDescriptorsApi activityDescriptors
, IActivityDescriptorOptionsApi activityDescriptorOptions
, IJavaScriptApi javaScript)
{
WorkflowDefinitions = workflowDefinitions;
WorkflowInstances = workflowInstances;
ActivityDescriptors = activityDescriptors;
ActivityDescriptors = activityDescriptors;
ActivityDescriptorOptions = activityDescriptorOptions;
JavaScript = javaScript;
}

/// <inheritdoc />
public IWorkflowDefinitionsApi WorkflowDefinitions { get; }

/// <inheritdoc />
public IActivityDescriptorsApi ActivityDescriptors { get; }

public IActivityDescriptorsApi ActivityDescriptors { get; }

/// <inheritdoc />
public IActivityDescriptorOptionsApi ActivityDescriptorOptions { get; }

/// <inheritdoc />
public IWorkflowInstancesApi WorkflowInstances { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Elsa.CSharp.Activities;

internal class RunCSharpOptionsProvider : IActivityPropertyOptionsProvider
{
public ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property, CancellationToken cancellationToken = default)
public ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property,object? context, CancellationToken cancellationToken = default)
{
var options = new Dictionary<string, object>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@
using System.Threading.Tasks;
using Elsa.Http.ContentWriters;
using Elsa.Workflows.Core.Contracts;

namespace Elsa.Http.ActivityOptionProviders;

/// <summary>
/// Provides options for the <see cref="SendHttpRequest"/> activity's <see cref="SendHttpRequest.ContentType"/> property.
/// </summary>
public class HttpContentTypeOptionsProvider : IActivityPropertyOptionsProvider
{
private readonly IEnumerable<IHttpContentFactory> _httpContentFactories;

private readonly IEnumerable<IHttpContentFactory> _httpContentFactories;
/// <summary>
/// Creates a new instance of the <see cref="HttpContentTypeOptionsProvider"/> class.
/// </summary>
public HttpContentTypeOptionsProvider(IEnumerable<IHttpContentFactory> httpContentFactories)
{
_httpContentFactories = httpContentFactories;
_httpContentFactories = httpContentFactories;
}

/// <inheritdoc />
public ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property, CancellationToken cancellationToken = default)
public ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property,object? context, CancellationToken cancellationToken = default)
{
var contentTypes = _httpContentFactories.SelectMany(x => x.SupportedContentTypes).Distinct().OrderBy(x => x).ToArray();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Elsa.JavaScript.Activities;

internal class RunJavaScriptOptionsProvider : IActivityPropertyOptionsProvider
{
public ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property, CancellationToken cancellationToken = default)
public ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property, object? context, CancellationToken cancellationToken = default)
{
var options = new Dictionary<string, object>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Elsa.Python.Activities;

internal class RunPythonOptionsProvider : IActivityPropertyOptionsProvider
{
public ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property, CancellationToken cancellationToken = default)
public ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property, object? context, CancellationToken cancellationToken = default)
{
var options = new Dictionary<string, object>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Elsa.Abstractions;
using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Core.Models;
using Elsa.Workflows.Core.Services;
using JetBrains.Annotations;

namespace Elsa.Workflows.Api.Endpoints.ActivityDescriptorOptions.Get;

[PublicAPI]
internal class Get : ElsaEndpoint<Request, Response>
{
private readonly IServiceProvider _serviceProvider;
private readonly IActivityRegistry _registry;
private readonly IPropertyOptionsResolver _optionsResolver;

/// <inheritdoc />
public Get(IServiceProvider serviceProvider, IActivityRegistry registry, IPropertyOptionsResolver optionsResolver)
{
_serviceProvider = serviceProvider;
_registry = registry;
_optionsResolver = optionsResolver;
}

/// <inheritdoc />
public override void Configure()
{
Get("/descriptors/activities/{activityTypeName}/options/{propertyName}");
ConfigurePermissions("read:*", "read:activity-descriptors-options");
}

/// <inheritdoc />
public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
{
var descriptor = request.Version == null ? _registry.Find(request.ActivityTypeName) : _registry.Find(request.ActivityTypeName, request.Version.Value);
if (descriptor == null)
await SendNotFoundAsync(cancellationToken);

var propertyDescriptor = descriptor!.Inputs.FirstOrDefault(x => x.Name == request.PropertyName);

if(propertyDescriptor == null)
await SendNotFoundAsync(cancellationToken);

var optionsResolver = new PropertyOptionsResolver(_serviceProvider);

var inputOptions = await optionsResolver.GetOptionsAsync(propertyDescriptor!.PropertyInfo, request.Context, cancellationToken);

await SendOkAsync(new Response(inputOptions), cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using FastEndpoints;
using JetBrains.Annotations;

namespace Elsa.Workflows.Api.Endpoints.ActivityDescriptorOptions.Get;

internal class Request
{

public string ActivityTypeName { get; set; } = default!;
public int? Version { get; set; }
public string PropertyName { get; set; } = default!;

public object? Context { get; set; }
}

[PublicAPI]
internal class Response
{
public Response(IDictionary<string,object> items)
{
Items = items;
}

public IDictionary<string, object> Items { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ public interface IActivityPropertyOptionsProvider
/// <summary>
/// Returns options for the specified property.
/// </summary>
ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property, CancellationToken cancellationToken = default);
ValueTask<IDictionary<string, object>> GetOptionsAsync(PropertyInfo property,object? context = default, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,14 @@ public interface IPropertyOptionsResolver
/// <param name="propertyInfo">The property to return options for.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Options for the specified property</returns>
ValueTask<IDictionary<string, object>?> GetOptionsAsync(PropertyInfo propertyInfo, CancellationToken cancellationToken = default);
ValueTask<IDictionary<string, object>?> GetOptionsAsync(PropertyInfo propertyInfo, CancellationToken cancellationToken = default);

/// <summary>
/// Returns options for the specified property
/// </summary>
/// <param name="propertyInfo">The property to return options for.</param>
/// <param name="context">An content object that can be used by the provider</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
ValueTask<IDictionary<string, object>?> GetOptionsAsync(PropertyInfo propertyInfo,object context, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public PropertyOptionsResolver(IServiceProvider serviceProvider)
/// <inheritdoc />
public async ValueTask<IDictionary<string, object>?> GetOptionsAsync(PropertyInfo propertyInfo, CancellationToken cancellationToken = default)
{
return await GetOptionsAsync(propertyInfo,null, cancellationToken);
}

/// <inheritdoc />
public async ValueTask<IDictionary<string, object>?> GetOptionsAsync(PropertyInfo propertyInfo, object? context, CancellationToken cancellationToken = default)
{
var inputAttribute = propertyInfo.GetCustomAttribute<InputAttribute>();

if (inputAttribute?.OptionsProvider != null)
Expand All @@ -32,7 +38,7 @@ public PropertyOptionsResolver(IServiceProvider serviceProvider)

using var scope = _serviceProvider.CreateScope();
var provider = (IActivityPropertyOptionsProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, providerType);
return await provider.GetOptionsAsync(propertyInfo, cancellationToken);
return await provider.GetOptionsAsync(propertyInfo,context, cancellationToken);
}

if (inputAttribute?.OptionsMethod is not null)
Expand All @@ -50,9 +56,9 @@ public PropertyOptionsResolver(IServiceProvider serviceProvider)
}

var defaultOptions = inputAttribute?.Options ?? (TryGetEnumOptions(propertyInfo, out var items) ? items : null);
return defaultOptions != null ? new Dictionary<string, object> { ["items"] = defaultOptions } : null;
}

return defaultOptions != null ? new Dictionary<string, object> { ["items"] = defaultOptions } : null;
}
private bool TryGetEnumOptions(PropertyInfo activityPropertyInfo, out IList<SelectListItem>? items)
{
var isNullable = activityPropertyInfo.PropertyType.IsNullableType();
Expand Down
Loading