Implement runtime list items provider
This commit is contained in:
parent
1277003ca5
commit
2f6ce896c6
|
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Design
|
||||
{
|
||||
public interface IRuntimeSelectListItemsProvider
|
||||
{
|
||||
ValueTask<IEnumerable<SelectListItem>> GetItemsAsync(object? context = default, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using Elsa.Serialization.Converters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Elsa.Design
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents settings to be used by the designer to invoke an Elsa's API endpoint to retrieve list options at runtime.
|
||||
/// </summary>
|
||||
public class RuntimeSelectListItemsProviderSettings
|
||||
{
|
||||
public RuntimeSelectListItemsProviderSettings(Type providerType, object? context = default)
|
||||
{
|
||||
RuntimeSelectListItemsProviderType = providerType;
|
||||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of the list items provider.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(FullTypeJsonConverter))]
|
||||
public Type RuntimeSelectListItemsProviderType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optionally provide an object containing useful information for the list select items provider to determine what items to provide.
|
||||
/// </summary>
|
||||
public object? Context { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Elsa.Serialization.Converters
|
||||
{
|
||||
public class FullTypeJsonConverter : JsonConverter<Type>
|
||||
{
|
||||
public override bool CanRead => true;
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override void WriteJson(JsonWriter writer, Type? value, JsonSerializer serializer)
|
||||
{
|
||||
var typeName = value!.AssemblyQualifiedName;
|
||||
serializer.Serialize(writer, typeName);
|
||||
}
|
||||
|
||||
public override Type ReadJson(JsonReader reader, Type objectType, Type? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
var typeName = serializer.Deserialize<string>(reader)!;
|
||||
return typeName is not null and not "" ? Type.GetType(typeName)! : default!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ using Elsa.Builders;
|
|||
using Elsa.Consumers;
|
||||
using Elsa.Converters;
|
||||
using Elsa.Decorators;
|
||||
using Elsa.Design;
|
||||
using Elsa.Dispatch;
|
||||
using Elsa.Dispatch.Consumers;
|
||||
using Elsa.Events;
|
||||
|
|
@ -102,6 +103,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
public static IServiceCollection AddActivityPropertyOptionsProvider<T>(this IServiceCollection services) where T : class, IActivityPropertyOptionsProvider => services.AddSingleton<IActivityPropertyOptionsProvider, T>();
|
||||
public static IServiceCollection AddRuntimeSelectItemsProvider<T>(this IServiceCollection services) where T : class, IRuntimeSelectListItemsProvider => services.AddScoped<IRuntimeSelectListItemsProvider, T>();
|
||||
public static IServiceCollection AddActivityTypeProvider<T>(this IServiceCollection services) where T : class, IActivityTypeProvider => services.AddSingleton<IActivityTypeProvider, T>();
|
||||
|
||||
private static ElsaOptionsBuilder AddWorkflowsCore(this ElsaOptionsBuilder options)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export namespace Components {
|
|||
interface ElsaDropdownProperty {
|
||||
"propertyDescriptor": ActivityPropertyDescriptor;
|
||||
"propertyModel": ActivityDefinitionProperty;
|
||||
"serverUrl": string;
|
||||
}
|
||||
interface ElsaExpressionEditor {
|
||||
"context"?: string;
|
||||
|
|
@ -543,6 +544,7 @@ declare namespace LocalJSX {
|
|||
interface ElsaDropdownProperty {
|
||||
"propertyDescriptor"?: ActivityPropertyDescriptor;
|
||||
"propertyModel"?: ActivityDefinitionProperty;
|
||||
"serverUrl"?: string;
|
||||
}
|
||||
interface ElsaExpressionEditor {
|
||||
"context"?: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import {Component, h, Prop, State} from '@stencil/core';
|
||||
import {ActivityDefinitionProperty, ActivityPropertyDescriptor, SyntaxNames} from "../../../../models";
|
||||
import {ActivityDefinitionProperty, ActivityPropertyDescriptor, RuntimeSelectListItemsProviderSettings, SelectListItem, SyntaxNames} from "../../../../models";
|
||||
import Tunnel from "../../../../data/workflow-editor";
|
||||
import {createElsaClient} from "../../../../services/elsa-client";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-dropdown-property',
|
||||
|
|
@ -10,7 +12,10 @@ export class ElsaDropdownProperty {
|
|||
|
||||
@Prop() propertyDescriptor: ActivityPropertyDescriptor;
|
||||
@Prop() propertyModel: ActivityDefinitionProperty;
|
||||
@Prop({mutable: true}) serverUrl: string;
|
||||
@State() currentValue?: string;
|
||||
|
||||
items: any[];
|
||||
|
||||
async componentWillLoad() {
|
||||
const defaultSyntax = this.propertyDescriptor.defaultSyntax || SyntaxNames.Literal;
|
||||
|
|
@ -26,6 +31,25 @@ export class ElsaDropdownProperty {
|
|||
onDefaultSyntaxValueChanged(e: CustomEvent) {
|
||||
this.currentValue = e.detail;
|
||||
}
|
||||
|
||||
async componentWillRender(){
|
||||
const propertyDescriptor = this.propertyDescriptor;
|
||||
const options = propertyDescriptor.options;
|
||||
let items = [];
|
||||
|
||||
if (!!options.runtimeSelectListItemsProviderType) {
|
||||
items = await this.fetchRuntimeItems(options);
|
||||
} else {
|
||||
items = options as Array<any> || [];
|
||||
}
|
||||
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
async fetchRuntimeItems(options: RuntimeSelectListItemsProviderSettings): Promise<Array<SelectListItem>>{
|
||||
const elsaClient = createElsaClient(this.serverUrl);
|
||||
return await elsaClient.designerApi.runtimeSelectItemsApi.get(options.runtimeSelectListItemsProviderType, options.context || {});
|
||||
}
|
||||
|
||||
render() {
|
||||
const propertyDescriptor = this.propertyDescriptor;
|
||||
|
|
@ -33,8 +57,8 @@ export class ElsaDropdownProperty {
|
|||
const propertyName = propertyDescriptor.name;
|
||||
const fieldId = propertyName;
|
||||
const fieldName = propertyName;
|
||||
const options = propertyDescriptor.options as Array<any> || [];
|
||||
const currentValue = this.currentValue;
|
||||
const items = this.items;
|
||||
|
||||
return (
|
||||
<elsa-property-editor propertyDescriptor={propertyDescriptor}
|
||||
|
|
@ -43,10 +67,10 @@ export class ElsaDropdownProperty {
|
|||
editor-height="2.75em"
|
||||
single-line={true}>
|
||||
<select id={fieldId} name={fieldName} onChange={e => this.onChange(e)} class="mt-1 block focus:ring-blue-500 focus:border-blue-500 w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-md">
|
||||
{options.map(option => {
|
||||
const optionIsObject = typeof (option) == 'object';
|
||||
const value = optionIsObject ? option.value : option.toString();
|
||||
const text = optionIsObject ? option.text : option.toString();
|
||||
{items.map(item => {
|
||||
const optionIsObject = typeof (item) == 'object';
|
||||
const value = optionIsObject ? item.value : item.toString();
|
||||
const text = optionIsObject ? item.text : item.toString();
|
||||
return <option value={value} selected={value === currentValue}>{text}</option>;
|
||||
})}
|
||||
</select>
|
||||
|
|
@ -54,3 +78,5 @@ export class ElsaDropdownProperty {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
Tunnel.injectProps(ElsaDropdownProperty, ['serverUrl']);
|
||||
|
|
@ -1,17 +1,13 @@
|
|||
import {Component, h, Host, Method, Prop, State, Watch} from '@stencil/core';
|
||||
import {eventBus} from '../../../../services/event-bus';
|
||||
import * as collection from 'lodash/collection';
|
||||
import {
|
||||
ActivityBlueprint, ActivityDefinitionProperty,
|
||||
ActivityDescriptor,
|
||||
ActivityModel, Connection,
|
||||
ConnectionModel,
|
||||
EventTypes, getVersionOptionsString, SyntaxNames,
|
||||
WorkflowBlueprint, WorkflowExecutionLogRecord,
|
||||
WorkflowInstance,
|
||||
WorkflowModel,
|
||||
WorkflowPersistenceBehavior,
|
||||
WorkflowStatus
|
||||
SyntaxNames,
|
||||
WorkflowBlueprint, WorkflowModel,
|
||||
WorkflowPersistenceBehavior
|
||||
} from "../../../../models";
|
||||
import {createElsaClient} from "../../../../services/elsa-client";
|
||||
import {pluginManager} from '../../../../services/plugin-manager';
|
||||
|
|
|
|||
|
|
@ -291,6 +291,16 @@ export enum ActivityTraits {
|
|||
Job = 4
|
||||
}
|
||||
|
||||
export interface SelectListItem {
|
||||
text: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface RuntimeSelectListItemsProviderSettings {
|
||||
runtimeSelectListItemsProviderType: string;
|
||||
context?: any;
|
||||
}
|
||||
|
||||
export class SyntaxNames {
|
||||
static readonly Literal = 'Literal';
|
||||
static readonly JavaScript = 'JavaScript';
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
ActivityDescriptor,
|
||||
ConnectionDefinition,
|
||||
getVersionOptionsString, OrderBy,
|
||||
PagedList,
|
||||
PagedList, SelectListItem,
|
||||
Variables,
|
||||
VersionOptions, WorkflowBlueprint, WorkflowBlueprintSummary,
|
||||
WorkflowContextOptions,
|
||||
|
|
@ -152,6 +152,14 @@ export const createElsaClient = function (serverUrl: string): ElsaClient {
|
|||
const response = await httpClient.get<string>(`v1/scripting/javascript/type-definitions/${workflowDefinitionId}?t=${new Date().getTime()}&context=${context}`);
|
||||
return response.data;
|
||||
}
|
||||
},
|
||||
designerApi:{
|
||||
runtimeSelectItemsApi:{
|
||||
get: async (providerTypeName: string, context?: any): Promise<Array<SelectListItem>> => {
|
||||
const response = await httpClient.post(`v1/designer/runtime-select-list-items/${providerTypeName}`, context);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,6 +171,7 @@ export interface ElsaClient {
|
|||
workflowInstancesApi: WorkflowInstancesApi;
|
||||
workflowExecutionLogApi: WorkflowExecutionLogApi;
|
||||
scriptingApi: ScriptingApi;
|
||||
designerApi: DesignerApi;
|
||||
}
|
||||
|
||||
export interface ActivitiesApi {
|
||||
|
|
@ -220,6 +229,14 @@ export interface ScriptingApi {
|
|||
getJavaScriptTypeDefinitions(workflowDefinitionId: string, context?: string): Promise<string>
|
||||
}
|
||||
|
||||
export interface DesignerApi {
|
||||
runtimeSelectItemsApi: RuntimeSelectItemsApi;
|
||||
}
|
||||
|
||||
export interface RuntimeSelectItemsApi {
|
||||
get(providerTypeName: string, context?: any): Promise<Array<SelectListItem>>
|
||||
}
|
||||
|
||||
export interface SaveWorkflowDefinitionRequest {
|
||||
workflowDefinitionId?: string;
|
||||
name?: string;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Design;
|
||||
using Elsa.Expressions;
|
||||
using Elsa.Metadata;
|
||||
using Elsa.Services;
|
||||
|
||||
namespace Elsa.Samples.Server.Host.Activities
|
||||
{
|
||||
[Action]
|
||||
public class VehicleActivity : Activity, IActivityPropertyOptionsProvider, IRuntimeSelectListItemsProvider
|
||||
{
|
||||
[ActivityProperty(
|
||||
UIHint = ActivityPropertyUIHints.Dropdown,
|
||||
OptionsProvider = typeof(VehicleActivity),
|
||||
DefaultSyntax = SyntaxNames.Literal,
|
||||
SupportedSyntaxes = new[] { SyntaxNames.Literal, SyntaxNames.Json, SyntaxNames.JavaScript, SyntaxNames.Liquid }
|
||||
)]
|
||||
public string? Brand { get; set; }
|
||||
|
||||
public object GetOptions(PropertyInfo property) => new RuntimeSelectListItemsProviderSettings(GetType());
|
||||
|
||||
public ValueTask<IEnumerable<SelectListItem>> GetItemsAsync(object? context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var brands = new[] { "BMW", "Peugot", "Tesla" };
|
||||
var items = brands.Select(x => new SelectListItem(x)).ToList();
|
||||
return new ValueTask<IEnumerable<SelectListItem>>(items);
|
||||
}
|
||||
|
||||
protected override IActivityExecutionResult OnExecute() => Done(Brand);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using Elsa.Activities.UserTask.Extensions;
|
|||
using Elsa.Persistence.EntityFramework.Core.Extensions;
|
||||
using Elsa.Persistence.EntityFramework.Sqlite;
|
||||
using Elsa.Persistence.YesSql;
|
||||
using Elsa.Samples.Server.Host.Activities;
|
||||
using Elsa.Server.Hangfire.Extensions;
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
|
@ -48,6 +49,8 @@ namespace Elsa.Samples.Server.Host
|
|||
});
|
||||
|
||||
services
|
||||
.AddActivityPropertyOptionsProvider<VehicleActivity>()
|
||||
.AddRuntimeSelectItemsProvider<VehicleActivity>()
|
||||
.AddElsa(elsa => elsa
|
||||
//.UseEntityFrameworkPersistence(ef => ef.UseSqlite())
|
||||
.UseYesSqlPersistence()
|
||||
|
|
@ -62,9 +65,11 @@ namespace Elsa.Samples.Server.Host
|
|||
.AddJavaScriptActivities()
|
||||
.AddUserTaskActivities()
|
||||
.AddTelnyx()
|
||||
.AddActivitiesFrom<VehicleActivity>()
|
||||
.AddWorkflowsFrom<Startup>()
|
||||
);
|
||||
|
||||
// Elsa API endpoints.
|
||||
services
|
||||
.AddElsaApiEndpoints()
|
||||
.AddElsaSwagger();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Design;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
|
||||
namespace Elsa.Server.Api.Endpoints.Designer.RuntimeSelectListItems
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1")]
|
||||
[Route("v{apiVersion:apiVersion}/designer/runtime-select-list-items/{providerTypeName}")]
|
||||
[Produces("application/json")]
|
||||
public class Get : Controller
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public Get(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OkObjectResult))]
|
||||
[SwaggerOperation(
|
||||
Summary = "Returns a list of items to be used by the list control requesting the items.",
|
||||
Description = "Returns a list of items to be used by the list control requesting the items.",
|
||||
OperationId = "Designer.RuntimeSelectListItems.Get",
|
||||
Tags = new[] { "Designer.RuntimeSelectListItems" })
|
||||
]
|
||||
public async Task<IActionResult> Handle(string providerTypeName, object? context = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var type = Type.GetType(providerTypeName)!;
|
||||
var provider = (IRuntimeSelectListItemsProvider)ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, type);
|
||||
var items = (await provider.GetItemsAsync(context, cancellationToken)).ToList();
|
||||
|
||||
return Ok(items);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue