jamesmh / coravel

Near-zero config .NET library that makes advanced application features like Task Scheduling, Caching, Queuing, Event Broadcasting, and more a breeze!
https://docs.coravel.net/Installation/
MIT License
3.63k stars 243 forks source link

If you want to queue a job from a webpage #368

Closed carsjo closed 2 months ago

carsjo commented 3 months ago

I ran in to issues queueing IInvocable and ICancellableTask from a webpage where the user could select which task to queue.

This PR solves this issue, provided example uses EPiServer / Optimizely

 public interface IExecutableJob : IInvocable, ICancellableTask
{
    string Name { get; }
    string? FullName { get; }
    string Description { get; }
    Task OnStatusChange(string? userName, string message);
}
public abstract class BaseExecutableJob : IExecutableJob
{
    private static readonly IDispatcher Dispatcher = ServiceLocator.Current.GetInstance<IDispatcher>();

    private Type Type => GetType().DeclaringType ?? GetType();
    public string Name => Type.Name;
    public string? FullName => Type.FullName;
    public abstract Task Invoke();
    public abstract string Description { get; }
    public Task OnStatusChange(string? userName, string message) => Dispatcher.Broadcast(new ExecutableJobEvent(userName, Name, message));
    public CancellationToken Token { get; set; } = CancellationToken.None;
}
public static IServiceCollection AddExecutableJobs(this IServiceCollection services)
{
    services.AddQueue().AddEvents();
    services.AddScoped<ExecutableJobEventListener>();

    var interfaceType = typeof(IExecutableJob);

    var executableJobList = typeof(ContentExtensions).Assembly.GetTypes()
        .Where(x => interfaceType.IsAssignableFrom(x) && x is { IsAbstract: false, IsClass: true })
        .ToList();

    foreach (var implementationType in executableJobList)
    {
        services.Add(new ServiceDescriptor(interfaceType, implementationType, ServiceLifetime.Singleton));
    }

    return services;
}
[Route("api/[controller]/[action]")]
[Authorize(Policy = SiteConstants.WebAdminPolicy)]
public class AdminMenuApiController : BaseAdminApiController
{
    private readonly IQueue _queue;
    private readonly IEnumerable<IExecutableJob> _executableJobs;

        private (Guid jobId, CancellationTokenSource? cancellationTokenSource) _runningJob =
            new(Guid.Empty, null);

    [HttpPost]
    [Produces(typeof(JobMessage))]
    public IActionResult ExecuteJob([FromQuery] string fullName)
    {
        try
        {
            var executableJob = _executableJobs.FirstOrDefault(x => x.FullName == fullName);

            if (executableJob is null)
            {
                return Problem(title: $"Job with name {fullName} not found!");
            }

            _runningJob = _queue.QueueCancellableInvocable(executableJob);

            return Ok(new JobMessage { Message = $"Job {executableJob.Name} was queued", JobId = jobId });
        }
        catch (Exception e)
        {
            return Problem(title: e.ToString());
        }
    }
}
carsjo commented 2 months ago

Ignore this. I found your examples of a better way.