Specs and Metadata Validation#

A spec is a label, attached to a node, that declares that the node follows some convention – for example, that its metadata contains certain fields. See Metadata and “Specs” and Standards Used by Tiled for background.

The Tiled server can validate node’s metadata against a spec when the data is written or its metadata is edited. For this, the server configuration can declare which specs are accepted and – optionally – a validator function that checks (and, if desired, normalizes) the metadata for each of them.

Write a validator function#

A validator is an ordinary Python function with the following interface:

from typing import Any, Optional

from tiled.adapters.protocols import AnyAdapter
from tiled.structures.core import Spec, StructureFamily
from tiled.validation_registration import ValidationError


def validate_my_spec(
    spec: Spec,
    metadata: dict[str, Any],
    entry: Optional[AnyAdapter],
    structure_family: Optional[StructureFamily],
    structure: Optional[dict[str, Any]],
) -> Optional[dict[str, Any]]:
    ...

The function should either:

  • raise tiled.validation_registration.ValidationError to reject the write (the server responds with HTTP 400 and the error message), or

  • return None to accept the metadata unchanged, or

  • return a (possibly modified) metadata dict to accept it and replace it. The returned metadata is what gets stored and is echoed back to the client in the response.

The arguments are:

  • spec – the Spec (tiled.structures.core.Spec) being validated.

  • metadata – the metadata dict submitted by the client.

  • entry – carries the existing node when metadata is being edited, or None when a new node is being created.

  • structure_family – e.g. "array", "table", "container"; may be None.

  • structure – the structure description of the new node; may be None.

The function may be synchronous (as above) or an async def; the server supports both.

Here is a small example that requires a sample_id field and, if it exists, normalizes it to a string:

# validators.py

from tiled.validation_registration import ValidationError


def validate_my_spec(spec, metadata, entry, structure_family, structure):
    if "sample_id" not in metadata:
        raise ValidationError("The 'my-validated-spec' spec requires a 'sample_id' field.")

    # Optionally normalize and return the modified metadata.
    normalized = dict(metadata)
    normalized["sample_id"] = str(normalized["sample_id"])
    return normalized

Notably, Tiled ships one built-in validator, for the composite container spec, in tiled/validation_registration.py.

Register spec validators in the server configuration#

Reference the validator from the server configuration file under specs, using an importable module:function path:

# config.yml
specs:
  - spec: my-validated-spec
    validator: validators:validate_my_spec
  - spec: another-spec  # accepted, but not validated (no validator is specified)

trees:
  - path: /
    tree: catalog
    args:
      uri: ./catalog.db

The directory containing the configuration file is placed on the Python path when the config is loaded, so a validators.py sitting next to config.yml is importable as shown. Alternatively, use any importable package, e.g. my_package.validators:validate_my_spec.

The validator key is optional. Listing a spec with no validator simply declares it as accepted. This is useful together with the reject_undeclared_specs option:

specs:
  ...

# Reject any write whose spec is not listed above.
reject_undeclared_specs: true

With reject_undeclared_specs: true, a client that tries to write a node tagged with a spec not present in this list receives an HTTP 400. By default (false) unknown specs are allowed through unvalidated. Please see Service Configuration Reference for more detail.

When validation runs#

Validators run when a node is created and when its metadata is replaced or patched. When a node carries multiple specs they are validated starting from least specific first (the reverse of the list order), so that a broad spec can normalize the metadata before a narrower one inspects it. When declaring multiple specs, please ensure that their validators do not make conflicting modifications; the server does not currently prevent that.