Relatude Help

Commands

Command(type, handler, readOnly) adds a command to the CMS API (chapter 17). It runs as the signed-in user: ctx.Db is the store with the user's read access, and ctx.Root is the whole store, for checks the server makes itself.

public static class EventCommands {
    public static object? Upcoming(CmsApiContext ctx) {
        var now = DateTime.UtcNow;
        var events = ctx.Db.Query<IEventPage>().Where(e => e.EndsUtc >= now).OrderBy(e => e.StartsUtc).Execute();
        return new { events = events.Select(e => new { id = e.Id, name = e.Name, startsUtc = e.StartsUtc }) };
    }

    sealed record CancelPayload(Guid Id);

    public static object? Cancel(CmsApiContext ctx) {
        if (!ctx.IsInGroup(EventsModule.Managers))                     // a direct member of the group
            throw new CmsApiException(403, "Only event managers may cancel an event.");
        var id = ctx.Payload<CancelPayload>().Id;                      // from camelCase JSON: { id }
        var e = ctx.Db.Query<IEventPage>().Where(id).Execute().FirstOrDefault()   // a query: Get ignores read access
            ?? throw new CmsApiException(404, "There is no such event.");
        e.Status = EventStatus.Cancelled;
        ctx.Db.Update(e);
        ctx.Changed(e.Id, e.Meta.NodeTypeId, CmsApiChangeKind.Updated);  // tells the open CMS interfaces
        return new { cancelled = true };
    }
}
  • Commands and jobs are named after their module: events.cancel, not cancel. Any other name throws when the module is added, so no module can replace another's commands or the API's own.
  • A read-only command (readOnly: true) changes nothing. It is the only kind an anonymous caller may run, when the API allows anonymous callers.
  • While the module is not active, its commands answer 403 with the reason.

The CMS interface sends a command with cms.store.send('events.cancel', { id }) (Commands of your own).