Stored Functions
A stored function is a block🧩 of code that is saved and executed directly on the server as an API. This code allows for deeper manipulation of data, enhances security, interacts with various APIs, and also performs more complex tasks (sending emails, generating reports, verification with external systems). Written in C#, these components can accept arguments and interact with all the functionalities of Activator, further enhancing its dynamic nature and giving it the ability to handle much more complex and global tasks.
As a simple example of use, we could have a stored function that registers a new client, then sends them a welcome email, and afterward notifies the internal administration (with a certain privilege level) of this new client registration. Additionally, we could have functions that retrieve a certain number of records and return the results only to users with the required privileges. The application possibilities are truly immense.
On Activator, several components are of the stored function type (using the same documentation defined below for their operation) 📣:
- Stored Functions
- Event Handlers
- Operation Handlers
- Wizard Handlers
- System Task Handlers
- Setting validator
Create A Stored Function
In Activator Admin, the stored function is located in the menu container as shown in (1) in the image below (this scenario is the same for other storedfunction components once their menu has been localized).
✋NB: Make sure you are in the System Component main module.

After initiating the creation of the Language Ressource, by clicking on the +Add new button (illustrated at (2) on the image above), you’ll observe a form (illustrated at (3) on the image above) appearing on the right side with some fields.
🔬After filling in the name and description fields, let's focus on the Definition and Meta Data fields.
The Meta Data field gathers and provides additional information for the component. Regarding the data present in this field, we will mainly focus on the arguments
property further #down. For more information on the other properties contained in this field, please refer to Meta Data for the complete list of additional information related to this component.
As for the Definition field, let's take a closer look at its Json content in the next point.
Definition Content
This code represents the definition of a stored function in the Activator environment.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using System.Threading.Tasks;
using Asm.Activator.Core.Apis.StoredFunctions.Core;
using Asm.Activator.Core.Apis.StoredFunctions.Core.Components;
using Asm.Activator.Core.Apis.StoredFunctions.Core.Handlers;
namespace Asm.Activator.StoredFunctions
{
public class StoredFunction: ActivatorStoredFunctionBase
{
public override async Task<StoredFuctionResult> Execute()
{
var result = new StoredFuctionResult
{
Errors = new List<string>(),
Payload = string.Empty
};
try
{
//DO YOUR WORK AND SET THE PAYLOAD
// The payload can be any object
// result.Payload = "The payload";
}
catch(Exception ex)
{
//Handle exception
result.Errors.Add(ex.Message);
}
//Return the result
return result;
}
}
}
Explanation:
SECTION | DESCRIPTION |
---|---|
Method Execute() | This is the method that will be called when the stored function is executed. It is marked as asynchronous with async Task , allowing the method to be also in a non-blocking manner. |
Result initialization |
A
|
Block try | Contains the business logic of the stored function. This is where the code to be executed should be written (calculations, queries, data manipulations). If everything works, the result to be returned should be stored in result.Payload . |
Block catch | If an exception occurs during execution, it is caught, and its message is added to the result.Errors list. |
This table summarizes each part of the code and its execution logic to help you create a stored function.
✋NB: Upon each registration, a comprehensive check (syntax, logic, and semantics) is conducted. If errors are detected, they are flagged; otherwise, the save is completed.
Additionnal Info
The arguments
property in the Meta Data section of a stored function defines the parameters that are passed during the function's execution. Each argument is represented by an object containing several attributes, such as:
"arguments": [
{
"name": "recordId",
"required": true,
"dataType": "Guid"
}
]
PROPERTY | REQUIRED | DESCRIPTION |
---|---|---|
name | Yes | Name of the argument, used to reference it when the stored function is executed. |
required | Yes | Boolean value indicating whether the argument is mandatory for execution. |
dataType | Yes | Expected data type for the argument. Possible types are: guid , string , boolean , and Datatype component name such as activatord.datatypes.name |
Execute A Stored Function
PROPERTY | DESCRIPTION |
---|---|
URL |
|
Method | POST |
BODY |
|
Utilities
Activator already contains predefined functions, shortcuts, and features that help developers save time, particularly when communicating with Activator's APIs, other components, or external systems.
General
Get Function Argument Value
var argumentValue = this.Context.Arguments.GetValue("argumentName");
Get the Tenant ID
var tenantId = this.Context.TenantId;
Get the User ID
var userId = this.Context.UserId;
Execute API
string url = "url";
string payloadModel = "payload model stringified";
string result = await this.Context.ExecuteActivatorApiCall(
"put", // [get, post, delete]
"module", // [schema, tenant]
url,
payloadModel);
The Context.ExecuteActivatorApiCall
of the result
function is called📞 to execute an API request to the Activator. This method takes several parameters described in this table👇:
PROPERTY | DESCRIPTION |
---|---|
HTTP method | The first parameter specifies the HTTP method used for the API call. This can be :
|
API type | The second parameter indicates the type of API involved in the request. For a better understanding, go to Activator APIs. The value to set can be chosen between [module, schema, tenant]. |
URL | The third parameter is the URL of the API to call, defined above in the This |
payloadModel | The payloadModel variable contains the request body data as a string. This model is "stringified" before being sent to the API (for a POST or PUT request). |
Serialization
Serialization to Json
const new = { firstName: "John", lastName: "Adams" }; const jsonEmployee = JSON.stringify(new);
Deserialize from Json
var myTypeVariable = this.DeserializeObjectFromJson<Type>(jsonString);
User And Roles
Get All Roles
var activatorRoles = await this.Context.AccessControl.GetRoles();
Get Role Attributes
var attributes = await this.Context.AccessControl.GetRoleAttributes("activatord.roles.roleName");
Get Role Attribute Value
string attributeValue = await this.Context.AccessControl.GetUserRoleAttributeValue("activatord.roles.roleName", "attributName");
User Is In Role
bool check = await this.Context.AccessControl.IsUserInRole("activatord.roles.roleName");
Get Users with the attribute value
var users = await this.Context.AccessControl.GetUsersWithRoleAttributeValue("activatord.roles.roleName", "attibuteName", "attributeValue");
Get Users In A Role
var users = await this.Context.AccessControl.GetUsersInRole("activatord.roles.roleName");
System Setting
Get A Setting Value
var settingValue = await this.Context.Settings.GetValue("activatord.configurationsettings.settingName");
Entities
Create an Entity Record
var entityModelJson = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"middleName\":\"Alain\"}"; Guid recordGuid = await this.Context.Entities.CreateRecord("activatord.entities.newEntity1", entityModelJson);
Update an Entity Record
Guid recordId = Guid.Parse("ebbe2919-54d6-4891-b0ce-578a0f498000"); string path = ""; var firstNameAttr = new EntityAttributeValue { Name = "firstName", Value = "Christian" }; var lastNameAttr = new EntityAttributeValue { Name = "lastName", Value = "Amougou" }; bool success = await this.Context.Entities.UpdateRecord( "activatord.entities.newEntity1", recordId, path, new List<EntityAttributeValue> { firstNameAttr, lastNameAttr } );
Create A Child Record
Guid recordId = Guid.Parse("ebbe2919-54d6-4891-b0ce-578a0f498000"); string route = "/dependents"; var modelJson = "{\"attribute1\":\"value5555\",\"attribute2\":\"555555\"}"; Guid newRecordId = await this.Context.Entities.InsertChildRecord( "activatord.entities.newEntity1", recordId, route, modelJson );
Update Entity Child Record
Guid recordId = Guid.Parse("ebbe2919-54d6-4891-b0ce-578a0f498000"); string route = "/dependents/b2ea134b-903e-4bb7-92f6-1ade0c67d698"; var firstNameAttr = new EntityAttributeValue { Name = "firstName", Value = "Christian" }; var lastNameAttr = new EntityAttributeValue { Name = "lastName", Value = "Amougou" }; bool success = await this.Context.Entities.UpdateChildRecord( "activatord.entities.newEntity1", recordId, route, new List<EntityAttributeValue> { firstNameAttr, lastNameAttr } );
Update Entity LanguageResources
This concerns the translation of an entity's data.
await this.Context.Entities.UpdateRecordLanguageResources( "activatord.entities.entityName", recordId, path, lang_keys);
lang_keys
must be of typeList<EntityRecordLanguageResource>
(which is a native Activator model), for more info on this, go to database data translation.
Stored Queries
Execution
With no paramter
string resultJson = await this.Context.StoredQueries.Execute("activatord.storedqueries.newStoredQuery1");
With parameters
var param1 = new QueryParameter { Name = "param1", Value = "Value1" }; var param2 = new QueryParameter { Name = "param2", Value = "Value2" }; string resultJson = await this.Context.StoredQueries.Execute( "activatord.storedqueries.newStoredQuery1", new List<QueryParameter> { param1, param2 } );
Dynamic Querie
//create a new instance
var queryBuilder = this.Context.CreateDynamicQueryBuilder();
//add filter
//filter with string
queryBuilder.addFilter("_id", recordId);
//filter with boolean
var isActiveObj = new JObject();
isActiveObj["$eq"] = true;
queryBuilder.addFilter("isActive", isActiveObj);
//filter with null element
var nullElment = new JObject();
nullElment["$eq"] = null;
queryBuilder.addFilter("report", nullElment);
// Add a projection
queryBuilder.addProjection("_id", 1);
queryBuilder.addProjection("tranckingNumber", 1);
// Add look up
// Add sort
// Add limit
--------------------------------------------------------------------
// Add look up
queryBuilder.addLookup(
"sys.entities.school",
"schoolRecordId",
"_id",
"schoolInfo"
);
// Add sort
// You need to master storquries
queryBuilder.addSort("_id", -1);
queryBuilder.setLimit(500);
// Add limit
string result = await this.Context.StoredQueries.ExecuteDynamicQuery(
"Aggregation", // [selectOne, selectMany] this is the type of request that is detailed in storedQueries
"activatord.entities.entityName", //The entity where we're going to execute
queryBuilder.ToString() //instance
);
//To make a projection
var createdDate = new JObject();
createdDate["$dateToString"] = new JObject
{
["format"] = "%d/%m/%Y",
["date"] = "$createdDate"
};
queryBuilder.addProjection("createdDate", createdDate);
//Or
queryBuilder.addProjection(
"beginningDateOfService",
JObject.Parse(@"{
""$dateToString"": {
""format"": ""%m/%d/%Y %H:%M:%S"",
""date"": ""$beginningDateOfService""
}
}")
);
//This one involves implementing the library: using Newtonsoft.Json.Linq; among those provided natively by Activator
Stored Functions
With no argument
var data = await this.Context.StoredFunctions.Execute("activatord.storedfunctions.functionName");
With argument
var arg1 = new FunctionArgument
{
Name = "arg1",
Value = "value1"
};
var arg2 = new FunctionArgument
{
Name = "arg2",
Value = "value2"
};
var data = await this.Context.StoredFunctions.Execute(
"activatord.storedfunctions.functionName",
new List<FunctionArgument> { arg1, arg2 }
);
Translation
According To Active Language
string translationInTheCurrentLanguage = await this.Translate(resourceName, titleResKey, defaultTitle);
According To The Language Specified As A Parameter
string translationInFrench = await this.Translate(resourceName, "french", titleResKey, defaultTitle);
Send Email Notification
By sending content directly
Send An Email
var emailMsgModel = new EmailMessageModel { To = new List<string> { "john.doe@asmafrik.com", "sarah@hotmail.com" }, DisplayFrom = "Activator Tenant Name", Subject = "No Salutations", Body = "Bonjour <strong>Mr. John Doe</strong>,<br><br>A plus." }; bool success = await this.Context.Notifications.SendEmail(emailMsgModel);
Send An Email With file Attachments
var fileAttachments = new List<EmailMessageAttachment>(); string demoBase64Content = ""; fileAttachments.Add(new EmailMessageAttachment { FileName = "demo.pdf", FileBase64Content = demoBase64Content, ContentType = "application/pdf" }); // valid content types: // (.txt) text/plain, // (.zip) application/zip // (.doc) application/msword // (.docx) application/vnd.openxmlformatsofficedocument.wordprocessingml.document // (.xls) application/vnd.ms-excel // (.xlsx) application/vnd.openxmlformats-officedocument.spreadsheetml.sheet var emailMsgModel = new EmailMessageModel { To = new List<string> { "john.doe@asmafrik.com", "sarah@hotmail.com" }, DisplayFrom = "Activatord e-dev", Subject = "No Salutations", Body = "Bonjour <strong>Mr. John Doe</strong>,<br><br>A plus.", FileAttachments = fileAttachments }; bool success = await this.Context.Notifications.SendEmail(emailMsgModel);
Using a report as a mail Template
Send An Email
var to = new List<EmailAddressModel>(); var reportParameters = new List<EmailTemplateReportParameters>(); var fileAttachments = new List<EmailMessageAttachment>(); to.Add(new EmailAddressModel { Display = "John Doe", EmailAddress = "john.doe@example.com" }); reportParameters.Add(new EmailTemplateReportParameters { ReportName = "activatord.reports.reportName", Parameters = new List<ReportParameter> { new ReportParameter { Name = "Param Name", Value = "Param Value" } } }); bool result = await context.Notifications.SendEmailFromTemplate( "activatord.emailtemplates.emailTemplateName", to, null, null, reportParameters, fileAttachments );
Send An Email With file Attachments
fileAttachments.Add( new EmailMessageAttachment { FileName = "FileName.pdf", FileBase64Content = base64EncodedContent } );
Reports
Execute with no parameters
var base64Content = await this.Context.Reports.Execute("activatord.reports.reportName", "pdf");
Execute with parameters
var param1 = new ReportParameter { Name = "param1", Value = "Value1" }; var param2 = new ReportParameter { Name = "param2", Value = "Value2" }; var base64Content = await this.Context.Reports.Execute( "activatord.reports.reportName", "pdf", new List<ReportParameter> { param1, param2 } );
Zip
Modules to be imported after the default ones:
using Newtonsoft.Json.Converters;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
using System.IO;
using Newtonsoft.Json.Linq;
The code to implement:
string zipFileBase64Content = this.CreateZipFile(
new List<ZipFileEntry>
{
new ZipFileEntry
{
FileName = "file1.pdf",
Base64Content = file1Base64FileContent
},
new ZipFileEntry
{
FileName = "file2.pdf",
Base64Content = file2Base64FileContent
},
new ZipFileEntry
{
FileName = "file3.pdf",
Base64Content = file3Base64FileContent
}
}
);
Conclusion
Stored functions provide a robust approach for executing complex tasks directly at the server side, ensuring better integration and enhanced performance. By centralizing critical operations and enabling rapid process execution, they simplify data management while minimizing the need for client-side processing. Their ability to handle various types of arguments and integrate seamlessly into API workflows demonstrates their value in designing efficient and scalable systems. In summary, stored functions are a valuable tool for optimizing data processing operations and enhancing application consistency.