Validation Schema Documentation

Validation schema is mandatory for using Waaila extension validator. You need to define schema of structure of your data layer. It is on you, how detailed you will be in your schema. You can create your schema by yoursefl with help of this documentation or Cross Masters are offering creating schema for you.

Main structure

Whole schema is JSON object where top structure of this object is Waaila specific. But for the explicit validation rules it is used JSON Schema structure (https://json-schema.org/).

Groups

Top level and mandatory part of schema structure is array groups. The groups array contains objects with definition of each group. There must be always at least one group. And every of this group definition must have these parameters: - name [string]- name of the single group of contexts. Every group must have unique name. - contexts [JSON schema object] - array of validation subschemas for explicit contexts.

Example of main structure:

{
    "groups": [
        {
            "name": "Page types",
            "contexts": {
                ...
            }
        },{
            "name": "Actions",
            "contexts": {
                ...
            }
        }
    ]
}

Why do we need groups? Groups are used for combining multiple contexts for validation at the same time. If you will add everything into one group you will not be able to select more contexts at once. But everytime you can select one context from each group. That will allow you to make combinations.

Example of groups "Page types" and "Actions" in interface of extension:

Contexts

Each context is independent JSON schema object. More about JSON schema structure there:

Example of schema structure with one group and one context in it:

{
    "groups": [
        {
            "name": "Page types",
            "contexts": {
                "home": {
                    "type": "object",
                    "properties": {
                        "page": {
                            ...
                        },
                        "user": {
                            ...
                        }
                    },
                    "required": [
                        "page",
                        "user"
                    ],
                    "additionalProperties": true
                }
            }
    ...
}

Shared subschemas (optional)

In some cases you will have several different contexts, but still there will be some part of the validation schema which is same for few or even many of them. In that case you dont want to repeate same part of validation schema in every of these contexts or even several times in one context. That is case where definitions can helps you a lot.

You can read more about definitions there: https://json-schema.org/understanding-json-schema/structuring.html

Definitions

The subschemas keyword defines subschemas that you can refer anywhere in contexts.

  • $id [string] - defines a base URI which you can reference within the contexts.
  • definitions [JSON schema object] - object of subschemas where you define definitions used as subschemas.

Example of whole schema structure:

{
    "groups": [
        {
            "name": "Page types",
            "contexts": {
                ...
            }
        },{
            "name": "Actions",
            "contexts": {
                ...
            }
        }
    ],
    "subschemas": {
        "$id": "defs.json",
        "definitions": {
            ...
    }
}

Example of currencyCode subschema definition:

...
"subschemas": {
  "$id": "defs.json",
  "definitions": {
    "currencyCode": {
        "type": "string",
        "minLength": 3,
        "maxLength": 3,
        "pattern": "^CZK$"
    }
    ...
  }
}
...

Referencing

If you already have some subschema created by definiton, we can then refer to this subschema from elsewhere using the $ref keyword. The easiest way to describe $ref is that it gets logically replaced with the thing that it points to.

Example of referencing currencyCode subschema:

{ "$ref": "defs.json#/definitions/currencyCode" }

The value of $ref is a URI-reference starting with $id of the definitions object, and the part after # sign is in a format called JSON Pointer. This can be used anywhere in contexts. You will always use $ref as the only key in an object: any other keys you put there will be ignored by the validator.

Example of using $ref:

...
"page": {
    "type": "object",
    "properties": {
        "currencyCode": { "$ref": "defs.json#/definitions/currencyCode" },
        "localCurrencyCode": { "$ref": "defs.json#/definitions/currencyCode" }
    ...
},
{
    "subschemas": {
        "$id": "defs.json",
        "definitions": {
            "currencyCode": {
                "type": "string",
                "minLength": 3,
                "maxLength": 3,
                "pattern": "^CZK$"
            }
        }
    }
}

Summary example of whole schema:

{
  "groups": [
    {
      "name": "Page types",
      "contexts": {
        "home": {
          "description": "Schema for custom DataLayer for home page",
          "type": "object",
          "properties": {
            "session": { "$ref": "defs.json#/definitions/session" },
            "page": {
              "type": "object",
              "properties": {
                "title": { "$ref": "defs.json#/definitions/title" },
                "locale": { "$ref": "defs.json#/definitions/locale" },
                "currencyCode": {
                  "$ref": "defs.json#/definitions/currencyCode"
                },
                "localCurrencyCode": {
                  "$ref": "defs.json#/definitions/currencyCode"
                },
                "type": {
                  "type": "string",
                  "pattern": "^home$"
                },
                "kind": { "type": "null" },
                "trail": {
                  "type": "string",
                  "pattern": "^Home$"
                }
              },
              "required": [
                "title",
                "locale",
                "currencyCode",
                "localCurrencyCode",
                "type",
                "kind",
                "trail"
              ],
              "additionalProperties": false
            }
          },
          "required": ["session", "page"],
          "additionalProperties": false
        },
        "error": {
          "description": "Schema for custom DataLayer for error page",
          "type": "object",
          "properties": {
            "session": {
              "$ref": "defs.json#/definitions/session"
            },
            "page": {
              "type": "object",
              "properties": {
                "title": {
                  "$ref": "defs.json#/definitions/title"
                },
                "locale": {
                  "$ref": "defs.json#/definitions/locale"
                },
                "currencyCode": {
                  "$ref": "defs.json#/definitions/currencyCode"
                },
                "localCurrencyCode": {
                  "$ref": "defs.json#/definitions/currencyCode"
                },
                "type": {
                  "type": "string",
                  "pattern": "^error$"
                },
                "kind": {
                  "type": "string",
                  "pattern": "^error$"
                },
                "trail": {
                  "type": "null"
                },
                "error.message": {
                  "type": "string",
                  "minLength": 1
                },
                "error.type": {
                  "type": "string",
                  "minLength": 1
                }
              },
              "required": [
                "title",
                "locale",
                "currencyCode",
                "localCurrencyCode",
                "type",
                "kind",
                "trail",
                "error.message",
                "error.type"
              ],
              "additionalProperties": false
            }
          },
          "required": ["session", "page"],
          "additionalProperties": false
        }
      }
    }
  ],
  "subschemas": {
    "$id": "defs.json",
    "definitions": {
      "session": {
        "type": "object",
        "properties": {
          "machine": {
            "type": "string",
            "enum": ["internal", "external"]
          },
          "ip": {
            "type": "string",
            "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
          },
          "application": {
            "type": "string",
            "minLength": 2
          },
          "serverTime": {
            "type": "number",
            "minimum": 2
          },
          "deviceType": {
            "type": "string",
            "enum": ["mobile", "tablet", "desktop"]
          },
          "env": { "type": "string" }
        },
        "required": [
          "machine",
          "ip",
          "application",
          "serverTime",
          "deviceType",
          "env"
        ],
        "additionalProperties": false
      },
      "title": {
        "type": "string",
        "minLength": 2
      },
      "locale": {
        "type": "string",
        "minLength": 5,
        "maxLength": 7,
        "pattern": "^[a-zA-Z]{2,3}([-\\\\/][a-zA-Z]{2,3})?$"
      },
      "currencyCode": {
        "type": "string",
        "minLength": 3,
        "maxLength": 3,
        "pattern": "^(AED|AFN|ALL|AMD|ANG|AOA|ARS|AUD|AWG|AZN|BAM|BBD|BDT|BGN|BHD|BIF|BMD|BND|BOB|BRL|BSD|BTN|BWP|BYR|BZD|CAD|CDF|CHF|CLP|CNY|COP|CRC|CUC|CUP|CVE|CZK|DJF|DKK|DOP|DZD|EGP|ERN|ETB|EUR|FJD|FKP|GBP|GEL|GGP|GHS|GIP|GMD|GNF|GTQ|GYD|HKD|HNL|HRK|HTG|HUF|IDR|ILS|IMP|INR|IQD|IRR|ISK|JEP|JMD|JOD|JPY|KES|KGS|KHR|KMF|KPW|KRW|KWD|KYD|KZT|LAK|LBP|LKR|LRD|LSL|LYD|MAD|MDL|MGA|MKD|MMK|MNT|MOP|MRO|MUR|MVR|MWK|MXN|MYR|MZN|NAD|NGN|NIO|NOK|NPR|NZD|OMR|PAB|PEN|PGK|PHP|PKR|PLN|PYG|QAR|RON|RSD|RUB|RWF|SAR|SBD|SCR|SDG|SEK|SGD|SHP|SLL|SOS|SPL|SRD|STD|SVC|SYP|SZL|THB|TJS|TMT|TND|TOP|TRY|TTD|TVD|TWD|TZS|UAH|UGX|USD|UYU|UZS|VEF|VND|VUV|WST|XAF|XCD|XDR|XOF|XPF|YER|ZAR|ZMW|ZWD)$"
      }
    }
  }
}