Files
fpc-cron/docs/API.md

305 lines
8.5 KiB
Markdown

# fpc-cron — API reference
Every callable the library exposes, with parameters, return
values, and runnable examples. See
[`architecture.md`](architecture.md) for the big picture and
[`DEVELOPER_GUIDE.md`](DEVELOPER_GUIDE.md) for the
consumer-oriented walkthrough.
## Contents
- [Quick start](#quick-start)
- [Types (`cron.types`)](#types)
- [Events (`cron.events`)](#events)
- [Schema specs (`cron.runner`)](#schema-specs)
- [TCron](#tcron)
- [Create](#tcroncreate)
- [Destroy](#tcrondestroy)
- [RegisterSystemTask](#tcronregistersystemtask)
- [RefreshTasks](#tcronrefreshtasks)
- [RunTaskNow](#tcronruntasknow)
- [GetTasksJSON / GetTaskJSON](#tcrongettasksjson)
- [UpdateTask](#tcronupdatetask)
- [Running (property)](#tcronrunning)
- [OnTaskStart / OnTaskComplete / OnTaskRegistered / OnPluginOrphaned / OnThreadStart / OnThreadStop](#tcronon)
- [ParseCronField (class function)](#tcronparsecronfield)
- [MatchesCron (class function)](#tcronmatchescron)
- [Version constants](#version-constants)
---
## Quick start
```pascal
uses
Classes, SysUtils, fpjson, DateUtils,
log.types,
database.types, database.pool,
cron.types, cron.events, cron.runner;
var
Pool: TDBPool;
C: TCron;
begin
Pool := TDBPool.Create;
Pool.Init(dbSQLite, '/tmp/cron.sqlite3');
try
C := TCron.Create(Pool, @MyHost.RunTask, nil, @MyHost.HandleLog);
try
C.OnTaskComplete := @MyHost.HandleTaskComplete;
C.RegisterSystemTask('cleanup', @MyHost.RunCleanup);
C.Start;
...
C.Terminate;
finally
C.Free;
end;
finally
Pool.Free;
end;
end.
```
## Types
`cron.types`:
```pascal
type
TCronTaskKind = (stkInterval, stkCron);
TCronTask = record
ID: Integer;
TaskName: string;
PluginName: string;
Description: string;
Category: string;
Kind: TCronTaskKind;
IntervalSeconds: Integer;
CronExpr: string;
Enabled: Boolean;
LastRun: TDateTime;
NextRun: TDateTime;
LastResult: string;
LastError: string;
RunCount: Integer;
FailCount: Integer;
UserModified: Boolean;
IsRunning: Boolean;
end;
TRunTaskProc = procedure(const APluginName, ATaskName: string) of object;
TGetExtraTasksFunc = function: TJSONArray of object;
TSystemTaskProc = procedure(const ATaskName: string) of object;
```
The logger type is `log.types.TLogProc`, defined in fpc-log.
## Events
`cron.events` defines the typed observer callbacks (same pattern
as fpc-binkp's `bp.events` and fpc-comet's `cm.events`):
```pascal
type
TCronOnTaskStart = procedure(const APluginName, ATaskName: string) of object;
TCronOnTaskComplete = procedure(const APluginName, ATaskName: string;
ASuccess: Boolean; ADurationMs: Integer;
const AError: string) of object;
TCronOnTaskRegistered = procedure(const APluginName, ATaskName: string;
AIntervalSeconds: Integer) of object;
TCronOnPluginOrphaned = procedure(const APluginName: string) of object;
TCronOnThreadStart = procedure of object;
TCronOnThreadStop = procedure of object;
```
All callbacks are `of object`. `nil` is always valid (no-op).
## Schema specs
```pascal
function BuildSystemSchedulerSpec(const ANowExpr: string): TDBTable;
function BuildSchedulerLogSpec: TDBTable;
```
Return fpc-db `TDBTable` specs for the two runner-owned tables.
Use these to declare schema independently of `TCron.Create`:
```pascal
APool.DeclareTable(BuildSystemSchedulerSpec(APool.Dialect.NowExpr));
APool.DeclareTable(BuildSchedulerLogSpec);
```
The names mirror canonical Fastway `fw_schema.pas`'s
`BuildSystemScheduler` / `BuildSchedulerLog` so a Fastway
database is reusable without migration.
## TCron
`TCron = class(TThread)`. Created suspended; caller calls
`.Start` to begin the wake loop.
### `TCron.Create`
```pascal
constructor Create(APool: TDBPool;
ARunTask: TRunTaskProc = nil;
AGetExtraTasks: TGetExtraTasksFunc = nil;
ALogger: TLogProc = nil);
```
- `APool` — required. fpc-db pool the runner reads/writes its
two tables on. Raises if `nil`.
- `ARunTask` — invoked for every non-system task. `nil` means
non-system tasks fail with `'No plugin task runner available'`.
- `AGetExtraTasks` — invoked at `Create` and on `RefreshTasks`
to discover plugin tasks. `nil` means
`SyncPluginTasks` is a no-op.
- `ALogger``log.types.TLogProc`. `nil` means silent.
`Create` calls `Pool.DeclareTable` for both schema specs, runs
`LoadTasksFromDB`, and runs `SyncPluginTasks`. Returns a
*suspended* thread — caller must call `.Start`.
### `TCron.Destroy`
If the wake loop is running, `Destroy` calls `Terminate`,
fires the stop event, and `WaitFor`s the thread. Then frees
the lock and the stop event. Safe to call from any thread.
### `TCron.RegisterSystemTask`
```pascal
procedure RegisterSystemTask(const AName: string; AProc: TSystemTaskProc);
```
Register a callback for a `system/<AName>` task. Calling twice
with the same name replaces the previous entry. Replaces
canonical Fastway's hardcoded `case ATaskName of` table.
If a `plugin_name='system'` task fires whose name isn't
registered, the runner logs `'unknown system task: <name>'` at
`llWarn` and moves on.
### `TCron.RefreshTasks`
```pascal
procedure RefreshTasks;
```
Reload from the DB and re-run `SyncPluginTasks`. Use after
inserting tasks externally, or after the supplier callback's
return value has changed. Recomputes `NextRun` for every
enabled task.
### `TCron.RunTaskNow`
```pascal
procedure RunTaskNow(ATaskID: Integer);
```
Synchronously run one task by row id. Updates `LastRun`,
`RunCount`, `FailCount`, `NextRun`, writes a `scheduler_log`
row, fires `OnTaskStart` + `OnTaskComplete`. Useful for "run
now" buttons in admin UIs.
### `TCron.GetTasksJSON`
```pascal
function GetTasksJSON: TJSONArray;
function GetTaskJSON(ATaskID: Integer): TJSONObject;
```
Snapshot the in-memory task table. `GetTaskJSON` returns `nil`
if the task ID isn't found. Caller owns the returned objects
(must `Free` them). All `last_run` / `next_run` timestamps are
formatted as `'yyyy-mm-dd hh:nn:ss'` (UTC); null when unset.
### `TCron.UpdateTask`
```pascal
function UpdateTask(ATaskID: Integer; AUpdates: TJSONObject): Boolean;
```
Apply mutations to an existing task and persist to DB.
Recognised keys in `AUpdates`:
- `enabled` (Boolean)
- `schedule_type` (`'interval'` or `'cron'`)
- `interval_seconds` (Integer, must be > 0)
- `cron_expr` (string)
Sets `user_modified = 1` (which exempts the row from
`SyncPluginTasks` orphan cleanup). Recomputes `NextRun`.
Returns `True` if the task was found, `False` otherwise.
### `TCron.Running`
```pascal
property Running: Boolean read FRunning;
```
`True` between `Execute` start and stop.
### `TCron.OnTaskStart` etc.
```pascal
property OnTaskStart: TCronOnTaskStart ...;
property OnTaskComplete: TCronOnTaskComplete ...;
property OnTaskRegistered: TCronOnTaskRegistered ...;
property OnPluginOrphaned: TCronOnPluginOrphaned ...;
property OnThreadStart: TCronOnThreadStart ...;
property OnThreadStop: TCronOnThreadStop ...;
```
Wire by assignment (`C.OnTaskStart := @Host.HandleStart`).
Each callback is invoked synchronously on the runner thread.
`nil` (the default) means no-op.
### `TCron.ParseCronField`
```pascal
class function ParseCronField(const AField: string;
AMin, AMax: Integer): TBits;
```
Parse one cron field (`*`, `*/N`, `a,b,c`, `a-b`, `a-b/N`,
or a single literal). Returns a `TBits` of size `AMax+1`
with the matched values set. Caller frees the result.
Out-of-range literals are silently dropped.
### `TCron.MatchesCron`
```pascal
class function MatchesCron(const ACronExpr: string;
ATime: TDateTime): Boolean;
```
Test whether a 5-field cron expression matches the given
`TDateTime`. Returns `False` if `ACronExpr` has fewer than 5
fields after splitting on whitespace.
`ATime` is treated as local time (canonical Fastway behaviour);
the canonical's `next_run` math converts to UTC after the match.
## Version constants
In `cron.version`:
```pascal
const
CRON_VERSION_MAJOR = 0;
CRON_VERSION_MINOR = 1;
CRON_VERSION_PATCH = 0;
CRON_VERSION_STRING = '0.1.0';
```
Bumped together with the git tag. Pin downstream consumers by
tag, not commit hash.