Skip to content

Commit

Permalink
Adds remaining content item triggers #82
Browse files Browse the repository at this point in the history
  • Loading branch information
apexdodge committed Apr 28, 2024
1 parent 5dc4d07 commit 8585683
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Raytha.Application.Common.Exceptions;
using Raytha.Application.Common.Interfaces;
using Raytha.Domain.Entities;
using System.Text.Json;

namespace Raytha.Application.Common.Shared;

public class RaythaFunctionAsBackgroundTaskPayload
{
public dynamic Target { get; init; }
public RaythaFunction RaythaFunction { get; init; }
}

public class RaythaFunctionAsBackgroundTask : IExecuteBackgroundTask
{
private readonly IRaythaDbContext _entityFrameworkDb;
private readonly IRaythaFunctionConfiguration _raythaFunctionConfiguration;
private readonly IRaythaFunctionScriptEngine _raythaFunctionScriptEngine;
private readonly IRaythaFunctionSemaphore _raythaFunctionSemaphore;

public RaythaFunctionAsBackgroundTask(
IRaythaFunctionConfiguration raythaFunctionConfiguration,
IRaythaFunctionSemaphore raythaFunctionSemaphore,
IRaythaFunctionScriptEngine raythaFunctionScriptEngine,
IRaythaDbContext entityFrameworkDb)
{
_raythaFunctionConfiguration = raythaFunctionConfiguration;
_raythaFunctionSemaphore = raythaFunctionSemaphore;
_raythaFunctionScriptEngine = raythaFunctionScriptEngine;
_entityFrameworkDb = entityFrameworkDb;
}

public async Task Execute(Guid jobId, JsonElement args, CancellationToken cancellationToken)
{
string? raythaFunctionName = args.GetProperty("RaythaFunction").GetProperty("Name").GetString();
string? code = args.GetProperty("RaythaFunction").GetProperty("Code").GetString();

var job = _entityFrameworkDb.BackgroundTasks.First(p => p.Id == jobId);
job.TaskStep = 1;
job.StatusInfo = $"Running Raytha Function";
job.PercentComplete = 0;
_entityFrameworkDb.BackgroundTasks.Update(job);
await _entityFrameworkDb.SaveChangesAsync(cancellationToken);

if (await _raythaFunctionSemaphore.WaitAsync(_raythaFunctionConfiguration.QueueTimeout, cancellationToken))
{
try
{
string payload = JsonSerializer.Serialize(args.GetProperty("Target"));
await _raythaFunctionScriptEngine.EvaluateRun(code, payload, _raythaFunctionConfiguration.ExecuteTimeout, cancellationToken);
job.TaskStep = 2;
job.StatusInfo = $"Completed Raytha Function: {raythaFunctionName}";
job.PercentComplete = 100;
}
catch (Exception exception) when(exception is RaythaFunctionExecuteTimeoutException or RaythaFunctionScriptException)
{
job.StatusInfo = $"Error running Raytha Function {raythaFunctionName} - {exception.Message}";
}
finally
{
job.TaskStep = 2;
job.PercentComplete = 100;
_entityFrameworkDb.BackgroundTasks.Update(job);
await _entityFrameworkDb.SaveChangesAsync(cancellationToken);
_raythaFunctionSemaphore.Release();
}
}
else
{
job.TaskStep = 2;
job.StatusInfo = $"Raytha Function {raythaFunctionName} failed to run because too many background tasks are running";
job.PercentComplete = 100;
_entityFrameworkDb.BackgroundTasks.Update(job);
await _entityFrameworkDb.SaveChangesAsync(cancellationToken);
}
}
}
4 changes: 3 additions & 1 deletion src/Raytha.Application/ConfigureServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using Raytha.Application.Common.Behaviors;
using Raytha.Application.Common.Shared;
using Raytha.Application.ContentItems;
using Raytha.Application.ContentItems.Commands;
using Raytha.Application.ContentItems.EventHandlers;
using System.Reflection;
using static Raytha.Application.ContentItems.EventHandlers.ContentItemCreatedEventHandler;

namespace Raytha.Application;

Expand All @@ -23,7 +25,7 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
});
services.AddScoped<BeginExportContentItemsToCsv.BackgroundTask>();
services.AddScoped<BeginImportContentItemsFromCsv.BackgroundTask>();
services.AddScoped<ContentItemCreatedEventHandler.BackgroundTask>();
services.AddTransient<RaythaFunctionAsBackgroundTask>();
services.AddScoped<FieldValueConverter>();
return services;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Raytha.Application.Common.Utils;
using Raytha.Domain.Entities;
using Raytha.Domain.Events;
using System.Text.Json;

namespace Raytha.Application.ContentItems.Commands;

Expand Down Expand Up @@ -75,7 +76,12 @@ public Handler(IRaythaDbContext db)
}
public async Task<CommandResponseDto<ShortGuid>> Handle(Command request, CancellationToken cancellationToken)
{
var entity = _db.ContentItems.First(p => p.Id == request.Id.Guid);
var entity = _db.ContentItems
.Include(p => p.CreatorUser)
.Include(p => p.LastModifierUser)
.Include(p => p.WebTemplate)
.Include(p => p.ContentType)
.Include(p => p.Route).First(p => p.Id == request.Id.Guid);

if (request.SaveAsDraft)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Raytha.Application.Common.Interfaces;
using Raytha.Application.Common.Models;
using Raytha.Application.Common.Utils;
using Raytha.Domain.Events;

namespace Raytha.Application.ContentItems.Commands;

Expand Down Expand Up @@ -94,6 +95,7 @@ public async Task<CommandResponseDto<ShortGuid>> Handle(Command request, Cancell

entity.WebTemplateId = request.TemplateId;
entity.Route.Path = request.RoutePath.ToUrlSlug();
entity.AddDomainEvent(new ContentItemUpdatedEvent(entity));
await _db.SaveChangesAsync(cancellationToken);

return new CommandResponseDto<ShortGuid>(entity.Id);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using MediatR;
using Raytha.Application.Common.Exceptions;
using Raytha.Application.Common.Interfaces;
using Raytha.Application.Common.Shared;
using Raytha.Domain.Entities;
using Raytha.Domain.Events;
using Raytha.Domain.ValueObjects;
Expand Down Expand Up @@ -28,83 +29,12 @@ public async Task Handle(ContentItemCreatedEvent notification, CancellationToken
{
foreach (var activeFunction in activeFunctions)
{
await _taskQueue.EnqueueAsync<BackgroundTask>(new ContentItemAndActiveFunction
await _taskQueue.EnqueueAsync<RaythaFunctionAsBackgroundTask>(new RaythaFunctionAsBackgroundTaskPayload
{
Event = ContentItemDto.GetProjection(notification.ContentItem),
Target = ContentItemDto.GetProjection(notification.ContentItem),
RaythaFunction = activeFunction
}, cancellationToken);
}
}
}

public class ContentItemAndActiveFunction
{
public required ContentItemDto Event { get; init; }
public required RaythaFunction RaythaFunction { get; init; }
}

public class BackgroundTask : IExecuteBackgroundTask
{
private readonly IRaythaDbContext _entityFrameworkDb;
private readonly IRaythaFunctionConfiguration _raythaFunctionConfiguration;
private readonly IRaythaFunctionScriptEngine _raythaFunctionScriptEngine;
private readonly IRaythaFunctionSemaphore _raythaFunctionSemaphore;

public BackgroundTask(
IRaythaFunctionConfiguration raythaFunctionConfiguration,
IRaythaFunctionSemaphore raythaFunctionSemaphore,
IRaythaFunctionScriptEngine raythaFunctionScriptEngine,
IRaythaDbContext entityFrameworkDb)
{
_raythaFunctionConfiguration = raythaFunctionConfiguration;
_raythaFunctionSemaphore = raythaFunctionSemaphore;
_raythaFunctionScriptEngine = raythaFunctionScriptEngine;
_entityFrameworkDb = entityFrameworkDb;
}

public async Task Execute(Guid jobId, JsonElement args, CancellationToken cancellationToken)
{
string? raythaFunctionName = args.GetProperty("RaythaFunction").GetProperty("Name").GetString();
string? code = args.GetProperty("RaythaFunction").GetProperty("Code").GetString();

var job = _entityFrameworkDb.BackgroundTasks.First(p => p.Id == jobId);
job.TaskStep = 1;
job.StatusInfo = $"Running Raytha Function";
job.PercentComplete = 0;
_entityFrameworkDb.BackgroundTasks.Update(job);
await _entityFrameworkDb.SaveChangesAsync(cancellationToken);

if (await _raythaFunctionSemaphore.WaitAsync(_raythaFunctionConfiguration.QueueTimeout, cancellationToken))
{
try
{
string payload = JsonSerializer.Serialize(args.GetProperty("Event"));
await _raythaFunctionScriptEngine.EvaluateRun(code, payload, _raythaFunctionConfiguration.ExecuteTimeout, cancellationToken);
job.TaskStep = 2;
job.StatusInfo = $"Completed Raytha Function: {raythaFunctionName}";
job.PercentComplete = 100;
}
catch (Exception exception) when(exception is RaythaFunctionExecuteTimeoutException or RaythaFunctionScriptException)
{
job.StatusInfo = $"Error running Raytha Function {raythaFunctionName} - {exception.Message}";
}
finally
{
job.TaskStep = 2;
job.PercentComplete = 100;
_entityFrameworkDb.BackgroundTasks.Update(job);
await _entityFrameworkDb.SaveChangesAsync(cancellationToken);
_raythaFunctionSemaphore.Release();
}
}
else
{
job.TaskStep = 2;
job.StatusInfo = $"Raytha Function {raythaFunctionName} failed to run because too many background tasks are running";
job.PercentComplete = 100;
_entityFrameworkDb.BackgroundTasks.Update(job);
await _entityFrameworkDb.SaveChangesAsync(cancellationToken);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using MediatR;
using Raytha.Application.Common.Interfaces;
using Raytha.Application.Common.Shared;
using Raytha.Domain.Events;
using Raytha.Domain.ValueObjects;

namespace Raytha.Application.ContentItems.EventHandlers;

public class ContentItemDeletedEventHandler : INotificationHandler<ContentItemDeletedEvent>
{
private readonly IBackgroundTaskQueue _taskQueue;
private readonly IRaythaDbContext _db;

public ContentItemDeletedEventHandler(
IBackgroundTaskQueue taskQueue,
IRaythaDbContext db)
{
_taskQueue = taskQueue;
_db = db;
}

public async Task Handle(ContentItemDeletedEvent notification, CancellationToken cancellationToken)
{
var activeFunctions = _db.RaythaFunctions.Where(p => p.IsActive && p.TriggerType == RaythaFunctionTriggerType.ContentItemDeleted.DeveloperName);
if (activeFunctions.Any())
{
foreach (var activeFunction in activeFunctions)
{
await _taskQueue.EnqueueAsync<RaythaFunctionAsBackgroundTask>(new RaythaFunctionAsBackgroundTaskPayload
{
Target = ContentItemDto.GetProjection(notification.ContentItem),
RaythaFunction = activeFunction
}, cancellationToken);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using MediatR;
using Raytha.Application.Common.Interfaces;
using Raytha.Application.Common.Shared;
using Raytha.Domain.Events;
using Raytha.Domain.ValueObjects;

namespace Raytha.Application.ContentItems.EventHandlers;

public class ContentItemUpdatedEventHandler : INotificationHandler<ContentItemUpdatedEvent>
{
private readonly IBackgroundTaskQueue _taskQueue;
private readonly IRaythaDbContext _db;

public ContentItemUpdatedEventHandler(
IBackgroundTaskQueue taskQueue,
IRaythaDbContext db)
{
_taskQueue = taskQueue;
_db = db;
}

public async Task Handle(ContentItemUpdatedEvent notification, CancellationToken cancellationToken)
{
var activeFunctions = _db.RaythaFunctions.Where(p => p.IsActive && p.TriggerType == RaythaFunctionTriggerType.ContentItemUpdated.DeveloperName);
if (activeFunctions.Any())
{
foreach (var activeFunction in activeFunctions)
{
await _taskQueue.EnqueueAsync<RaythaFunctionAsBackgroundTask>(new RaythaFunctionAsBackgroundTaskPayload
{
Target = ContentItemDto.GetProjection(notification.ContentItem),
RaythaFunction = activeFunction
}, cancellationToken);
}
}
}
}
17 changes: 14 additions & 3 deletions src/Raytha.Web/Areas/Admin/Views/RaythaFunctions/ViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ public class RaythaFunctionsCreate_ViewModel : FormSubmit_ViewModel
*/
/**
* For Trigger Type: Http trigger
* Receives a get request at /raytha/functions/execute/{developerName}
* @param {IQueryCollection} query Passed in from .NET's Request.Query
* @param {IQueryCollection} query passed in from .NET's Request.Query
* @returns {object} of type JsonResult, HtmlResult, RedirectResult, or StatusCodeResult
*/
function get(query) {
Expand All @@ -65,16 +66,26 @@ public class RaythaFunctionsCreate_ViewModel : FormSubmit_ViewModel
}
/**
* For Trigger Type: Http trigger
* Receives a post request at /raytha/functions/execute/{developerName}
* @param {IFormCollection} payload Passed in from .NET's Request.Form
* @param {IQueryCollection} query Passed in from .NET's Request.Query
* @param {IFormCollection} payload passed in from .NET's Request.Form
* @param {IQueryCollection} query passed in from .NET's Request.Query
* @returns {object} of type JsonResult, HtmlResult, RedirectResult, or StatusCodeResult
*/
function post(payload, query) {
return new JsonResult({ success: true });
//example 1: return new HtmlResult(""<p>Hello World</p>"");
//example 2: return new RedirectResult(""https://raytha.com"");
//example 3: return new StatusCodeResult(404, ""Not Found"");
}
/**
* For Trigger Type: Content item created, updated, deleted
* @param {ContentItemDto} payload passed in from system
* @returns {void}, no return type
*/
function run(payload) {
//example: HttpClient.Post(""https://your-endpoint.com"", headers=null, body=payload);
}";
}

Expand Down

0 comments on commit 8585683

Please sign in to comment.