orbeon / orbeon-forms

Orbeon Forms is an open source web forms solution. It includes an XForms engine, the Form Builder web-based form editor, and the Form Runner runtime.
http://www.orbeon.com/
GNU Lesser General Public License v2.1
512 stars 220 forks source link

External validation with HTTP/database services #1304

Open avernet opened 10 years ago

avernet commented 10 years ago

Right now this is possible, but only indirectly:

  1. Define the service to call in Form Builder.
  2. Store the result of the service in a hidden field, i.e. a field which visibility has been set to false().
  3. Use the value of that field in the constraint expression of the field you really want to validate.
  4. Define an action that calls the service when there a value change on the field to validate.

This approach has some limitations:

  1. It is convoluted – If a number of fields need to be validated this way, as many services, actions, and hidden fields need to be defined and maintained.
  2. It isn't well-suited to validating multiple fields – Often, customers would like to use the same service to validate several fields.
  3. It doesn't allow for the validation to run on save or submit – As calling the service can be an expensive operation, it might make sense to just do it on save or submit, not on value change.
ebruchez commented 10 years ago

This comment contains ideas moved from the wiki.

Rationale

Currently, Form Runner only supports "internal" validation, that is validation expressed with types and constraints (like in plain XForms).

Often, you need external validation, which might involve complex calculations or lookups that are hard or inconvenient to implement with XForms. E.g.:

A possible way

A possible way of implementing this is documented in this how-to: Perform external validation.

This works in limited cases but has limitations, including:

These limitations are likely not acceptable for Form Runner as we want this feature to be future-proof.

New thoughts

Requirements

Lifecycle

With internal validation, data is frequently revalidated, typically whenever a field changes.

With external validation, this by default might be too costly. In that case, external validation:

Service

Validation information is provided by a service:

The exact format of the response is TBD, but could be:

Persistent/imperative MIPs

In XForms, Model Item Properties (MIPs) are declarative, so don't need to be "persisted" in an instance DOM as they can be recomputed as needed.

With external validation, we might need to introduce a type of persistent, imperative MIPs, settable with an action:

<xxf:setmip ref="foo/bar" name="valid" value="false()"/>

and readable as an XPath function:

xxf:getmip($name as xs:string) as item()

An action to clear all MIPs, or all MIPs of a given type, might be needed:

 <xxf:clearmip ref="instance()" name="valid"/>

NOTE: Persistent MIPs do not replace declarative MIPs: they combine with them.

When nodes are removed from an instance, these MIPs are automatically removed.

Impact on the XForms engine

This has the following impact on the implementation:

Impact on Form Runner

Impact on Form Builder

None for now, this has to be configured through properties.

ebruchez commented 10 years ago

At this point, I am not 100% sure that the "persistent MIP idea" is the best one. But the idea that external validation could be triggered by default only upon save/submit is good, and maybe configurable via independent properties or directly in the save/send processes properties.

There should probably also be ways to trigger external validation separately, maybe by adding an action once we have separated Form Builder actions from services.

As a first step, it might be ok to say the following:

avernet commented 10 years ago

+1 from customer

avernet commented 9 years ago

+1 from customer

ebruchez commented 7 years ago

+1 from evaluator

ebruchez commented 7 years ago

Comment on the proposal above: we could send the whole XForms instance, or possibly pass a list of control names to send. Then only those values would be sent, as URL parameters or in a application/x-www-form-urlencoded POST.

Clearly an important step is to determine the format(s) of the request/response.

ebruchez commented 7 years ago

+1 from evaluator

ebruchez commented 7 years ago
ebruchez commented 7 years ago

With datasets, the workaround can look like:

This is still heavy but doesn't require hidden controls. A single service call can return all validity information.

ebruchez commented 7 years ago

Two scenarios to consider:

Both are probably reasonsable depending on the situation.

ebruchez commented 7 years ago

With datasets:

Example:

<xf:bind
    ref="instance('fr-form-instance')//*[empty(*)]"
    constraint="
        for $name in name(.), $ds in fr:dataset('validation-dataset') return
            empty($ds/*[name() = $name][1]/[@valid = 'false'])
    "
/>

Above assuming a validation document like:

<validation>
    <name valid="false"/>
    <foo valid="false"/>
    <bar valid="false"/>
</validation

You can vary the format of the validation document.

ebruchez commented 7 years ago

The above might work if we add the ability to set a dataset with the result of a process send() action.

avernet commented 5 years ago

+1 from user

ebruchez commented 5 years ago

+1 from customer