Value validations
=================

Synopsis
-------------

A verification is a complementary validation to the type which allows the content of a variable to be validated more precisely.

The purpose of validation is to verify that the variable is of :ref:`good quality <data_quality>` and/or that the variable is :ref:`consistent <consistency>`.

.. glossary::

   validator

       A validator is a Jinja code that parses a variable's value and checks
       that this value corresponds to some stated criteria.
       This is a `validator` parameter of our variable, and another `jinja` sub-parameter
       contains validation code for the variable's value.

.. seealso:: The tutorial with a real world sample :ref:`validators <tutorial_validators>`

Parameters
--------------

Depending on the types of calculation, the parameters will be different:

.. list-table::
   :header-rows: 1

   * - **Calculation type**
     - **Comment**

   * - **type**

       `string`
     - The value is `jinja`

   * - **jinja**

       `string`

       `mandatory`
     - Please set a Jinja template.

   * - **params**

       `dict`
     - Additional parameters passed to the Jinja template (see below).

   * - **return_type**

       `string`
     - A validation could returns the error message directly, so return_type is `string` (the default behavior).
       Or a `boolean`, in this case if it's return `true` this means that the value is not valid.

   * - **description**

       `string`
     - Additional information for the :ref:`documentation <data_documentation>` and the error message in case of return_type is a boolean.

   * - **warnings**

       `boolean`
     - If `true` the validation did not raise, it only display a warning.


Params
~~~~~~

There are two types of params:

- the standard parameters (string, boolean, integer, float or null), in this case just do: "key: value"
- advanced settings (with something like "key: {}":

  - parameter via a variable
  - parameter via an information
  - parameter via an identifier: in a :ref:`dynamically built family <dynamically_built_family>` returns the current identifier
  - parameter via an index: in the case of a follower variable returns the current index
  - parameter via :ref:`the current namespace name <namespace>`

Variable params
"""""""""""""""

.. list-table::
   :header-rows: 1

   * - **Parameter**
     - **Comments**
     - **Sample**

   * - **type**

       `string`
     - Type of parameter, which is variable.
     - variable

   * - **variable**

       `string`

       `mandatory`
     - Variable's path.
     - rougail.variable

   * - **propertyerror**

       `boolean`
     - If access to the variable is not possible due to a property
       (for example `disabled`) by default an error is returned.
       If the attribute is `false`, the parameter is not passed to the Jinja template.

       **Default value**: `false`
     - `true`

   * - **optional**

       `boolean`
     - If the variable is not defined the value is always `null`.

       **Default value**: `false`
     - `true`

   * - **whole**

       `boolean`
     - In :term:`dynamically built family` only the value of the current index is retrieve. If this parameter is set to `true` it returns all
       If this parameter is set to `true`, it returns the set of values for all indices.

       **Default value**: `false`
     - `true`

Information params
~~~~~~~~~~~~~~~~~~

.. list-table::
   :header-rows: 1

   * - **Parameter**
     - **Comments**
     - **Sample**

   * - **type**

       `string`
     - Type of parameter, which is information
     - information

   * - **information**

       `string`

       `mandatory`
     - Name of the information whose value we want to retrieve.
     - doc

   * - **variable**

       `string`
     - By default, the information is a context information. It is possible to get a variable information. In this case just specify a path.
     - rougail.variable

Samples
--------------

Strict verification of values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Here is a simple example of validating values:

.. code-block:: yaml

   %YAML 1.2
   ---
   version: 1.1

   my_variable:
     validators:
       - jinja: |-
           {% if my_variable is not none and not my_variable.islower() %}
             {{ my_variable }} is not lowercase string
           {% endif %}
   ...

A verification function must take into account 2 important aspects:

- the value may not be entered (even if the variable is mandatory), the None value must be taken into account
- if return_type is `string` and the value is invalid, a sentence must be returned with an explicit message.

From now on only `null` and lowercase values will be allowed.

Checking values with warning
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In the constraint, it is possible to specify the error level and put it as a warning:

.. code-block:: yaml

   %YAML 1.2
   ---
   version: 1.1

   my_variable:
     validators:
       - jinja: |-
           {% if my_variable is not none and not my_variable.islower() %}
             {{ my_variable }} is not lowercase string
           {% endif %}
         params:
           warnings: true
   ...

In this case a value with a capital letter will be accepted, but a warning message will appear.

Verification with parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: yaml

   %YAML 1.2
   ---
   version: 1.1

   my_hidden_variable:
     disabled: true

   my_variable:
     validators:
       - type: jinja
         jinja: |-
           {% if param1 is defined and my_variable == param1 %}
             has same value as unknown_variable
           {% endif %}
           {% if param2 is defined and my_variable == param2 %}
             has same value as my_hidden_variable
           {% endif %}
         params:
           param1:
             variable: unknown_variable
             optional: true
           param2:
             variable: my_hidden_variable
             propertyerror: false
   ...

An example with an identifier type parameter:

.. code-block:: yaml

   %YAML 1.2
   ---
   version: 1.1

   my_dyn_family_{{ identifier }}:
     type: dynamic
     dynamic:
       - val1
       - val2
     description: 'Describe {{ identifier }}'

     my_dyn_var:
       validators:
         - jinja: |
             {% if my_dyn_family_.my_dyn_var == param1 %}
               forbidden!
             {% endif %}
           params:
             param1:
               type: identifier
   ...

In this example, we see a :term:`dynamically built family`. Two families will be created: `my_dyn_family_val1.my_dyn_var` and `my_dyn_family_val2.my_dyn_var`.

The value of the variable within this family cannot be equal to the value
of the identifier (`val1` and `val2` respectively).

An example with an index type parameter:

.. code-block:: yaml

   %YAML 1.2
   ---
   version: 1.1

   family:
     type: sequence

     leader:
       default:
         - val1
         - val2

     follower1:
       type: integer
       validators:
         - type: jinja
           jinja: |-
             {% if family.follower1 == param1 %}
               forbidden!
             {% endif %}
           params:
             param1:
               type: index
   ...
