Cottonwood-Technology / ValidX

Fast, powerful, and flexible validator with sane syntax.
BSD 2-Clause "Simplified" License
20 stars 4 forks source link

Question: how to access location in data? #20

Closed draggeta closed 1 year ago

draggeta commented 1 year ago

This may seem unintuitive, but for a project we need to be able to validate if a piece of data is correct in multiple locations. Here is easy an example:

{
  "interfaces": [
    {
      "name": "GigabitEthernet0/1.1234",
      "description": "DEVICE01 Backbone 1234",
      "vlan-id": 1234
    },
    { ... }
  ]
}

In this case we'd want for the VLAN identifier to match between the keys. I tried looking at a way to figure out where the validator is in the data structure. This would allow me to write a function that can relatively look up data throughout the stucture. Something like the below example, where Contains (a validator I would implement myself) knows where in the data structure it is in and can then move up one level and grab the value from the vlan-id key:

List(
  Dict({
    "name": AllOf(Str(pattern="GigabitEthernet\d\/\d\.\d+"), Contains(full_data, ".vlan-id")),
    "description": AllOf(Str(pattern="\S+ Backbone \d+"), Contains(full_data,".vlan-id")),
    "vlan-id": Int(),
  })
)

I tried looking at contexts as these do contain the path,but these seem to only be accessible when handling errors. Is there a way to do this that I have missed, or a better way to implement it?

kr41 commented 1 year ago

In short, ValidX doesn't provide such feature and I have no plans to add it.

ValidX uses declarative approach. It works well when we need to describe how valid data should look like. But when we introduce conditional relations between particular elements, the result looks awkward, because conditionals are better expressed by if-then operator in an imperative way.

So if you really need to validate such relations, use ValidX to prevalidate common structure and data types, and regular Python to deal with conditional relations.

validate = Dict(
    {
        "name": Str(),
        "description": Str(),
        "vlan-id": Int(),
    },
    optionals=["vlan-id"]
)

...

item = validate(item)
if re.search("backbone", item["description"], re.I) and not item.get("vlan-id"):
    raise ValueError("'vlan-id' is required")
draggeta commented 1 year ago

That is completely understandable. Thanks for the confirmation 😄

We've managed to get a similar solution working, but with absolute paths in a custom validator instead of relative.

I'll close the issue.