Skip to content

Kitto.Web.Data.Service

Shared CRUD service layer used by both the HTML/HTMX GUI and the REST API. It resolves a view, enforces ACL, loads stores/records, and applies the full business-rule sequence around persistence (Apply*RecordRules + per-field AfterFieldChange + ApplyBeforeRules + Model.SaveRecord), exactly as the interactive GUI does — so a record created/updated over REST behaves identically to one edited in a form (computed fields, reference captions, master totals, validation).

This unit is part of the core and has NO dependency on any JSON framework or on HTTP/HTML: values are applied through the caller-supplied AApplyValues callback (the REST serializer injects JSON values there), and errors are surfaced as EKXDataError carrying an HTTP status the caller maps to a response. See KittoX_RestServer.md (service layer, §6/§7).

TKXAutoViewInfo record

Describes a menu-referenced autobuild view (a "Build <Builder>" node with a Model child, e.g. "Build AutoList / Model: KITTO_USER_ROLES"). The REST layer exposes each such view under the endpoint name <BuilderName>_<ModelName> (e.g. AutoList_KITTO_USER_ROLES), which stays distinct across builders (AutoList/AutoForm/…) on the same model.

EKXDataError class

Domain error carrying the HTTP status and a symbolic code that the caller (e.g. the REST error filter) maps to a JSON error envelope + real status. Descends from EKError so it flows through the normal exception path.

pascal
constructor Create(const AHTTPStatus: Integer;

Creates the error with the given HTTP status, symbolic code, message and optional field.

pascal
property HTTPStatus: Integer read FHTTPStatus;

The HTTP status to return (404, 403, 409, …).

pascal
property Code: string read FCode;

Machine-readable symbolic code (e.g. 'view_not_found', 'access_denied').

pascal
property FieldName: string read FFieldName;

Optional name of the field the error refers to.

TKXDataService class

Stateless CRUD service. All methods run inside the current request/app context (TKWebApplication.Current) and raise EKXDataError on view-not-found (404), not-a-data-view (404) or ACL denial (403).

pascal
class procedure ApplyFieldValue(const ARecord: TKViewTableRecord;

Applies a single scalar value to a record field, replicating the GUI's write guards (CanInsert on insert; not IsKey and CanUpdate on update; skip binary blobs and file-reference fields), then sets the value with the datatype-aware EF converter (TEFNode.SetAsJSONValue). Request- and JSON-agnostic: the value arrives as a string in the chosen convention.

pascal
class function LoadList(const AViewName, AFilterExpr, ASort, ADir: string;

Loads one page of a view's data. AFilterExpr must already be a safe SQL expression (built from the view's configured filters, not raw client input); ASort/ADir are field names validated against the view. Returns the store (caller owns it and must Free it); ATotal receives the full unpaged record count.

pascal
class function LoadRecord(const AViewName, AKey: string;

Loads a single record by its key string ('field=val&…'). Returns the record; AStore receives the owning store (caller frees it). Raises EKXDataError(404) if no record matches.

pascal
class function CreateRecord(const AViewName: string;

Creates a record: appends+initializes, applies new-record rules, applies the caller's values (notifications disabled), refreshes derived reference values, fires the field/record rule sequence, then persists. Returns the persisted record; AStore receives the owning store (caller frees it). Rule violations raise EKValidationError; the caller maps them to 422.

pascal
class function UpdateRecord(const AViewName, AKey: string;

Updates the record identified by AKey: loads it, applies edit-record rules, applies the caller's values, refreshes derived references, fires the rule sequence, then persists. Returns the record; AStore receives the owning store (caller frees it). Raises EKXDataError(404) if absent.

pascal
class procedure DeleteRecord(const AViewName, AKey: string;

Deletes the record identified by AKey (marks deleted + persists). Raises EKXDataError(404) if absent. Optional hooks run around persistence.

pascal
class function EnumMenuAutoViews: TArray<TKXAutoViewInfo>;

Enumerates the autobuild views referenced by the application menu (the 'MainMenu' TreeView): every "Build <Builder>" node with a Model child whose builder is registered. No side effects (it does not build the views). The REST layer uses this to publish exactly the autobuild surface the GUI menu exposes, each under the endpoint name <Builder>_<Model>.

pascal
class function ResolveAutoView(const AName: string): TKView;

Resolves a menu autobuild view by its endpoint name AName (<Builder><Model>): returns it from the dynamic cache if already built, otherwise — only if AName is a menu-referenced autobuild — builds it with the registered view builder and caches it as a dynamic object under AName. Returns nil if AName is not a menu autobuild. Idempotent. This namespace (<Builder><Model>) never collides with the GUI's own dynamic naming (ModelName) nor with file views.

Released under Apache License, Version 2.0.