Refactor API client to handle activities as JsonObjects
This commit is contained in:
parent
f698028488
commit
ae4dce9119
|
|
@ -1,30 +0,0 @@
|
|||
using Elsa.Api.Client.Activities;
|
||||
using Elsa.Api.Client.Contracts;
|
||||
|
||||
namespace Elsa.Api.Client.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a base class for <see cref="IActivityTypeResolver"/> implementations.
|
||||
/// </summary>
|
||||
public abstract class ActivityTypeResolverBase : IActivityTypeResolver
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public virtual double Priority => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this provider supports the specified activity type.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="ActivityTypeResolverContext"/>.</param>
|
||||
/// <returns>A value indicating whether this provider supports the specified activity type.</returns>
|
||||
protected virtual bool GetSupportsType(ActivityTypeResolverContext context) => false;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the activity type.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Type ResolveType(ActivityTypeResolverContext context) => typeof(Activity);
|
||||
|
||||
bool IActivityTypeResolver.GetSupportsType(ActivityTypeResolverContext context) => GetSupportsType(context);
|
||||
Type IActivityTypeResolver.ResolveType(ActivityTypeResolverContext context) => ResolveType(context);
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
using Elsa.Api.Client.Extensions;
|
||||
|
||||
namespace Elsa.Api.Client.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an activity.
|
||||
/// </summary>
|
||||
public class Activity : Dictionary<string, object>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of this activity.
|
||||
/// </summary>
|
||||
public string Id
|
||||
{
|
||||
get => this.TryGetValue<string>("id")!;
|
||||
set => this["id"] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of this activity.
|
||||
/// </summary>
|
||||
public string? Name
|
||||
{
|
||||
get => this.TryGetValue<string>("name")!;
|
||||
set => this["name"] = value!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of this activity.
|
||||
/// </summary>
|
||||
public string Type
|
||||
{
|
||||
get => this.TryGetValue<string>("type")!;
|
||||
set => this["type"] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version of this activity.
|
||||
/// </summary>
|
||||
public int Version
|
||||
{
|
||||
get => this.TryGetValue<int>("version");
|
||||
set => this["version"] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the metadata of this activity.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> Metadata
|
||||
{
|
||||
get => this.TryGetValue<IDictionary<string, object>>("metadata", () => new Dictionary<string, object>())!;
|
||||
set => this["metadata"] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets custom properties of this activity.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> CustomProperties
|
||||
{
|
||||
get => this.TryGetValue<IDictionary<string, object>>("customProperties", () => new Dictionary<string, object>())!;
|
||||
set => this["customProperties"] = value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
using Elsa.Api.Client.Extensions;
|
||||
using Elsa.Api.Client.Shared.Models;
|
||||
|
||||
namespace Elsa.Api.Client.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a flow switch activity.
|
||||
/// </summary>
|
||||
public class FlowSwitch : Activity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the cases.
|
||||
/// </summary>
|
||||
public ICollection<Case>? Cases
|
||||
{
|
||||
get => this.TryGetValue<ICollection<Case>>("cases");
|
||||
set => this["cases"] = value!;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
using Elsa.Api.Client.Extensions;
|
||||
using Elsa.Api.Client.Shared.Models;
|
||||
|
||||
namespace Elsa.Api.Client.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a flowchart activity.
|
||||
/// </summary>
|
||||
public class Flowchart : Container
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the connections between activities.
|
||||
/// </summary>
|
||||
public ICollection<Connection> Connections
|
||||
{
|
||||
get => this.TryGetValue<ICollection<Connection>>("connections", () => new List<Connection>())!;
|
||||
set => this["connections"] = value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
namespace Elsa.Api.Client.Activities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a trigger.
|
||||
/// </summary>
|
||||
public class Trigger : Activity
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
using Elsa.Api.Client.Abstractions;
|
||||
using Elsa.Api.Client.Activities;
|
||||
using Elsa.Api.Client.Contracts;
|
||||
|
||||
namespace Elsa.Api.Client.ActivityProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a default implementation of <see cref="IActivityTypeResolver"/> that always resolves the <see cref="Activity"/> type.
|
||||
/// </summary>
|
||||
public class DefaultActivityTypeResolver : ActivityTypeResolverBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override double Priority => -1;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool GetSupportsType(ActivityTypeResolverContext context) => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Type ResolveType(ActivityTypeResolverContext context) => typeof(Activity);
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
using Elsa.Api.Client.Abstractions;
|
||||
using Elsa.Api.Client.Activities;
|
||||
using Elsa.Api.Client.Contracts;
|
||||
|
||||
namespace Elsa.Api.Client.ActivityProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Flowchart"/> activity.
|
||||
/// </summary>
|
||||
public class FlowSwitchTypeResolver : ActivityTypeResolverBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override bool GetSupportsType(ActivityTypeResolverContext context) => context.ActivityTypeName == "Elsa.FlowSwitch";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Type ResolveType(ActivityTypeResolverContext context) => typeof(FlowSwitch);
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
using Elsa.Api.Client.Abstractions;
|
||||
using Elsa.Api.Client.Activities;
|
||||
using Elsa.Api.Client.Contracts;
|
||||
|
||||
namespace Elsa.Api.Client.ActivityProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Flowchart"/> activity.
|
||||
/// </summary>
|
||||
public class FlowchartTypeResolver : ActivityTypeResolverBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override bool GetSupportsType(ActivityTypeResolverContext context) => context.ActivityTypeName == "Elsa.Flowchart";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Type ResolveType(ActivityTypeResolverContext context) => typeof(Flowchart);
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
using Elsa.Api.Client.Activities;
|
||||
|
||||
namespace Elsa.Api.Client.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves an activity type.
|
||||
/// </summary>
|
||||
public interface IActivityTypeResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the priority of this activity provider. Activity providers with a higher priority are considered first.
|
||||
/// </summary>
|
||||
double Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this activity provider supports the specified activity type.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="ActivityTypeResolverContext"/>.</param>
|
||||
/// <returns>A value indicating whether this activity provider supports the specified activity type.</returns>
|
||||
bool GetSupportsType(ActivityTypeResolverContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of <see cref="Activity"/> for the specified activity type.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="ActivityTypeResolverContext"/>.</param>
|
||||
/// <returns>The resolved type.</returns>
|
||||
Type ResolveType(ActivityTypeResolverContext context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides context for an <see cref="IActivityTypeResolver"/>.
|
||||
/// </summary>
|
||||
/// <param name="ActivityTypeName">The activity type.</param>
|
||||
public record ActivityTypeResolverContext(string ActivityTypeName);
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
namespace Elsa.Api.Client.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the .NET type of an activity type name.
|
||||
/// </summary>
|
||||
public interface IActivityTypeService
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves the .NET type of the specified activity type name.
|
||||
/// </summary>
|
||||
/// <param name="activityTypeName">The activity type name.</param>
|
||||
/// <returns>Returns the .NET type of the specified activity type name.</returns>
|
||||
Type ResolveType(string activityTypeName);
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Api.Client.Activities;
|
||||
using Elsa.Api.Client.Contracts;
|
||||
|
||||
namespace Elsa.Api.Client.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// A JSON converter that serializes <see cref="Activity"/>.
|
||||
/// </summary>
|
||||
public class ActivityJsonConverter : JsonConverter<Activity>
|
||||
{
|
||||
private readonly IActivityTypeService _activityTypeService;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ActivityJsonConverter(IActivityTypeService activityTypeService)
|
||||
{
|
||||
_activityTypeService = activityTypeService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Activity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (!JsonDocument.TryParseValue(ref reader, out var doc))
|
||||
throw new JsonException("Failed to parse JsonDocument");
|
||||
|
||||
var activityRoot = doc.RootElement;
|
||||
var activityTypeName = activityRoot.GetProperty("type").GetString()!;
|
||||
var activityType = _activityTypeService.ResolveType(activityTypeName);
|
||||
var newOptions = new JsonSerializerOptions();
|
||||
var activity = (Activity)JsonSerializer.Deserialize(activityRoot.GetRawText(), activityType, newOptions)!;
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, Activity value, JsonSerializerOptions options)
|
||||
{
|
||||
var newOptions = new JsonSerializerOptions(options)
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
foreach (var prop in value.Where(kvp => kvp.Value != null))
|
||||
{
|
||||
writer.WritePropertyName(prop.Key);
|
||||
JsonSerializer.Serialize(writer, prop.Value, prop.Value.GetType(), newOptions);
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Api.Client.Activities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Api.Client.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Creates instances of <see cref="ActivityJsonConverter"/>.
|
||||
/// </summary>
|
||||
public class ActivityJsonConverterFactory : JsonConverterFactory
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ActivityJsonConverterFactory(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
// Notice that this factory only creates converters when the type to convert is IActivity.
|
||||
// The ActivityJsonConverter will create concrete activity objects, which then uses regular serialization
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Activity);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => ActivatorUtilities.CreateInstance<ActivityJsonConverter>(_serviceProvider);
|
||||
}
|
||||
|
|
@ -1,108 +1,114 @@
|
|||
using Elsa.Api.Client.Activities;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json.Nodes;
|
||||
using Elsa.Api.Client.Shared.Models;
|
||||
|
||||
namespace Elsa.Api.Client.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="Activity"/>.
|
||||
/// Provides extension methods for <see cref="JsonObject"/>.
|
||||
/// </summary>
|
||||
public static class ActivityExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the designer metadata for the specified activity.
|
||||
/// Gets the type name of the specified activity.
|
||||
/// </summary>
|
||||
public static void SetDesignerMetadata(this Activity activity, ActivityDesignerMetadata designerMetadata)
|
||||
{
|
||||
var metadata = activity.Metadata;
|
||||
metadata["designer"] = designerMetadata;
|
||||
activity.Metadata = metadata;
|
||||
}
|
||||
public static string GetTypeName(this JsonObject activity) => activity.GetProperty<string>("type")!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of the specified activity.
|
||||
/// </summary>
|
||||
public static string GetId(this JsonObject activity) => activity.GetProperty<string>("id")!;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ID of the specified activity.
|
||||
/// </summary>
|
||||
public static void SetId(this JsonObject activity, string value) => activity.SetProperty(JsonValue.Create(value), "id");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the specified activity.
|
||||
/// </summary>
|
||||
public static string? GetName(this JsonObject activity) => activity.GetProperty<string>("name");
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name of the specified activity.
|
||||
/// </summary>
|
||||
public static void SetName(this JsonObject activity, string? value) => activity.SetProperty(JsonValue.Create(value), "name");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the designer metadata for the specified activity.
|
||||
/// </summary>
|
||||
public static ActivityDesignerMetadata GetDesignerMetadata(this Activity activity)
|
||||
{
|
||||
var metadata = activity.Metadata;
|
||||
var designerMetadata = metadata.TryGetValue("designer", () => new ActivityDesignerMetadata())!;
|
||||
metadata["designer"] = designerMetadata;
|
||||
activity.Metadata = metadata;
|
||||
return designerMetadata;
|
||||
}
|
||||
|
||||
public static JsonObject? GetMetadata(this JsonObject activity) => activity.GetProperty("metadata")?.AsObject();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the designer metadata for the specified activity.
|
||||
/// </summary>
|
||||
public static void SetDesignerMetadata(this JsonObject activity, ActivityDesignerMetadata designerMetadata) => activity.SetProperty(designerMetadata.SerializeToNode(), "metadata", "designer");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the designer metadata for the specified activity.
|
||||
/// </summary>
|
||||
public static ActivityDesignerMetadata GetDesignerMetadata(this JsonObject activity) => activity.GetProperty<ActivityDesignerMetadata>("metadata", "designer") ?? new ActivityDesignerMetadata();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display text for the specified activity.
|
||||
/// </summary>
|
||||
public static string? GetDisplayText(this Activity activity)
|
||||
{
|
||||
var metadata = activity.Metadata;
|
||||
return metadata.TryGetValue<string>("displayText");
|
||||
}
|
||||
|
||||
public static string? GetDisplayText(this JsonObject activity) => activity.GetProperty("metadata", "displayText")?.GetValue<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the display text for the specified activity.
|
||||
/// </summary>
|
||||
public static void SetDisplayText(this Activity activity, string value)
|
||||
public static void SetDisplayText(this JsonObject activity, string? value)
|
||||
{
|
||||
var metadata = activity.Metadata;
|
||||
metadata["displayText"] = value;
|
||||
activity.Metadata = metadata;
|
||||
activity.SetProperty(JsonValue.Create(value), "metadata", "displayText");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description for the specified activity.
|
||||
/// </summary>
|
||||
public static string? GetDescription(this Activity activity)
|
||||
{
|
||||
var metadata = activity.Metadata;
|
||||
return metadata.TryGetValue<string>("description");
|
||||
}
|
||||
|
||||
public static string? GetDescription(this JsonObject activity) => activity.GetProperty("metadata", "description")?.GetValue<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the description for the specified activity.
|
||||
/// </summary>
|
||||
public static void SetDescription(this Activity activity, string value)
|
||||
{
|
||||
var metadata = activity.Metadata;
|
||||
metadata["description"] = value;
|
||||
activity.Metadata = metadata;
|
||||
}
|
||||
|
||||
public static void SetDescription(this JsonObject activity, string value) => activity.SetProperty(JsonValue.Create(value), "metadata", "description");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the description for the specified activity should be shown.
|
||||
/// </summary>
|
||||
public static bool? GetShowDescription(this Activity activity)
|
||||
{
|
||||
var metadata = activity.Metadata;
|
||||
return metadata.TryGetValue<bool>("showDescription");
|
||||
}
|
||||
|
||||
public static bool? GetShowDescription(this JsonObject activity) => activity.GetProperty<bool>("metadata", "showDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value indicating whether the description for the specified activity should be shown.
|
||||
/// </summary>
|
||||
public static void SetShowDescription(this Activity activity, bool value)
|
||||
{
|
||||
var metadata = activity.Metadata;
|
||||
metadata["showDescription"] = value;
|
||||
activity.Metadata = metadata;
|
||||
}
|
||||
|
||||
public static void SetShowDescription(this JsonObject activity, bool value) => activity.SetProperty(JsonValue.Create(value), "metadata", "showDescription");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the specified activity can trigger the workflow.
|
||||
/// </summary>
|
||||
public static bool? GetCanStartWorkflow(this Activity activity)
|
||||
{
|
||||
var properties = activity.CustomProperties;
|
||||
return properties.TryGetValue<bool>("canStartWorkflow");
|
||||
}
|
||||
|
||||
public static bool? GetCanStartWorkflow(this JsonObject activity) => activity.GetProperty<bool>("customProperties", "canStartWorkflow");
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value indicating whether the specified activity can trigger the workflow.
|
||||
/// </summary>
|
||||
public static void SetCanStartWorkflow(this Activity activity, bool value)
|
||||
{
|
||||
var properties = activity.CustomProperties;
|
||||
properties["canStartWorkflow"] = value;
|
||||
activity.CustomProperties = properties;
|
||||
}
|
||||
public static void SetCanStartWorkflow(this JsonObject activity, bool value) => activity.SetProperty(JsonValue.Create(value), "customProperties", "canStartWorkflow");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the activities in the specified flowchart.
|
||||
/// </summary>
|
||||
public static IEnumerable<JsonObject> GetActivities(this JsonObject flowchart) => flowchart.GetProperty("activities")?.AsArray().AsEnumerable().Cast<JsonObject>() ?? Array.Empty<JsonObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the activities in the specified flowchart.
|
||||
/// </summary>
|
||||
public static void SetActivities(this JsonObject flowchart, JsonArray activities) => flowchart.SetProperty(activities, "activities");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connections in the specified flowchart.
|
||||
/// </summary>
|
||||
public static IEnumerable<Connection> GetConnections(this JsonObject flowchart) => flowchart.GetProperty<ICollection<Connection>>("connections") ?? new List<Connection>();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the connections in the specified flowchart.
|
||||
/// </summary>
|
||||
public static void SetConnections(this JsonObject flowchart, IEnumerable<Connection> connections) => flowchart.SetProperty(JsonValue.Create(connections), "connections");
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Api.Client.ActivityProviders;
|
||||
using Elsa.Api.Client.Contracts;
|
||||
using Elsa.Api.Client.Converters;
|
||||
using Elsa.Api.Client.Options;
|
||||
|
|
@ -49,10 +48,6 @@ public static class DependencyInjectionExtensions
|
|||
/// </summary>
|
||||
public static IServiceCollection AddActivityTypeService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IActivityTypeService, DefaultActivityTypeService>();
|
||||
services.AddActivityTypeResolver<DefaultActivityTypeResolver>();
|
||||
services.AddActivityTypeResolver<FlowchartTypeResolver>();
|
||||
services.AddActivityTypeResolver<FlowSwitchTypeResolver>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
@ -67,14 +62,6 @@ public static class DependencyInjectionExtensions
|
|||
services.AddRefitClient<T>(settings)
|
||||
.ConfigureHttpClient(ConfigureElsaApiHttpClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an activity type resolver to the service collection.
|
||||
/// </summary>
|
||||
public static IServiceCollection AddActivityTypeResolver<T>(this IServiceCollection services) where T : class, IActivityTypeResolver
|
||||
{
|
||||
return services.AddSingleton<IActivityTypeResolver, T>();
|
||||
}
|
||||
|
||||
private static void ConfigureElsaApiHttpClient(IServiceProvider serviceProvider, HttpClient httpClient)
|
||||
{
|
||||
|
|
@ -91,7 +78,6 @@ public static class DependencyInjectionExtensions
|
|||
|
||||
serializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
serializerOptions.Converters.Add(new VersionOptionsJsonConverter());
|
||||
serializerOptions.Converters.Add(new ActivityJsonConverterFactory(serviceProvider));
|
||||
serializerOptions.Converters.Add(new ExpressionJsonConverterFactory());
|
||||
|
||||
var settings = new RefitSettings
|
||||
|
|
|
|||
186
src/clients/Elsa.Api.Client/Extensions/JsonObjectExtensions.cs
Normal file
186
src/clients/Elsa.Api.Client/Extensions/JsonObjectExtensions.cs
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Elsa.Api.Client.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="JsonObject"/>.
|
||||
/// </summary>
|
||||
public static class JsonObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes the specified value to a <see cref="JsonObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to serialize.</param>
|
||||
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
|
||||
/// <returns>A <see cref="JsonObject"/> representing the specified value.</returns>
|
||||
public static JsonNode SerializeToNode(this object value, JsonSerializerOptions? options = default)
|
||||
{
|
||||
options ??= new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
return JsonSerializer.SerializeToNode(value, options)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified value to a <see cref="JsonArray"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to serialize.</param>
|
||||
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
|
||||
/// <returns>A <see cref="JsonObject"/> representing the specified value.</returns>
|
||||
public static JsonArray SerializeToArray(this object value, JsonSerializerOptions? options = default)
|
||||
{
|
||||
options ??= new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
return JsonSerializer.SerializeToNode(value, options)!.AsArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified value to a <see cref="JsonArray"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to serialize.</param>
|
||||
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
|
||||
/// <returns>A <see cref="JsonObject"/> representing the specified value.</returns>
|
||||
public static JsonArray SerializeToArray<T>(this IEnumerable<T> value, JsonSerializerOptions? options = default)
|
||||
{
|
||||
options ??= new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
return JsonSerializer.SerializeToNode(value, options)!.AsArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the specified <see cref="JsonNode"/> to the specified type.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="JsonNode"/> to deserialize.</param>
|
||||
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
|
||||
/// <typeparam name="T">The type to deserialize to.</typeparam>
|
||||
/// <returns>The deserialized value.</returns>
|
||||
public static T Deserialize<T>(this JsonNode value, JsonSerializerOptions? options = default)
|
||||
{
|
||||
options ??= new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
if (value is JsonObject jsonObject)
|
||||
return JsonSerializer.Deserialize<T>(jsonObject, options)!;
|
||||
|
||||
if (value is JsonArray jsonArray)
|
||||
return JsonSerializer.Deserialize<T>(jsonArray, options)!;
|
||||
|
||||
if (value is JsonValue jsonValue)
|
||||
return jsonValue.GetValue<T>();
|
||||
|
||||
throw new NotSupportedException($"Cannot deserialize {value.GetType()} to {typeof(T)}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value of the specified model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to set the property value on.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <param name="path">The path to the property.</param>
|
||||
public static void SetProperty(this JsonObject model, JsonNode? value, params string[] path)
|
||||
{
|
||||
model = GetPropertyContainer(model, path);
|
||||
model[path.Last()] = value?.SerializeToNode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value of the specified model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to set the property value on.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <param name="path">The path to the property.</param>
|
||||
public static void SetProperty(this JsonObject model, JsonArray? value, params string[] path)
|
||||
{
|
||||
model = GetPropertyContainer(model, path);
|
||||
model[path.Last()] = value?.SerializeToNode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value of the specified model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to set the property value on.</param>
|
||||
/// <param name="value">The value to set.</param>
|
||||
/// <param name="path">The path to the property.</param>
|
||||
public static void SetProperty(this JsonObject model, IEnumerable<JsonNode> value, params string[] path)
|
||||
{
|
||||
model = GetPropertyContainer(model, path);
|
||||
model[path.Last()] = new JsonArray(value.Select(x => x.SerializeToNode()).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value of the specified model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to get the property value from.</param>
|
||||
/// <param name="path">The path to the property.</param>
|
||||
/// <returns>The property value.</returns>
|
||||
public static JsonNode? GetProperty(this JsonObject model, params string[] path)
|
||||
{
|
||||
var currentModel = model;
|
||||
|
||||
foreach (var prop in path.SkipLast(1))
|
||||
{
|
||||
if (currentModel[prop] is not JsonObject value)
|
||||
return default;
|
||||
|
||||
currentModel = value;
|
||||
}
|
||||
|
||||
return currentModel[path.Last()];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value of the specified model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to get the property value from.</param>
|
||||
/// <param name="path">The path to the property.</param>
|
||||
/// <typeparam name="T">The type to deserialize to.</typeparam>
|
||||
/// <returns>The property value.</returns>
|
||||
public static T? GetProperty<T>(this JsonObject model, params string[] path)
|
||||
{
|
||||
var property = GetProperty(model, path);
|
||||
return property != null ? property.Deserialize<T>() : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value of the specified model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to get the property value from.</param>
|
||||
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use when deserializing.</param>
|
||||
/// <param name="path">The path to the property.</param>
|
||||
/// <typeparam name="T">The type to deserialize to.</typeparam>
|
||||
/// <returns>The property value.</returns>
|
||||
public static T? GetProperty<T>(this JsonObject model, JsonSerializerOptions options, params string[] path)
|
||||
{
|
||||
var property = GetProperty(model, path);
|
||||
return property != null ? property.Deserialize<T>(options) : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property container of the specified model.
|
||||
/// </summary>
|
||||
/// <param name="model">The model to set the property value on.</param>
|
||||
/// <param name="path">The path to the property.</param>
|
||||
private static JsonObject GetPropertyContainer(this JsonObject model, params string[] path)
|
||||
{
|
||||
foreach (var prop in path.SkipLast(1))
|
||||
{
|
||||
var property = model[prop] as JsonObject ?? new JsonObject();
|
||||
model[prop] = property;
|
||||
model = property;
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using System.ComponentModel;
|
|||
using System.Dynamic;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Elsa.Api.Client.Extensions;
|
||||
|
|
@ -52,17 +53,20 @@ public static class ObjectConverter
|
|||
var underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType;
|
||||
var underlyingSourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
|
||||
|
||||
if (value is JsonElement jsonNumber && jsonNumber.ValueKind == JsonValueKind.Number && underlyingTargetType == typeof(string))
|
||||
if (value is JsonElement { ValueKind: JsonValueKind.Number } jsonNumber && underlyingTargetType == typeof(string))
|
||||
return jsonNumber.ToString().ConvertTo(underlyingTargetType);
|
||||
|
||||
if (value is JsonElement jsonObject)
|
||||
if (value is JsonElement jsonElement)
|
||||
{
|
||||
if (jsonObject.ValueKind == JsonValueKind.String && underlyingTargetType != typeof(string))
|
||||
return jsonObject.GetString().ConvertTo(underlyingTargetType);
|
||||
if (jsonElement.ValueKind == JsonValueKind.String && underlyingTargetType != typeof(string))
|
||||
return jsonElement.GetString().ConvertTo(underlyingTargetType);
|
||||
|
||||
return jsonObject.Deserialize(targetType, options);
|
||||
return jsonElement.Deserialize(targetType, options);
|
||||
}
|
||||
|
||||
if (value is JsonNode jsonNode)
|
||||
return jsonNode.Deserialize(targetType, options);
|
||||
|
||||
if (underlyingSourceType == typeof(string) && !underlyingTargetType.IsPrimitive && underlyingTargetType != typeof(object))
|
||||
{
|
||||
var stringValue = (string)value;
|
||||
|
|
@ -116,7 +120,7 @@ public static class ObjectConverter
|
|||
|
||||
if (targetTypeConverter.CanConvertFrom(underlyingSourceType))
|
||||
return targetTypeConverter.IsValid(value)
|
||||
? targetTypeConverter.ConvertFrom(null, CultureInfo.InvariantCulture, value)
|
||||
? targetTypeConverter.ConvertFrom(null!, CultureInfo.InvariantCulture, value)
|
||||
: targetType.GetDefaultValue();
|
||||
|
||||
var sourceTypeConverter = TypeDescriptor.GetConverter(underlyingSourceType);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
namespace Elsa.Api.Client.Resources.StorageDrivers.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a storage driver descriptor.
|
||||
/// </summary>
|
||||
/// <param name="TypeName">The type name of the storage driver.</param>
|
||||
/// <param name="DisplayName">The display name of the storage driver.</param>
|
||||
public record StorageDriverDescriptor(string TypeName, string DisplayName);
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
using Elsa.Api.Client.Activities;
|
||||
//using Elsa.Api.Client.Activities;
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Elsa.Api.Client.Shared.Models;
|
||||
|
||||
namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
|
||||
|
|
@ -76,7 +79,7 @@ public class WorkflowDefinition : VersionedEntity
|
|||
/// <summary>
|
||||
/// The root activity of the workflow.
|
||||
/// </summary>
|
||||
public Activity Root { get; set; } = default!;
|
||||
public JsonObject Root { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// An option to use the workflow as a readonly workflow.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using Elsa.Api.Client.Activities;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
//using Elsa.Api.Client.Activities;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
|
||||
|
|
@ -9,25 +11,88 @@ namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
|
|||
[PublicAPI]
|
||||
public class WorkflowDefinitionModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the version ID of the workflow definition.
|
||||
/// </summary>
|
||||
public string Id { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the definition ID of the workflow definition.
|
||||
/// </summary>
|
||||
public string DefinitionId { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the workflow definition.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description of the workflow definition.
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time at which the workflow definition was created.
|
||||
/// </summary>
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version of the workflow definition.
|
||||
/// </summary>
|
||||
public int Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version of the tool that created the workflow definition.
|
||||
/// </summary>
|
||||
public Version? ToolVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the variables of the workflow definition.
|
||||
/// </summary>
|
||||
public ICollection<VariableDefinition>? Variables { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the inputs of the workflow definition.
|
||||
/// </summary>
|
||||
public ICollection<InputDefinition>? Inputs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the outputs of the workflow definition.
|
||||
/// </summary>
|
||||
public ICollection<OutputDefinition>? Outputs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the outcomes of the workflow definition.
|
||||
/// </summary>
|
||||
public ICollection<string>? Outcomes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom properties associated with the workflow definition.
|
||||
/// </summary>
|
||||
public IDictionary<string, object>? CustomProperties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the workflow definition is read-only.
|
||||
/// </summary>
|
||||
public bool IsReadonly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this is the latest version of the workflow definition.
|
||||
/// </summary>
|
||||
public bool IsLatest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this is the published version of the workflow definition.
|
||||
/// </summary>
|
||||
public bool IsPublished { get; set; }
|
||||
|
||||
/// <summary>The type of <c>IWorkflowActivationStrategy</c> to apply when new instances are requested to be created.</summary>
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="WorkflowOptions"/> of the workflow definition.
|
||||
/// </summary>
|
||||
public WorkflowOptions? Options { get; set; }
|
||||
|
||||
/// <summary></summary>
|
||||
public Activity? Root { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the root activity of the workflow definition.
|
||||
/// </summary>
|
||||
public JsonObject? Root { get; set; }
|
||||
}
|
||||
|
|
@ -1,35 +1,35 @@
|
|||
using Elsa.Api.Client.Activities;
|
||||
using Elsa.Api.Client.Contracts;
|
||||
|
||||
namespace Elsa.Api.Client.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a default implementation of <see cref="IActivityTypeService"/> that creates a <see cref="Activity"/>.
|
||||
/// </summary>
|
||||
public class DefaultActivityTypeService : IActivityTypeService
|
||||
{
|
||||
private readonly IEnumerable<IActivityTypeResolver> _activityProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DefaultActivityTypeService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="activityProviders">The <see cref="IActivityTypeResolver"/>s.</param>
|
||||
public DefaultActivityTypeService(IEnumerable<IActivityTypeResolver> activityProviders)
|
||||
{
|
||||
_activityProviders = activityProviders.OrderByDescending(x => x.Priority);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type ResolveType(string activityType)
|
||||
{
|
||||
var context = new ActivityTypeResolverContext(activityType);
|
||||
var activityProvider = GetActivityProvider(context);
|
||||
|
||||
if (activityProvider == null)
|
||||
throw new Exception($"No activity provider found for activity type '{activityType}'.");
|
||||
|
||||
return activityProvider.ResolveType(context);
|
||||
}
|
||||
|
||||
private IActivityTypeResolver? GetActivityProvider(ActivityTypeResolverContext context) => _activityProviders.FirstOrDefault(provider => provider.GetSupportsType(context));
|
||||
}
|
||||
// using Elsa.Api.Client.Activities;
|
||||
// using Elsa.Api.Client.Contracts;
|
||||
//
|
||||
// namespace Elsa.Api.Client.Services;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Provides a default implementation of <see cref="IActivityTypeService"/> that creates a <see cref="Activity"/>.
|
||||
// /// </summary>
|
||||
// public class DefaultActivityTypeService : IActivityTypeService
|
||||
// {
|
||||
// private readonly IEnumerable<IActivityTypeResolver> _activityProviders;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Initializes a new instance of the <see cref="DefaultActivityTypeService"/> class.
|
||||
// /// </summary>
|
||||
// /// <param name="activityProviders">The <see cref="IActivityTypeResolver"/>s.</param>
|
||||
// public DefaultActivityTypeService(IEnumerable<IActivityTypeResolver> activityProviders)
|
||||
// {
|
||||
// _activityProviders = activityProviders.OrderByDescending(x => x.Priority);
|
||||
// }
|
||||
//
|
||||
// /// <inheritdoc />
|
||||
// public Type ResolveType(string activityType)
|
||||
// {
|
||||
// var context = new ActivityTypeResolverContext(activityType);
|
||||
// var activityProvider = GetActivityProvider(context);
|
||||
//
|
||||
// if (activityProvider == null)
|
||||
// throw new Exception($"No activity provider found for activity type '{activityType}'.");
|
||||
//
|
||||
// return activityProvider.ResolveType(context);
|
||||
// }
|
||||
//
|
||||
// private IActivityTypeResolver? GetActivityProvider(ActivityTypeResolverContext context) => _activityProviders.FirstOrDefault(provider => provider.GetSupportsType(context));
|
||||
// }
|
||||
|
|
@ -41,10 +41,10 @@ public class ActivityDesignerMetadata
|
|||
/// <summary>
|
||||
/// Gets or sets the position of the activity.
|
||||
/// </summary>
|
||||
public Position Position { get; set; } = default!;
|
||||
public Position Position { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the activity.
|
||||
/// </summary>
|
||||
public Size Size { get; set; } = default!;
|
||||
public Size Size { get; set; } = new();
|
||||
}
|
||||
|
|
@ -1,29 +1,29 @@
|
|||
using Elsa.Api.Client.Activities;
|
||||
using Elsa.Api.Client.Extensions;
|
||||
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
|
||||
|
||||
namespace Elsa.Api.Client.Shared.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a container activity.
|
||||
/// </summary>
|
||||
public class Container : Activity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the activities contained in this container.
|
||||
/// </summary>
|
||||
public ICollection<Activity> Activities
|
||||
{
|
||||
get => this.TryGetValue<ICollection<Activity>>("activities", () => new List<Activity>())!;
|
||||
set => this["activities"] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the variables in this container.
|
||||
/// </summary>
|
||||
public ICollection<Variable> Variables
|
||||
{
|
||||
get => this.TryGetValue<ICollection<Variable>>("variables", () => new List<Variable>())!;
|
||||
set => this["variables"] = value;
|
||||
}
|
||||
}
|
||||
// using Elsa.Api.Client.Activities;
|
||||
// using Elsa.Api.Client.Extensions;
|
||||
// using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
|
||||
//
|
||||
// namespace Elsa.Api.Client.Shared.Models;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Represents a container activity.
|
||||
// /// </summary>
|
||||
// public class Container : Activity
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// Gets or sets the activities contained in this container.
|
||||
// /// </summary>
|
||||
// public ICollection<Activity> Activities
|
||||
// {
|
||||
// get => this.TryGetValue<ICollection<Activity>>("activities", () => new List<Activity>())!;
|
||||
// set => this["activities"] = value;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// Gets or sets the variables in this container.
|
||||
// /// </summary>
|
||||
// public ICollection<Variable> Variables
|
||||
// {
|
||||
// get => this.TryGetValue<ICollection<Variable>>("variables", () => new List<Variable>())!;
|
||||
// set => this["variables"] = value;
|
||||
// }
|
||||
// }
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using Elsa.Api.Client.Activities;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Elsa.Api.Client.Shared.Models;
|
||||
|
||||
|
|
@ -15,5 +15,5 @@ public class HttpStatusCodeCase
|
|||
/// <summary>
|
||||
/// The activity to execute when the HTTP status code matches.
|
||||
/// </summary>
|
||||
public Activity? Activity { get; set; }
|
||||
public JsonObject? Activity { get; set; }
|
||||
}
|
||||
Loading…
Reference in a new issue