Store Workflow Activity Data When Publishing - elsa-workflows

I Need to store a specific activity data in another collection in database whenever a user publish a workflow in elsa.
I dont find any documentation, Please suggest me some resource or suggestion to achieve this. I have try to implement this with middleware. The Middleware code is
namespace WorkFlowV3
{
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class CustomMiddleware
{
private readonly RequestDelegate _next;
static HttpClient client = new HttpClient();
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
//Write Custom Logic Here....
client.BaseAddress = new Uri("#");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
string path = "/api/test-middleware-call";
HttpResponseMessage response = await client.GetAsync(path);
await _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleware>();
}
}
}
But in this process, I cant fetch the specific activity data.

The easiest way to store information in your own DB in response to the "workflow published" event is by implementing a notification handler (from MediatR) that handles the WorkflowDefinitionPublished notification.
For example:
public class MyWorkflowPublishedhandler : INotificationhandler<WorkflowDefinitionPublished>
{
private readonly IMyDatabaseStore _someRepository;
public MyWorkflowPublishedhandler(IMyDatabaseStore someRepository)
{
_someRepository = someRepository;
}
public async Task Handle(WorkflowDefinitionPublished notification, CancellationToken cancellationToken)
{
var workflowDefinition = notification.WorkflowDefinition;
// Your logic to do a thing.
}
}
To register this handler, from your Startup or Program class, add the following code:
services.AddNotificationHandler<MyWorkflowPublishedhandler>();
Your handler will be invoked every time a workflow gets published.

Related

Access IStatePropertyAccessor botstate in a controller in v4

If you need to access the state in botframework v4 outside of a bot context (that does not implement IBot), for instance a Controller, how could you easily get a hold of this state object? The problem is that you can't really inject it directly because it needs to be initialized with a ChannelId and ConversationId.
The following approach works but looks a bit strange as I am using the TestAdapter to initialize the TurnContext. Isn't there a better, more obvious method to get a hold of the state?
public class SampleController : ControllerBase
{
private readonly MyBotAccessors _botAccessors;
public SampleController(MyBotAccessors botAccessors)
{
_botAccessors = botAccessors;
}
}
public async Task Reset(string conversationKey, string channelId)
{
var turnContext = new TurnContext(new TestAdapter(), new Activity { ChannelId = channelId, Conversation = new ConversationAccount { Id = conversationKey } });
await _botAccessors.MyContextState.SetAsync(turnContext, new MyContextState().Reset());
await _botAccessors.ConversationState.SaveChangesAsync(turnContext);
}

Alternative to HttpRequestMessage SetContext method

I have this code from codeplex
private async Task ExecuteChangeSet(
ChangeSetRequestItem changeSet,
IList<ODataBatchResponseItem> responses,
CancellationToken cancellation)
{
ChangeSetResponseItem changeSetResponse;
// Create a new ShoppingContext instance, associate it with each of the requests, start a new
// transaction, execute the changeset and then commit or rollback the transaction depending on
// whether the responses were all successful or not.
using (ShoppingContext context = new ShoppingContext())
{
foreach (HttpRequestMessage request in changeSet.Requests)
{
request.SetContext(context);
}
The complete sample code can be found here.
I downloaded the project and it is using .net framework 4.5
but in .NET Framework 4.6.1 the SetContext method is no longer present
I want to know how can I achieve the same in framework version 4.6.1?
I am basically creating a OData V3 Service which will be hosted in IIS.
You can go with creating your own functionality for setting the context and retrieve it where needed, with HttpRequestMessage extensions like:
Example class:
public static class HttpRequestMessageExtensions
{
private const string Context = "ShoppingContext";
public static void SetContext(this HttpRequestMessage request, ShoppingContext context)
{
request.Properties[Context] = context;
}
public static ShoppingContext GetContext(this HttpRequestMessage request)
{
object context;
if (request.Properties.TryGetValue(Context, out context))
{
return (ShoppingContext) context;
}
return null;
}
}
Usage:
//Setting context
request.SetContext(context);
//reading context
var context = request.GetContext();

Entity Framework 6 "DbContext has been disposed" exception

Something very strange is happening in production, and it only happens in production. I have a Web API running and in one of the APIs, there is a repository created in the constructor and used in the functions. This is how the flow of a request works:
HTTP request comes in
MVC API controller decides which "worker" class to instantiate and creates it using Activator.CreateInstance
API controller calls worker.OnExecute inside of a Task.Run() and returns the http response
Worker calls _engine.Execute
Each worker instantiates another "engine" class that has all of the logic.
The engine in case constructs 3 repositories created using a UnitOfWork that is created per engine instance, like so:
public class MyWorker : Worker
{
private readonly MyEngine _engine;
public MyWorker()
{
_engine = new MyEngine();
}
protected override WorkerResult OnExecute(JObject data, CancellationToken cta)
{
return new WorkerResult(HttpStatusCode.OK, _engine.Execute(data));
}
}
public class MyEngine : EngineBase
{
private BaseRepository<Order> OrderRepo { get; set; }
private BaseRepository<OrderItem> OrderItemRepo { get; set; }
public MyEngine()
{
OrderRepo = new BaseRepository<Order>(MyUnitOfWork);
OrderItemRepo = new BaseRepository<OrderItem>(MyUnitOfWork);
}
public string Execute(JObject data)
{
return IsOrderValid(data).ToString();
}
public bool IsOrderValid(JObject data)
{
var orderId = data.Value<int>("OrderId");
// Without this line it crashes. With this line it crashes
//OrderRepo = new BaseRepository<Order>(InternationalWork);
// This is where it crashes
Order order = OrderRepo.First(x => x.OrderID == orderId);
// more code
}
}
public class EngineBase : UnitOfWorker, IDisposable
{
private UnitOfWork _myUnitOfWork;
public EngineBase() { }
public UnitOfWork MyUnitOfWork
{
get
{
return _myUnitOfWork ?? (_myUnitOfWork = new UnitOfWork(new DbContextAdapter(new MyDbContext())));
}
}
}
This is the actual stack trace:
The operation cannot be completed because the DbContext has been disposed.
StackTrace1
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.LazyInternalContext.get_ObjectContext()
at System.Data.Entity.Internal.Linq.InternalSet`1.CreateObjectQuery(Boolean asNoTracking, Nullable`1 streaming, IDbExecutionStrategy executionStrategy)
at System.Data.Entity.Internal.Linq.InternalSet`1.InitializeUnderlyingTypes(EntitySetTypePair pair)
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
The stack trace shows "FirstOrDefault" because OrderRepo.First internally calls DbSet.FirstOrDefault, like so:
public virtual T First(Expression<Func<T, bool>> query)
{
return _dbSet.FirstOrDefault(query);
}
I'm stumped because each worker is created per http request. Each DBContext is created per engine instance so I don't know how it could be disposed when it was just created in the constructor. And this only happens on the production web server where I presume it's being called more. Any tips would be greatly appreciated.

How to persist policy authorization results for users in ASP.NET Core, MVC 6?

Currently I have a simple custom policy handler that looks like so:
protected override void Handle(AuthorizationContext context, UserPolicyRequirement requirement)
{
// authorize user against policy requirements
if (_authorizationTask.AuthorizeUserAgainstPolicy(context.User, requirement))
{
// User passed policy req's
context.Succeed(requirement);
}
}
Problem is, this authorization step takes a long time to execute, but this is required in many different areas of the website. Is there any readily available mechanisms to save/cache the results of this policy authorization so that I only need to do this once per session?
I am currently using Windows Authentication, if that helps.
If per session way does not cause any problem, you can use Session to store user data. Simple implementation is something like below:
First you need a service to get user data from any store
public interface IGetUserDataService
{
<type> GetUserData();
}
I assume that there is Session configuration(see) and IGetUserDataService implementation.
Then you need to create a middleware to handle Session
public class SessionMiddleware
{
private readonly RequestDelegate _next;
private readonly IGetUserDataService _getUserDataService;
public SessionMiddleware(RequestDelegate next, IGetUserDataService getUserDataService)
{
_next = next;
_getUserDataService = getUserDataService;
}
public async Task Invoke(HttpContext context)
{
//user data is obtained only once then is stored in Session
if (context.Session.Get("UserData") == null)
{
context.Session.Set("UserData", getUserDataService.GetData());
}
await _next.Invoke(context);
}
}
//In Startup.cs
app.UseMiddleware<SessionMiddleware>();
Finally get and use session data in handler
public class YourHandler : AuthorizationHandler<YourRequirement>
{
private readonly IHttpContextAccessor _accessor;
public YourHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
{
var userData =(<type>)_accessor.HttpContext.Session.Get("UserData");
// check
}
}

Logging response and request to OAuth provider

Is there a way to capture the response from requests served by OAuth? Specifically, I need to log the request and response from OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials().
I've tried extending OwinMiddleware and overriding Invoke as shown in this post, but I'm unable to read the response body. I'd like to use a message handler as this post demonstrates, but I don't have UseHttpMessageHandler on my AppBuilder object.
Any help would be greatly appreciated.
Thank you!
Update
Modifying the example provided in Brock's excellent video, here's what I need to do:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use(typeof(MW1));
app.Map("/api", fooApp => {
fooApp.Use<MW2>();
});
}
}
public class MW1 {
Func<IDictionary<string, object>, Task> next;
public MW1(Func<IDictionary<string, object>, Task> next) {
this.next = next;
}
public async Task Invoke(IDictionary<string, object> env) {
var ctx = new OwinContext(env);
await next(env);
// I need to be able to read: <h1>MW2 called</h1> written by MW2
var body = ctx.Response.Body;
// body.CanRead = False
}
}
public class MW2 {
Func<IDictionary<string, object>, Task> next;
public MW2(Func<IDictionary<string, object>, Task> next) {
this.next = next;
}
public async Task Invoke(IDictionary<string, object> env) {
var ctx = new OwinContext(env);
await ctx.Response.WriteAsync("<h1>MW2 called</h1>");
await next(env);
}
}
I actually need to read the response sent from the OAuth provider, but I assume it would be the same process.
Why not implement an OWIN middleware component that sits in front of the OAuth AS middleware?

Resources