Implementing Operations in Bloqs

Operations in Bloqs are non-CRUD actions that can be executed within the system. Unlike standard Create, Read, Update, Delete actions, an operation represents a custom task or workflow you want to run.

  • Operations have an input entity that is sent to the API.

  • They can only be implemented on the API side.

  • The UI can trigger operations via a dedicated page.


Declaring an Operation in the Model

To add an operation, first declare its input class and register the operation in your app model.

.AddEntityClass<SendMailInput>()
.AddOperation(new Operation()
{
    Id = AppConstants.Operations.SendMail,
    Name = AppConstants.Operations.SendMailName,
})

Input Entity Class

using Bloqs.App.Engine.Entities;

namespace Templates.SampleApp.Models.Operations;

public class SendMailInput : Entity
{
    public required string MailAddress { get; set; }
    public required string Message { get; set; }
}

This entity defines the input required for the operation — in this case, an email address and a message body.


Implementing the Operation in the API

On the API side, you implement the operation by creating an Operation Command Handler.

using System.Net;
using System.Net.Mail;
using Bloqs.Api.Engine.DataAccess;
using Bloqs.App.Engine.Commands;
using Bloqs.App.Engine.Commands.Operation;
using Templates.SampleApp.Models;
using Templates.SampleApp.Models.Data;
using Templates.SampleApp.Models.Operations;

namespace Templates.SampleApp.Api.Commands.Operations;

[OperationCommandHandler(AppConstants.Operations.SendMailName)]
public class SendMail(IDataServiceCreator dataServiceCreator)
    : ICommandHandler<OperationCommand, OperationCommandResult>
{
    private const string SmtpUser = "tomv@noost.nl";
    private const string SmtpPassword = "<insert gmail pwd>"; // Use the 16-character App Password
    private const string SmtpHost = "smtp.gmail.com";
    private const int SmtpPort = 587;

    public async Task<OperationCommandResult> HandleAsync(
        OperationCommand command,
        CancellationToken cancellationToken = default
    )
    {
        try
        {
            var dataService = dataServiceCreator.GetDataService<Activity>(
                DataServiceCreatorArgs.FromCommand<Activity>(command)
            );

            var input =
                command.InputEntity as SendMailInput
                ?? throw new Exception("Invalid input entity for SendMail operation");

            var activity = await dataService.GetAsync(input.ActivityId);

            var fromEmail = SmtpUser;
            var toEmail = input.MailAddress;
            var subject = "Email from Sample App: " + activity.Name;
            var body = input.Message;

            var mail = new MailMessage(fromEmail, toEmail, subject, body);

            var smtp = new SmtpClient(SmtpHost, SmtpPort)
            {
                Credentials = new NetworkCredential(SmtpUser, SmtpPassword),
                EnableSsl = true,
            };

            smtp.Send(mail);

            Console.WriteLine("Email sent successfully.");

            return OperationCommandResult.CreateSuccess(null);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error sending email: {ex.Message}");

            return OperationCommandResult.CreateFailure();
        }
    }
}

This handler takes the SendMailInput object and performs the actual email sending logic.

Last updated