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, notcancel. 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).