girib
girib

Reputation: 5

Validating value combinations with jsonschema

I'm using jsonschema to validate a configuration file. Currently I'm running into trouble validating value combinations of nested properties.

I originally implemented if/then/else, but did not see the desired behavior after several passes. The same was done for switch, dependencies, and lastly using implication.

This is my schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "account_group": {
      "type": "string"
    },
    "region": {
      "type": "string",
      "pattern": "^(us(-gov)?|ap|ca|cn|eu|sa)-(central|(north|south)?(east|west)?)-\\d$"
    },
    "appid": {
      "type": "string"
    },
    "services": {
      "type": "object",
      "minProperties": 1,
      "patternProperties": {
        "^[A-Za-z_]*$": {
          "type": "object",
          "properties": {
            "source": {
              "type": "string"
            },
            "port": {
              "type": "number"
            },
            "cpu": {
              "type": "number",
              "enum": [256, 512, 1024, 2048, 4096]
            },
            "memory": {
              "type": "number",
              "enum": [0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
                12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
                22, 23, 24, 25, 26, 27, 28, 29, 30]
            }
          },
          "required": [
            "source",
            "port",
            "cpu",
            "memory"
          ],
        }
      },
      "additionalProperties": false
    },
    "stages": {
      "type": "object",
      "minProperties": 1
    },
    "workingDirectory": {
      "type": "string"
    }
  },
  "required": [
    "name",
    "account_group",
    "appid",
    "services"
  ]
}

Note: I removed my latest allOf block to avoid presenting broken code I've already tested against

What I'm trying to accomplish is validating the correct combination of cpu/memory values in the configuration file being parsed and compared against the schema.

The combinations are:

256/[0.5, 1, 2]
512/[1-4]
1024/[2-8]
2048/[2-16]
4096/[2-30]

Would someone kindly suggest the appropriate schema design to validate these combinations?

Essentially I need validation to fail if the cpu is 256, and value of memory is anything but 0.5, 1, 2 - to fail if cpu is 512 and value of memory is anything but 1 - 4, etc.

I've gotten the closest using implication, but I've seen many combinations using dependencies which has further muddied the waters for me. Personally I felt if/then/else would be the most logical approach, but it did not produce the expected behavior.

Upvotes: 0

Views: 975

Answers (1)

Jason Desrosiers
Jason Desrosiers

Reputation: 24479

if/then is the best choice for this. The implication pattern works too, but if/then is nicer to work with. The general strategy is to create a definition for each of your conditions and then use an allOf to combine them. You could of course put the conditions inline instead of using definitions, but it reads a lot better this way which makes maintenance easer. I did the first two to illustrate. You can extrapolate from there.

{
  "type": "object",
  "properties": {
    "source": { "type": "string" },
    "port": { "type": "number" },
    "cpu": { "enum": [256, 512, 1024, 2048, 4096] },
    "memory": { "enum": [0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
                         12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
                         22, 23, 24, 25, 26, 27, 28, 29, 30] }
  },
  "required": ["source", "port", "cpu", "memory"],
  "allOf": [
    { "$ref": "#/definitions/if-cpu-256-then-memory-0.5-1-2" },
    { "$ref": "#/definitions/if-cpu-512-then-memory-1-4" }
  ],
  "definitions": {
    "if-cpu-256-then-memory-0.5-1-2": {
      "if": {
        "properties": {
          "cpu": { "const": 256 }
        },
        "required": ["cpu"]
      },
      "then": {
        "properties": {
          "memory": { "enum": [0.5, 1, 2] }
        }
      }
    },
    "if-cpu-512-then-memory-1-4": {
      "if": {
        "properties": {
          "cpu": { "const": 512 }
        },
        "required": ["cpu"]
      },
      "then": {
        "properties": {
          "memory": { "minimum": 1, "maximum": 4 }
        }
      }
    }
  }
}

Upvotes: 2

Related Questions