Documentation
¶
Overview ¶
Package formdecision gives a form field its options from a deployed DMN decision: a select, radio group, checklist or tag list whose choices are the list a decision answers with — typically a boxed list (DMN §10.3.2.7) that the people who own the list maintain in the decision editor, versioned and published like any other decision (ADR-0511). The decision's inputs are bound to other fields of the form, or fixed, so a choice in one field decides what the next one offers: a group, then its members; a country, then its regions.
The decision is evaluated when the form is shown and whenever a field its inputs are bound to changes, never stored with it: a change to the list is what the next person to open the form sees. Nothing here is durable. An evaluation is a read of a compiled model the registry already holds; it reaches no log, no state store and no processor, and the only thing that becomes a fact is the value the person submits — which is checked against the list once more before the task completes.
**What a request may say** is the values of the fields the stored form binds, and nothing else: which decision, which input each value is, and which inputs are fixed are read from the form, and a request naming anything more is refused. A value bound from a field that offers fixed choices — its own static options, or the list of a decision field before it — must be one of those choices, so a field cannot be used to ask a decision about values the form never offers. The form editor's route evaluates a decision with inputs of the author's choosing, and is a modeler's, who may already try any model (POST /api/v1/decisions/evaluate).
**This area owns no state**, like formlookup: the registry it reads is the server's, and the server's closures observe the single writer (I3).
Index ¶
- Constants
- Variables
- func Check(schema []byte) error
- type Binding
- type Config
- type Error
- type Field
- type Form
- type Input
- type Option
- type Service
- func (s *Service) CheckSubmission(ctx context.Context, schema []byte, vars map[string]any, ...) *Error
- func (s *Service) FieldOptions(ctx context.Context, schema []byte, key string, values map[string]any) ([]Option, int, *Error)
- func (s *Service) HandleEvaluate(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
- func (s *Service) Options(ctx context.Context, id string, inputs map[string]any) ([]Option, int, *Error)
- func (s *Service) Resolve(ctx context.Context, schema []byte) []byte
- func (s *Service) WriteFieldOptions(w http.ResponseWriter, r *http.Request, schema []byte, key string)
- type Source
- type Step
Constants ¶
const MaxOptions = 1000
MaxOptions bounds the options one decision gives a field. A choice among more than this is not made from a list a person scrolls through; it wants a search, which is what a directory field is (ADR-0473). A list over the bound is refused rather than cut, because a cut list would hide entries without saying which.
const Member = "atlasDecision"
Member is the component member that makes a field a decision field.
const Version = 1
Version is the one version of the block this server reads. A form written for a newer one is refused at save with that reason, rather than half-understood.
Variables ¶
var Codes = []string{
"not_deployed", "unbound_input", "evaluation_failed", "not_a_list",
"not_found", "invalid_query", "invalid_value", "invalid_configuration",
}
Codes are the machine-readable codes a refusal answers with, beside its message.
var FieldTypes = []string{"select", "radio", "checklist", "taglist"}
FieldTypes are the form-js fields that take their options from a source. A select and a radio group submit one value; a checklist and a tag list submit a list.
Functions ¶
Types ¶
type Binding ¶
Binding is one input of a decision field, checked: the input's name, and either the key of the field it is bound to or its fixed value.
type Config ¶
type Config struct {
// Decision resolves a decision id to its newest decision deployment — what it is,
// and the key it is evaluated under (dmn.Registry.LatestDeployedDecision).
Decision func(id string) (dmn.DeployedDecision, uint64, bool)
// Decisions lists every decision that has a decision deployment.
Decisions func() []dmn.DeployedDecision
// Evaluate evaluates the decision from the deployment under key against inputs.
Evaluate func(ctx context.Context, key uint64, id string, in map[string]any) (map[string]any, error)
// MaxBody bounds a request's body: the server's Request budget.
MaxBody int64
}
Config is the server's half of this area.
type Error ¶
Error is a refusal: a status, a code a client can branch on, and a message a person can read.
func (*Error) Write ¶
func (e *Error) Write(w http.ResponseWriter)
Write answers the refusal in the API's error envelope, with its code.
type Field ¶
type Field struct {
Key string
Label string
Type string
DecisionID string
Inputs []Binding
// Multi is true for a field that submits a list of values.
Multi bool
// Path is where the field's value sits in what the form submits: the paths of the
// groups and dynamic lists around it, then its own key, both split at their dots as
// form-js splits them.
Path []Step
Required bool
Disabled bool
Conditional bool
}
Field is one decision field of a form, checked.
func (Field) BoundFields ¶
BoundFields reports whether any input of the field is bound to another field — which is what makes its options depend on what the person chooses.
type Form ¶
type Form struct {
Fields []Field
// contains filtered or unexported fields
}
Form is what a schema says about its decision fields and the fields they are bound to.
func Parse ¶
Parse reads a form-js schema's decision fields and checks every one of them. A schema without any parses to none; a malformed atlasDecision is refused with the field and the reason.
What it does not check is whether the decision is deployed, which inputs it takes and whether it answers with a list. That is the server's state, not the form's: it changes after the form is saved, and an import may well bring a form in before the decision it names. The form editor shows it while the field is edited, and a form rendered against a decision that cannot answer says so in the field.
func (*Form) DecisionIDs ¶
DecisionIDs are the decisions the form's fields name, each once, sorted.
type Input ¶
type Input struct {
Name string `json:"name"`
Field string `json:"field,omitempty"`
Value json.RawMessage `json:"value,omitempty"`
}
Input is one of the decision's inputs as a block states it: bound to another field of the form by its key, or a fixed value.
type Option ¶
Option is one choice as the form shows it: what a person reads, and what the form submits when it is chosen.
func OptionsOf ¶
OptionsOf turns a decision's result into options, the way form-js turns a select's `values` into options (normalizeOptionsData in @bpmn-io/form-js-viewer 1.24), so what the server checks a submission against is what the person was shown:
- a string, number or boolean is both the label and the value; an empty string is no option;
- a context with a non-empty string `label` and a `value` that is such a primitive or a non-empty context is taken as it is;
- a context with only a primitive `value` is labelled with that value;
- anything else — null, a list, a context without a usable value — is skipped and counted, so the editor can say how many entries the form will not show.
A result that is not a list at all is refused: the decision answers something else.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service serves a form's decision-backed options. Build it with New.
func (*Service) CheckSubmission ¶
func (s *Service) CheckSubmission(ctx context.Context, schema []byte, vars map[string]any, known func() map[string]any) *Error
CheckSubmission holds what a completion submits to each decision field's list, as the decision answers now for what the same submission chose in the fields its inputs are bound to, and returns the first refusal. Inside a dynamic list each row is held to the list its own row's choices give. A form without a decision field is not checked at all.
A bound field form-js did not submit — a disabled one — is read from known, which is what the form was opened with (the instance's variables for a task), and failing that from its default value. known is called at most once, and only when needed; it may be nil.
It checks that a value given is one the list offers, not that one is given: a required field is form-js's to insist on, as it is for every other field. Like the directory field's check (ADR-0473) it runs before the completion, not with it: the list may change between this check and the next step that reads the value.
func (*Service) FieldOptions ¶
func (s *Service) FieldOptions(ctx context.Context, schema []byte, key string, values map[string]any) ([]Option, int, *Error)
FieldOptions answers the options of one decision field of a stored form, for the values of the fields its inputs are bound to. values may name those fields and the ones they depend on in turn, and nothing else.
func (*Service) HandleEvaluate ¶
func (s *Service) HandleEvaluate(w http.ResponseWriter, r *http.Request)
HandleEvaluate is POST /api/v1/form-data/decisions/{decisionId}/options: the options a decision answers for inputs the author gives — the form editor's previews, for a form that may not be saved yet.
func (*Service) HandleList ¶
func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
HandleList is GET /api/v1/form-data/decisions: the decisions a field's options can come from — every decision with a decision deployment — and the inputs each takes.
func (*Service) Options ¶
func (s *Service) Options(ctx context.Context, id string, inputs map[string]any) ([]Option, int, *Error)
Options evaluates a decision's newest deployment against inputs and returns its list as options, and how many of its entries are no option a form can show. Every input the decision declares has to be given — a null is given, an absent one is not.
func (*Service) Resolve ¶
Resolve writes each decision field's options into the form as the field's own `values` and drops its atlasDecision block, so the form renders as a plain form-js form wherever it is shown. It is for the pages nobody signs in to — a public start link, a public wizard step — where each such field would otherwise be one more request.
Only a field whose inputs are all fixed is written in: one bound to another field depends on what the visitor chooses there, and asks the page's route as the visitor chooses. A field whose decision cannot answer now is left as it was too: the viewer then says that its options could not be loaded, which an empty list would not. A form without a decision field, or one the save check would refuse, comes back unchanged.
func (*Service) WriteFieldOptions ¶
func (s *Service) WriteFieldOptions(w http.ResponseWriter, r *http.Request, schema []byte, key string)
WriteFieldOptions answers a request for the options of one decision field of a stored form the server has already authorized the caller for: the console's, a public link's or a public wizard step's.