question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if youā€™re still stuck at the end, weā€™re happy to hop on a call to see how we can help out.

Support request: How to formulate a schema containing partially exclusive or without Or?

See original GitHub issue

Iā€™ve a config which is partially exclusive OR. It may be either like this

common:
- ...
variant_a:
- ...
variant_a_again:
- ...

or

common:
- ...
variant_b:
- ...
variant_b_again:
- ...

Using a schema like schema = Map({"common": Seq(...), "variant_a": Seq(...), "variant_a_again": Seq(...)}) | Map({"common": Seq(...), "variant_b": Seq(...), "variant_b_again": Seq(...)}) should work. However I get this error strictyaml.exceptions.InvalidValidatorError: You tried to Or ('|') together 2 Map validators. Try using revalidation instead. which prevents me from using it. To me itā€™s not clear how to do revalidation here. How do I have to use revalidation in this case?

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:14 (10 by maintainers)

github_iconTop GitHub Comments

2reactions
chrisburrcommented, Jun 10, 2020

@fkromer I think the suggestion is something like

schema_var_a = Map(...)
schema_var_b = Map(...)

config = load(my_yaml, MapPattern(Str(), Any()))

if "variant_a" in config:
    config = config.revalidate(schema_var_a)
elif "variant_b" in config:
    config = config.revalidate(schema_var_b)
else:
    raise ValueError(my_yaml)

config = config.data

The whole ā€œMap({ā€¦}) | Map({ā€¦}) | Map({ā€¦})ā€ approach Iā€™m not really keen on since thereā€™s no way to know which one the user was necessarily intending to match, so what error should it display? If the 4th out of 6 would have matched were it not for a mis-spelled key, then showing the error message that it didnā€™t match the last map (current behavior) isnā€™t very useful.

I think I would expect the error to come from the OrValidator with something like: failed to find a valid schema for the value starting at....

Example:

In my case the schema could be one of these three:

1. Currently possible solution

schema = MapPattern(Str(), Map({
    "name": Str(),
    "input": Map(Str(), Any()),
}))
data = load(my_yaml, schema)

for job_name, job_data in data.items():
    if "url" in job_data["input"]:
        job_data["input"].revalidate(Map({
            "url": Str(),
        }))
    elif "id" in job_data["input"]:
        job_data["input"].revalidate(Map({
            "id": Int(),
        }))
    elif "job" in job_data["input"]:
        job_data["input"].revalidate(Map({
            "job": Regex("(" + "|".join(map(re.escape, data.data.keys())),
        }))
    else:
        raise ValueError(f"No valid input type found for {job_name}")

2. Using an OrValidator with Map

data = load(my_yaml, MapPattern(Str(): Any()))

input_data_schema = Map({
    "url": Str(),
}) | Map({
    "id": Int(),
}) | Map({
    "job": Regex("(" + "|".join(map(re.escape, data.data.keys())),
})
full_schema = MapPattern(Str(), Map({
    "name": Str(),
    "input": input_data_schema,
}))
data.revalidate(full_schema)

3. After writing the first two I realised this could also be done using a custom validator class

I started writing a custom validator here but deleted it as it become quite long to do correctly for both validate and to_yaml.

Summary thoughts

I still prefer option (2). I can easily imagine option (1) becoming much more complicated if input_data_schema could appear in more places or at multiple levels in a deeper structure. If there are problems with (2) then (3) is an acceptable fallback.

1reaction
chrisburrcommented, Jun 9, 2020

Thanks @crdoconnor, it happens to us all šŸ˜† (btw Iā€™m loving strictyaml)

@fkromer Beautiful! šŸ˜‰ Unfortunately mines a little messier as the map is the key of another map:

first_thing:
    input:
        url: http://example.com  # Str()

second_thing:
    input:
        id: 1234  # Int()

third_thing:
    input:
        job: first_thing  # Str() corresponding to one of the other top-level keys

I can workaround it by setting input to be a Map() and then looping over the keys to check which key exists so I can revalidate with the corresponding schema but it feels overly complicated.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Federated schema design best practices - Apollo GraphQL Docs
Best practice #2: Design schemas in a demand-oriented, abstract way ... With federation, however, a reviews service's schema can represent a true subset...
Read more >
W3C XML Schema Definition Language (XSD) 1.1 Part 1
Abstract. This document specifies the XML Schema Definition Language, which offers facilities for describing the structure and constrainingĀ ...
Read more >
Schema design tips - Apache Druid
Druid datasources can be ingested with or without rollup. With rollup enabled, Druid partially aggregates your data during ingestion, potentially reducingĀ ...
Read more >
Online Schema Changes | CockroachDB Docs
Update table schemas without external tools or downtime. ... Your application's queries can run normally, with no effect on read/write latency. The schema...
Read more >
Non-relational data and NoSQL - Azure Architecture Center
Specifically, they tend toward having no fixed schema. Also, they tend not to support transactions, or else restrict the scope of transactions, and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found