Merge pull request #1513 from MilkyWare/elsa-2.0/fileactivities
Elsa 2.0/fileactivities
This commit is contained in:
commit
b98528a95a
6
Elsa.sln
6
Elsa.sln
|
|
@ -358,6 +358,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Samples.Blazo
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.HttpEndpointSecurity", "src\samples\aspnet\Elsa.Samples.HttpEndpointSecurity\Elsa.Samples.HttpEndpointSecurity.csproj", "{82B115DA-E3D0-49D7-AD08-DE9387656756}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.WatchDirectoryWorker", "src\samples\worker\Elsa.Samples.WatchDirectoryWorker\Elsa.Samples.WatchDirectoryWorker.csproj", "{8EC3CF51-EBC1-4B45-881E-4B1B8190D579}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.InfiniteLoopDetection", "src\samples\aspnet\Elsa.Samples.InfiniteLoopDetection\Elsa.Samples.InfiniteLoopDetection.csproj", "{A2572DBF-AF11-4A02-AF99-B343871F2D85}"
|
||||
EndProject
|
||||
Global
|
||||
|
|
@ -846,6 +847,10 @@ Global
|
|||
{82B115DA-E3D0-49D7-AD08-DE9387656756}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{82B115DA-E3D0-49D7-AD08-DE9387656756}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{82B115DA-E3D0-49D7-AD08-DE9387656756}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8EC3CF51-EBC1-4B45-881E-4B1B8190D579}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8EC3CF51-EBC1-4B45-881E-4B1B8190D579}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8EC3CF51-EBC1-4B45-881E-4B1B8190D579}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8EC3CF51-EBC1-4B45-881E-4B1B8190D579}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A2572DBF-AF11-4A02-AF99-B343871F2D85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A2572DBF-AF11-4A02-AF99-B343871F2D85}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A2572DBF-AF11-4A02-AF99-B343871F2D85}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
@ -1009,6 +1014,7 @@ Global
|
|||
{DCB3C4DD-2B7D-44E9-A366-F36F4ABFF488} = {D86B94DC-A53C-4A67-A820-828DD359C49B}
|
||||
{9A4B1C48-16FB-4A65-AE98-5CDE7BCB506C} = {D86B94DC-A53C-4A67-A820-828DD359C49B}
|
||||
{82B115DA-E3D0-49D7-AD08-DE9387656756} = {22E75696-6FE9-436A-9097-EE21C603F818}
|
||||
{8EC3CF51-EBC1-4B45-881E-4B1B8190D579} = {E42743A0-FBDD-4150-9D53-6000496D9B87}
|
||||
{A2572DBF-AF11-4A02-AF99-B343871F2D85} = {22E75696-6FE9-436A-9097-EE21C603F818}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Elsa.ActivityResults;
|
|||
using Elsa.Attributes;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Activities.File
|
||||
|
|
@ -11,8 +12,9 @@ namespace Elsa.Activities.File
|
|||
Outcomes = new[] { OutcomeNames.Done })]
|
||||
public class DeleteFile : Activity
|
||||
{
|
||||
[Required]
|
||||
[ActivityInput(Hint = "Path of the file to delete.")]
|
||||
public string Path { get; set; } = default!;
|
||||
public string? Path { get; set; }
|
||||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Elsa.Attributes;
|
|||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
|
|
@ -13,20 +14,21 @@ namespace Elsa.Activities.File
|
|||
Outcomes = new[] { OutcomeNames.Done })]
|
||||
public class EnumerateFiles : Activity
|
||||
{
|
||||
[Required]
|
||||
[ActivityInput(Hint = "Path of the folder to enumerate files.")]
|
||||
public string Path { get; set; } = default!;
|
||||
|
||||
[ActivityInput(Hint = "Pattern for files to return.")]
|
||||
public string? Pattern { get; set; }
|
||||
|
||||
[ActivityInput(Hint = "Ignore inaccessible files.", Label = "Ignore Inaccessible")]
|
||||
public bool IgnoreInaccessible { get; set; } = true;
|
||||
[ActivityInput(Hint = "Ignore inaccessible files.", Label = "Ignore Inaccessible", DefaultValue = true)]
|
||||
public bool IgnoreInaccessible { get; set; }
|
||||
|
||||
[ActivityInput(Hint = "Set case sensitivity.", Label = "Match Casing")]
|
||||
public MatchCasing MatchCasing { get; set; } = MatchCasing.CaseInsensitive;
|
||||
[ActivityInput(Hint = "Set case sensitivity.", Label = "Match Casing", DefaultValue = MatchCasing.CaseInsensitive)]
|
||||
public MatchCasing MatchCasing { get; set; }
|
||||
|
||||
[ActivityInput(Hint = "Return files from sub directories", Label = "Sub Directories")]
|
||||
public bool SubDirectories { get; set; } = false;
|
||||
[ActivityInput(Hint = "Return files from sub directories", Label = "Sub Directories", DefaultValue = false)]
|
||||
public bool SubDirectories { get; set; }
|
||||
|
||||
[ActivityOutput(Hint = "List of files.")]
|
||||
public IEnumerable<string>? Files { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Design;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Activities.File
|
||||
|
|
@ -11,8 +13,9 @@ namespace Elsa.Activities.File
|
|||
Outcomes = new[] { OutcomeNames.True, OutcomeNames.False })]
|
||||
public class FileExists : Activity
|
||||
{
|
||||
[Required]
|
||||
[ActivityInput(Hint = "Path of the file to delete.")]
|
||||
public string Path { get; set; } = default!;
|
||||
public string? Path { get; set; }
|
||||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Elsa.Expressions;
|
|||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
@ -16,14 +17,16 @@ namespace Elsa.Activities.File
|
|||
Outcomes = new[] { OutcomeNames.Done })]
|
||||
public class OutFile : Activity
|
||||
{
|
||||
[Required]
|
||||
[ActivityInput(Hint = "Bytes to write to file.", SupportedSyntaxes = new[] { SyntaxNames.JavaScript }, DefaultSyntax = SyntaxNames.JavaScript)]
|
||||
public byte[] Bytes { get; set; } = default!;
|
||||
public byte[]? Bytes { get; set; }
|
||||
|
||||
[Required]
|
||||
[ActivityInput(Hint = "Path to create file at.", UIHint = ActivityInputUIHints.SingleLine, SupportedSyntaxes = new[] { SyntaxNames.JavaScript, SyntaxNames.Liquid })]
|
||||
public string Path { get; set; } = default!;
|
||||
public string? Path { get; set; }
|
||||
|
||||
[ActivityInput(Hint = "How the output file should be written to.", UIHint = ActivityInputUIHints.Dropdown)]
|
||||
public CopyMode Mode { get; set; } = CopyMode.CreateNew;
|
||||
[ActivityInput(Hint = "How the output file should be written to.", UIHint = ActivityInputUIHints.Dropdown, DefaultValue = CopyMode.CreateNew)]
|
||||
public CopyMode Mode { get; set; }
|
||||
|
||||
protected override async ValueTask<IActivityExecutionResult> OnExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Elsa.Design;
|
|||
using Elsa.Expressions;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
|
|
@ -14,6 +15,7 @@ namespace Elsa.Activities.File
|
|||
Outcomes = new[] { OutcomeNames.Done })]
|
||||
public class ReadFile : Activity
|
||||
{
|
||||
[Required]
|
||||
[ActivityInput(Hint = "Path to read content from.", UIHint = ActivityInputUIHints.SingleLine, SupportedSyntaxes = new[] { SyntaxNames.JavaScript, SyntaxNames.Liquid })]
|
||||
public string Path { get; set; } = default!;
|
||||
|
||||
|
|
@ -23,7 +25,7 @@ namespace Elsa.Activities.File
|
|||
public override async ValueTask<IActivityExecutionResult> ExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
Bytes = await System.IO.File.ReadAllBytesAsync(Path);
|
||||
return Done();
|
||||
return Done(Bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,34 +10,34 @@ namespace Elsa.Activities.File
|
|||
{
|
||||
public static class ReadFileBuilderExtensions
|
||||
{
|
||||
public static IActivityBuilder ReadFile(this IBuilder builder, Action<ISetupActivity<OutFile>> setup, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) => builder.Then(setup, null, lineNumber, sourceFile);
|
||||
public static IActivityBuilder ReadFile(this IBuilder builder, Action<ISetupActivity<ReadFile>> setup, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) => builder.Then(setup, null, lineNumber, sourceFile);
|
||||
|
||||
public static IActivityBuilder ReadFile(this IBuilder builder, Func<ActivityExecutionContext, string?> path, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.OutFile(activity => activity
|
||||
builder.ReadFile(activity => activity
|
||||
.Set(x => x.Path, path),
|
||||
lineNumber,
|
||||
sourceFile);
|
||||
|
||||
public static IActivityBuilder ReadFile(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<string?>> path, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.OutFile(activity => activity
|
||||
builder.ReadFile(activity => activity
|
||||
.Set(x => x.Path, path),
|
||||
lineNumber,
|
||||
sourceFile);
|
||||
|
||||
public static IActivityBuilder ReadFile(this IBuilder builder, Func<string?> path, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.OutFile(activity => activity
|
||||
builder.ReadFile(activity => activity
|
||||
.Set(x => x.Path, path),
|
||||
lineNumber,
|
||||
sourceFile);
|
||||
|
||||
public static IActivityBuilder ReadFile(this IBuilder builder, Func<ValueTask<string?>> path, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.OutFile(activity => activity
|
||||
builder.ReadFile(activity => activity
|
||||
.Set(x => x.Path, path),
|
||||
lineNumber,
|
||||
sourceFile);
|
||||
|
||||
public static IActivityBuilder ReadFile(this IBuilder builder, string? path, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.OutFile(activity => activity
|
||||
builder.ReadFile(activity => activity
|
||||
.Set(x => x.Path, path),
|
||||
lineNumber,
|
||||
sourceFile);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Services;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Activities.File
|
||||
|
|
@ -10,6 +11,7 @@ namespace Elsa.Activities.File
|
|||
Outcomes = new[] { OutcomeNames.Done })]
|
||||
public class TempFile : Activity
|
||||
{
|
||||
[Required]
|
||||
[ActivityOutput(Hint = "Path of the created temporary file.")]
|
||||
public string Path { get; set; } = default!;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
using Elsa.Activities.File.Models;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Design;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Activities.File
|
||||
{
|
||||
[Trigger(Category = "File",
|
||||
Description = "Triggers when files are created/modified in the given folder",
|
||||
Outcomes = new[] { OutcomeNames.Done })]
|
||||
public class WatchDirectory : Activity
|
||||
{
|
||||
[Required]
|
||||
[ActivityInput(Hint = "The path of the directory to watch")]
|
||||
public string? Path { get; set; }
|
||||
|
||||
[ActivityInput(Hint = "The file pattern for interested files")]
|
||||
public string? Pattern { get; set; }
|
||||
|
||||
[ActivityInput(Label = "Change Types", Hint = "The types of file system events to subscribe to", UIHint = ActivityInputUIHints.CheckList, DefaultValue = WatcherChangeTypes.All)]
|
||||
public WatcherChangeTypes ChangeTypes { get; set; }
|
||||
|
||||
[ActivityInput(Label = "Notify Filters", UIHint = ActivityInputUIHints.CheckList, DefaultValue = NotifyFilters.Attributes |
|
||||
NotifyFilters.CreationTime |
|
||||
NotifyFilters.DirectoryName |
|
||||
NotifyFilters.FileName |
|
||||
NotifyFilters.LastAccess |
|
||||
NotifyFilters.LastWrite |
|
||||
NotifyFilters.Security |
|
||||
NotifyFilters.Size)]
|
||||
public NotifyFilters NotifyFilters { get; set; }
|
||||
|
||||
[ActivityOutput]
|
||||
public FileSystemEvent? Output { get; set; }
|
||||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
{
|
||||
var model = (FileSystemEvent)context.Input!;
|
||||
Output = model;
|
||||
return Done(Output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using Elsa.Builders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Elsa.Activities.File
|
||||
{
|
||||
public static class WatchDirectoryBuilderExtensions
|
||||
{
|
||||
public static IActivityBuilder WatchDirectory(this IActivityBuilder builder, Action<ISetupActivity<WatchDirectory>> setup, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) => builder.Then(setup, null, lineNumber, sourceFile);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Activities.File
|
||||
{
|
||||
public static class WatchDirectoryExtensions
|
||||
{
|
||||
#region ChangeTypes
|
||||
public static ISetupActivity<WatchDirectory> WithChangeTypes(this ISetupActivity<WatchDirectory> setup, Func<ActivityExecutionContext, ValueTask<WatcherChangeTypes>> changeTypes) => setup.Set(x => x.ChangeTypes, changeTypes);
|
||||
public static ISetupActivity<WatchDirectory> WithChangeTypes(this ISetupActivity<WatchDirectory> setup, Func<ActivityExecutionContext, WatcherChangeTypes> changeTypes) => setup.Set(x => x.ChangeTypes, changeTypes);
|
||||
public static ISetupActivity<WatchDirectory> WithChangeTypes(this ISetupActivity<WatchDirectory> setup, Func<ValueTask<WatcherChangeTypes>> changeTypes) => setup.Set(x => x.ChangeTypes, changeTypes);
|
||||
public static ISetupActivity<WatchDirectory> WithChangeTypes(this ISetupActivity<WatchDirectory> setup, Func<WatcherChangeTypes> changeTypes) => setup.Set(x => x.ChangeTypes, changeTypes);
|
||||
public static ISetupActivity<WatchDirectory> WithChangeTypes(this ISetupActivity<WatchDirectory> setup, WatcherChangeTypes changeTypes) => setup.Set(x => x.ChangeTypes, changeTypes);
|
||||
#endregion
|
||||
|
||||
#region NotifyFilters
|
||||
public static ISetupActivity<WatchDirectory> WithNotifyFilters(this ISetupActivity<WatchDirectory> setup, Func<ActivityExecutionContext, ValueTask<NotifyFilters>> notifyFilters) => setup.Set(x => x.NotifyFilters, notifyFilters);
|
||||
public static ISetupActivity<WatchDirectory> WithNotifyFilters(this ISetupActivity<WatchDirectory> setup, Func<ActivityExecutionContext, NotifyFilters> notifyFilters) => setup.Set(x => x.NotifyFilters, notifyFilters);
|
||||
public static ISetupActivity<WatchDirectory> WithNotifyFilters(this ISetupActivity<WatchDirectory> setup, Func<ValueTask<NotifyFilters>> notifyFilters) => setup.Set(x => x.NotifyFilters, notifyFilters);
|
||||
public static ISetupActivity<WatchDirectory> WithNotifyFilters(this ISetupActivity<WatchDirectory> setup, Func<NotifyFilters> notifyFilters) => setup.Set(x => x.NotifyFilters, notifyFilters);
|
||||
public static ISetupActivity<WatchDirectory> WithNotifyFilters(this ISetupActivity<WatchDirectory> setup, NotifyFilters notifyFilters) => setup.Set(x => x.NotifyFilters, notifyFilters);
|
||||
#endregion
|
||||
|
||||
#region Path
|
||||
public static ISetupActivity<WatchDirectory> WithPath(this ISetupActivity<WatchDirectory> setup, Func<ActivityExecutionContext, ValueTask<string?>> path) => setup.Set(x => x.Path, path);
|
||||
public static ISetupActivity<WatchDirectory> WithPath(this ISetupActivity<WatchDirectory> setup, Func<ActivityExecutionContext, string?> path) => setup.Set(x => x.Path, path);
|
||||
public static ISetupActivity<WatchDirectory> WithPath(this ISetupActivity<WatchDirectory> setup, Func<ValueTask<string?>> path) => setup.Set(x => x.Path, path);
|
||||
public static ISetupActivity<WatchDirectory> WithPath(this ISetupActivity<WatchDirectory> setup, Func<string?> path) => setup.Set(x => x.Path, path);
|
||||
public static ISetupActivity<WatchDirectory> WithPath(this ISetupActivity<WatchDirectory> setup, string? path) => setup.Set(x => x.Path, path);
|
||||
#endregion
|
||||
|
||||
#region Pattern
|
||||
public static ISetupActivity<WatchDirectory> WithPattern(this ISetupActivity<WatchDirectory> setup, Func<ActivityExecutionContext, ValueTask<string?>> pattern) => setup.Set(x => x.Pattern, pattern);
|
||||
public static ISetupActivity<WatchDirectory> WithPattern(this ISetupActivity<WatchDirectory> setup, Func<ActivityExecutionContext, string?> pattern) => setup.Set(x => x.Pattern, pattern);
|
||||
public static ISetupActivity<WatchDirectory> WithPattern(this ISetupActivity<WatchDirectory> setup, Func<ValueTask<string?>> pattern) => setup.Set(x => x.Pattern, pattern);
|
||||
public static ISetupActivity<WatchDirectory> WithPattern(this ISetupActivity<WatchDirectory> setup, Func<string?> pattern) => setup.Set(x => x.Pattern, pattern);
|
||||
public static ISetupActivity<WatchDirectory> WithPattern(this ISetupActivity<WatchDirectory> setup, string? pattern) => setup.Set(x => x.Pattern, pattern);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using Elsa.Services;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Activities.File.Bookmarks
|
||||
{
|
||||
public class FileSystemEventBookmark : IBookmark
|
||||
{
|
||||
public FileSystemEventBookmark()
|
||||
{ }
|
||||
|
||||
public FileSystemEventBookmark(string? path, string? pattern, WatcherChangeTypes changeTypes, NotifyFilters notifyFilters)
|
||||
{
|
||||
ChangeTypes = changeTypes;
|
||||
NotifyFilters = notifyFilters;
|
||||
Path = path;
|
||||
Pattern = pattern;
|
||||
}
|
||||
|
||||
public WatcherChangeTypes ChangeTypes { get; set; }
|
||||
|
||||
public NotifyFilters NotifyFilters { get; set; }
|
||||
|
||||
public string? Path { get; set; }
|
||||
|
||||
public string? Pattern { get; set; }
|
||||
}
|
||||
|
||||
public class FileCreatedBookmarkProvider : BookmarkProvider<FileSystemEventBookmark, WatchDirectory>
|
||||
{
|
||||
public override async ValueTask<IEnumerable<BookmarkResult>> GetBookmarksAsync(BookmarkProviderContext<WatchDirectory> context, CancellationToken cancellationToken)
|
||||
{
|
||||
var changeTypes = await context.ReadActivityPropertyAsync(a => a.ChangeTypes);
|
||||
var notifyFilters = await context.ReadActivityPropertyAsync(a => a.NotifyFilters);
|
||||
var path = await context.ReadActivityPropertyAsync(a => a.Path);
|
||||
var pattern = await context.ReadActivityPropertyAsync(a => a.Pattern);
|
||||
var result = Result(new FileSystemEventBookmark(path, pattern, changeTypes, notifyFilters));
|
||||
return new[] { result };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
using Elsa;
|
||||
using Elsa.Activities.File;
|
||||
using Elsa.Activities.File.Bookmarks;
|
||||
using Elsa.Activities.File.Services;
|
||||
using Elsa.Activities.File.StartupTasks;
|
||||
using Elsa.Runtime;
|
||||
using System;
|
||||
using Elsa.Options;
|
||||
using Elsa.Activities.File.MapperProfiles;
|
||||
using Elsa.Services;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
|
|
@ -23,7 +29,14 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
.AddActivity<FileExists>()
|
||||
.AddActivity<OutFile>()
|
||||
.AddActivity<ReadFile>()
|
||||
.AddActivity<TempFile>();
|
||||
.AddActivity<TempFile>()
|
||||
.AddActivity<WatchDirectory>();
|
||||
|
||||
builder.Services.AddBookmarkProvider<FileCreatedBookmarkProvider>()
|
||||
.AddAutoMapperProfile<FileSystemEventProfile>()
|
||||
.AddSingleton<FileSystemWatchersStarter>()
|
||||
.AddSingleton<Scoped<IWorkflowLaunchpad>>()
|
||||
.AddStartupTask<StartFileSystemWatchers>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
using AutoMapper;
|
||||
using Elsa.Activities.File.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Elsa.Activities.File.MapperProfiles
|
||||
{
|
||||
public class FileSystemEventProfile : Profile
|
||||
{
|
||||
public FileSystemEventProfile()
|
||||
{
|
||||
CreateMap<FileSystemEventArgs, FileSystemEvent>()
|
||||
.ForMember(d => d.ChangeType,
|
||||
o => o.MapFrom(s => s.ChangeType))
|
||||
.ForMember(d => d.Directory,
|
||||
o => o.MapFrom(s => Path.GetDirectoryName(s.FullPath)))
|
||||
.ForMember(d => d.FileName,
|
||||
o => o.MapFrom(s => s.Name))
|
||||
.ForMember(d => d.FullPath,
|
||||
o => o.MapFrom(s => s.FullPath))
|
||||
.ForMember(d => d.TimeStamp,
|
||||
o => o.MapFrom(s => DateTime.Now));
|
||||
|
||||
CreateMap<RenamedEventArgs, FileSystemEvent>()
|
||||
.IncludeBase<FileSystemEventArgs, FileSystemEvent>()
|
||||
.ForMember(d => d.OldFileName,
|
||||
o => o.MapFrom(s => s.OldName))
|
||||
.ForMember(d => d.OldFullPath,
|
||||
o => o.MapFrom(s => Path.GetDirectoryName(s.OldFullPath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Elsa.Activities.File.Models
|
||||
{
|
||||
public class FileSystemChanged
|
||||
{
|
||||
public FileSystemChanged()
|
||||
{
|
||||
}
|
||||
|
||||
public FileSystemChanged(WatcherChangeTypes changeType, string directory, string fileName, string fullPath)
|
||||
{
|
||||
ChangeType = changeType;
|
||||
Directory = directory;
|
||||
FileName = fileName;
|
||||
FullPath = fullPath;
|
||||
TimeStamp = DateTime.Now;
|
||||
}
|
||||
|
||||
public WatcherChangeTypes ChangeType { get; set; }
|
||||
|
||||
public string Directory { get; set; } = default!;
|
||||
|
||||
public string FileName { get; set; } = default!;
|
||||
|
||||
public string FullPath { get; set; } = default!;
|
||||
|
||||
public DateTime TimeStamp { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Elsa.Activities.File.Models
|
||||
{
|
||||
public class FileSystemEvent
|
||||
{
|
||||
public WatcherChangeTypes ChangeType { get; set; }
|
||||
|
||||
public string Directory { get; set; } = default!;
|
||||
|
||||
public string FileName { get; set; } = default!;
|
||||
|
||||
public string FullPath { get; set; } = default!;
|
||||
|
||||
public string OldFileName { get; set; } = default!;
|
||||
|
||||
public string OldFullPath { get; set; } = default!;
|
||||
|
||||
public DateTime TimeStamp { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
using AutoMapper;
|
||||
using Elsa.Activities.File.Bookmarks;
|
||||
using Elsa.Activities.File.Models;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Activities.File.Services
|
||||
{
|
||||
public class FileSystemWatchersStarter
|
||||
{
|
||||
private readonly ILogger<FileSystemWatchersStarter> _logger;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly SemaphoreSlim _semaphore = new(1);
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ICollection<FileSystemWatcher> _watchers;
|
||||
private readonly Scoped<IWorkflowLaunchpad> _workflowLaunchpad;
|
||||
|
||||
public FileSystemWatchersStarter(ILogger<FileSystemWatchersStarter> logger,
|
||||
IMapper mapper,
|
||||
IServiceScopeFactory scopeFactory,
|
||||
IServiceProvider serviceProvider,
|
||||
Scoped<IWorkflowLaunchpad> workflowLaunchpad)
|
||||
{
|
||||
_logger = logger;
|
||||
_mapper = mapper;
|
||||
_scopeFactory = scopeFactory;
|
||||
_serviceProvider = serviceProvider;
|
||||
_watchers = new List<FileSystemWatcher>();
|
||||
_workflowLaunchpad = workflowLaunchpad;
|
||||
}
|
||||
|
||||
public async Task CreateAndAddWatchersAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _semaphore.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var activities = GetActivityInstancesAsync<WatchDirectory>(cancellationToken);
|
||||
await foreach (var a in activities)
|
||||
{
|
||||
var changeTypes = await a.EvaluatePropertyValueAsync(x => x.ChangeTypes, cancellationToken);
|
||||
var notifyFilters = await a.EvaluatePropertyValueAsync(x => x.NotifyFilters, cancellationToken);
|
||||
var path = await a.EvaluatePropertyValueAsync(x => x.Path, cancellationToken);
|
||||
var pattern = await a.EvaluatePropertyValueAsync(x => x.Pattern, cancellationToken);
|
||||
CreateAndAddWatcher(path, pattern, changeTypes, notifyFilters);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateAndAddWatcher(string? path, string? pattern, WatcherChangeTypes changeTypes, NotifyFilters notifyFilters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
throw new ArgumentException("File watcher path must not be null or empty");
|
||||
|
||||
EnsurePathExists(path);
|
||||
|
||||
var watcher = new FileSystemWatcher()
|
||||
{
|
||||
Path = path,
|
||||
Filter = pattern,
|
||||
NotifyFilter = notifyFilters
|
||||
};
|
||||
|
||||
if (changeTypes == WatcherChangeTypes.Created || changeTypes == WatcherChangeTypes.All)
|
||||
watcher.Created += FileCreated;
|
||||
|
||||
if (changeTypes == WatcherChangeTypes.Changed || changeTypes == WatcherChangeTypes.All)
|
||||
watcher.Changed += FileChanged;
|
||||
|
||||
if (changeTypes == WatcherChangeTypes.Deleted || changeTypes == WatcherChangeTypes.All)
|
||||
watcher.Deleted += FileDeleted;
|
||||
|
||||
if (changeTypes == WatcherChangeTypes.Renamed || changeTypes == WatcherChangeTypes.All)
|
||||
watcher.Renamed += FileRenamed;
|
||||
|
||||
watcher.EnableRaisingEvents = true;
|
||||
_watchers.Add(watcher);
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<IActivityBlueprintWrapper<WatchDirectory>> GetActivityInstancesAsync<TActivity>([EnumeratorCancellation] CancellationToken cancellationToken) where TActivity : IActivity
|
||||
{
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var workflowRegistry = scope.ServiceProvider.GetRequiredService<IWorkflowRegistry>();
|
||||
var workflowBlueprintReflector = scope.ServiceProvider.GetRequiredService<IWorkflowBlueprintReflector>();
|
||||
var workflows = await workflowRegistry.ListActiveAsync(cancellationToken);
|
||||
|
||||
var query = from workflow in workflows
|
||||
from activity in workflow.Activities
|
||||
where activity.Type == nameof(WatchDirectory)
|
||||
select workflow;
|
||||
|
||||
foreach (var workflow in query)
|
||||
{
|
||||
var workflowBlueprintWrapper = await workflowBlueprintReflector.ReflectAsync(scope.ServiceProvider, workflow, cancellationToken);
|
||||
|
||||
foreach (var activity in workflowBlueprintWrapper.Filter<WatchDirectory>())
|
||||
{
|
||||
yield return activity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsurePathExists(string? path)
|
||||
{
|
||||
_logger.LogDebug($"Checking ${path} exists");
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
_logger.LogInformation($"Creating directory {path}");
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
#region Watcher delegates
|
||||
private void FileCreated(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
StartWorkflow((FileSystemWatcher)sender, e);
|
||||
}
|
||||
|
||||
private void FileChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
if (e.ChangeType != WatcherChangeTypes.Changed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StartWorkflow((FileSystemWatcher)sender, e);
|
||||
}
|
||||
|
||||
private void FileDeleted(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
StartWorkflow((FileSystemWatcher)sender, e);
|
||||
}
|
||||
|
||||
private void FileRenamed(object sender, RenamedEventArgs e)
|
||||
{
|
||||
StartWorkflow((FileSystemWatcher)sender, e);
|
||||
}
|
||||
|
||||
private void StartWorkflow(FileSystemWatcher watcher, FileSystemEventArgs e)
|
||||
{
|
||||
var changeTypes = e.ChangeType;
|
||||
var notifyFilter = watcher.NotifyFilter;
|
||||
var path = watcher.Path;
|
||||
var pattern = watcher.Filter;
|
||||
|
||||
var model = _mapper.Map<FileSystemEvent>(e);
|
||||
var bookmark = new FileSystemEventBookmark(path, pattern, changeTypes, notifyFilter);
|
||||
var launchContext = new WorkflowsQuery(nameof(WatchDirectory), bookmark);
|
||||
_workflowLaunchpad.UseService(s => s.CollectAndDispatchWorkflowsAsync(launchContext, new WorkflowInput(model)));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using Elsa.Activities.File.Services;
|
||||
using Elsa.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Activities.File.StartupTasks
|
||||
{
|
||||
public class StartFileSystemWatchers : IStartupTask
|
||||
{
|
||||
private readonly FileSystemWatchersStarter _starter;
|
||||
|
||||
public StartFileSystemWatchers(FileSystemWatchersStarter starter) => _starter = starter;
|
||||
|
||||
public int Order => 2000;
|
||||
|
||||
public Task ExecuteAsync(CancellationToken cancellationToken = default) => _starter.CreateAndAddWatchersAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="appsettings.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Console\Elsa.Activities.Console.csproj" />
|
||||
<ProjectReference Include="..\..\..\activities\Elsa.Activities.File\Elsa.Activities.File.csproj" />
|
||||
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Temporal.Quartz\Elsa.Activities.Temporal.Quartz.csproj" />
|
||||
<ProjectReference Include="..\..\..\core\Elsa\Elsa.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="appsettings.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Program.cs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
using Elsa.Samples.WatchDirectoryWorker.Workflows;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Elsa.Samples.WatchDirectoryWorker
|
||||
{
|
||||
class Program
|
||||
{
|
||||
private static readonly string _directory;
|
||||
private static readonly string _systemRoot;
|
||||
|
||||
static Program()
|
||||
{
|
||||
_systemRoot = Path.GetPathRoot(Environment.SystemDirectory);
|
||||
_directory = Path.Combine(_systemRoot, "Temp\\FileWatchers");
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
services
|
||||
.AddElsa(options => options
|
||||
.AddConsoleActivities()
|
||||
.AddFileActivities()
|
||||
.AddQuartzTemporalActivities()
|
||||
.AddWorkflow<TimerCreateFile>(sp => ActivatorUtilities.CreateInstance<TimerCreateFile>(services.BuildServiceProvider(), _directory))
|
||||
.AddWorkflow<WatchDirectoryCreatedWorkflow>(sp => new WatchDirectoryCreatedWorkflow(_directory)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using Elsa.Activities.File;
|
||||
using Elsa.Activities.Temporal;
|
||||
using Elsa.Builders;
|
||||
using NodaTime;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Samples.WatchDirectoryWorker.Workflows
|
||||
{
|
||||
public class TimerCreateFile : IWorkflow
|
||||
{
|
||||
private IClock _clock;
|
||||
private string _directory;
|
||||
|
||||
public TimerCreateFile(IClock clock, string directory)
|
||||
{
|
||||
_clock = clock;
|
||||
_directory = directory;
|
||||
}
|
||||
|
||||
public void Build(IWorkflowBuilder builder) => builder
|
||||
.Timer(Duration.FromSeconds(10))
|
||||
.OutFile(setup =>
|
||||
{
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
var filename = Path.Combine(_directory, $"{guid}.txt");
|
||||
|
||||
setup.WithPath(filename);
|
||||
setup.WithBytes(Encoding.UTF8.GetBytes("Hello World"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
using Elsa.Activities.Console;
|
||||
using Elsa.Activities.File;
|
||||
using Elsa.Activities.File.Models;
|
||||
using Elsa.Builders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Unicode;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Samples.WatchDirectoryWorker.Workflows
|
||||
{
|
||||
public class WatchDirectoryCreatedWorkflow : IWorkflow
|
||||
{
|
||||
private readonly string _directory;
|
||||
private readonly string _systemRoot = Path.GetPathRoot(Environment.SystemDirectory);
|
||||
|
||||
public WatchDirectoryCreatedWorkflow(string directory)
|
||||
{
|
||||
_directory = directory;
|
||||
}
|
||||
|
||||
public void Build(IWorkflowBuilder builder) => builder.WatchDirectory(setup =>
|
||||
{
|
||||
setup.WithPath(Path.Combine(_systemRoot, "Temp\\FileWatchers"))
|
||||
.WithPattern("*.txt")
|
||||
.WithChangeTypes(WatcherChangeTypes.Created);
|
||||
})
|
||||
.ReadFile(setup =>
|
||||
{
|
||||
setup.WithPath(context => context.GetInput<FileSystemEvent>().FullPath);
|
||||
})
|
||||
.WriteLine(setup =>
|
||||
{
|
||||
setup.WithText(async context =>
|
||||
{
|
||||
var fsEvent = await context.WorkflowExecutionContext.GetActivityPropertyAsync<WatchDirectory, FileSystemEvent>("activity-1", a => a.Output);
|
||||
var data = await context.WorkflowExecutionContext.GetActivityPropertyAsync<ReadFile, byte[]>("activity-2", a => a.Bytes);
|
||||
var line = $"{GetType().Name}-{fsEvent.FullPath}-{fsEvent.ChangeType}-{Encoding.UTF8.GetString(data)}";
|
||||
return line;
|
||||
});
|
||||
})
|
||||
.DeleteFile(context => context.GetInput<FileSystemEvent>().FullPath);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
},
|
||||
"Console": {
|
||||
"FormatterName": "simple",
|
||||
"FormatterOptions": {
|
||||
"TimestampFormat": "yyyy-MM-ddTHH:mm:ss "
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue