Scheduler
-
Scheduler programs are executed based on CRON Expression.
-
They are executed asynchronously
-
A cron expression is a string used to represent a schedule for running a command or job automatically at specific times or intervals.
-
A cron expression define the time and frequency at which a task should run. e.g. for Every day at 3:15 PM cron expression is 15 15 * * *
-
scheduled program is a piece of code that runs automatically at defined intervals or on specific events. In the context of business applications, scheduled programs are used to automate routine tasks, like processing records, sending notifications, or updating the instance based on certain conditions
Coding guidelines
-
Scheduler program is a class that must implement the interface
ISchedulerProgramAsyncpublic interface ISchedulerProgramAsync
{
Task RunAsync();
}NOTE: Implement ISchedulerProgramAsync with the RunAsync method to enable asynchronous, scalable trigger logic. Most internal service methods are now async, and their synchronous versions are deprecated and will be removed. Use
RunAsyncto ensure compatibility with these changes.
Typical use cases for Scheduler program
- Scheduler automatically manages the execution of tasks, scripts, or programs at specific times or intervals.
- This kind of scheduled program is typically used in environments where business rules dictate specific actions (e.g., moving a record into a new state) after a certain amount of time, ensuring that business processes are followed without constant manual oversight.
Sample Code
using Prorigo.Protrak.API.Contracts;
using Prorigo.Protrak.API.Services;
using Prorigo.Protrak.Programs;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Customization.Programs
{
public class AutoHoldAgreementsAfterOneYear : ISchedulerProgramAsync
{
public IInstanceService InstanceService { get; set; }
private readonly string TYPE_AGREEMENT = "Agreement";
private readonly string ATTRIBUTE_AGREEMENT_REQUESTED_DATE = "AgreementRequestedDate";
private readonly string ATTRIBUTE_CREATED_DATE = "Created";
private readonly string ATTRIBUTE_REACTIVATED_DATE = "ReactivatedDate";
private readonly string STATE_DRAFT_AGREEMENT = "Draft Agreement";
private readonly string STATE_IP_LEGAL_REVIEW = "IP Legal Review";
private readonly string STATE_IISC_LEGAL_REVIEW = "IISc Legal Review";
private readonly string STATE_COMPANY_REVIEW = "Company Review";
private readonly string ACTION_NAME = "On Hold";
private readonly string COMMENTS = "System has autopromoted the agreement to 'On Hold' state.";
public async Task RunAsync()
{
var agreementInstanceQuery = new InstanceQuery
{
InstanceTypeName = TYPE_AGREEMENT,
Skip = 0,
Take = int.MaxValue,
Attributes = new string[] { ATTRIBUTE_AGREEMENT_REQUESTED_DATE, ATTRIBUTE_CREATED_DATE, ATTRIBUTE_REACTIVATED_DATE },
StateFilter = new string[] { STATE_DRAFT_AGREEMENT, STATE_IP_LEGAL_REVIEW, STATE_IISC_LEGAL_REVIEW, STATE_COMPANY_REVIEW },
};
var agreementInstances = await InstanceService.GetInstancesAsync(agreementInstanceQuery);
if (agreementInstances != null && agreementInstances.TotalCount > 0)
{
foreach (var agreement in agreementInstances.Items)
{
var attributeName = agreement.State.Name == STATE_DRAFT_AGREEMENT
? ATTRIBUTE_CREATED_DATE
: ATTRIBUTE_AGREEMENT_REQUESTED_DATE;
var dateAttr = agreement.Attributes.FirstOrDefault(a => a.Name == attributeName);
if (dateAttr != null && dateAttr.DateValue != null)
{
var isOneYearPassed = dateAttr.DateValue.Value.Date.AddDays(364) <= DateTime.UtcNow.Date;
var reactivatedDate = agreement.Attributes.FirstOrDefault(a => a.Name == ATTRIBUTE_REACTIVATED_DATE);
var isOneYearPassedAfterReactivation = true;
if (reactivatedDate != null && reactivatedDate.DateValue != null)
{
isOneYearPassedAfterReactivation = reactivatedDate.DateValue.Value.Date.AddDays(364) <= DateTime.UtcNow.Date;
}
if (isOneYearPassed && isOneYearPassedAfterReactivation)
{
await InstanceService.PromoteInstanceAsync(agreement.Id, ACTION_NAME, COMMENTS);
}
}
}
}
}
}
}
Note:
The Run method (from ISchedulerProgram) should only be used if the trigger logic is fully synchronous, does not involve any service/API calls, and consists only lightweight in-memory operations.