0

I'm trying to validate the following JSON

{
    "domain": "example.com", 
    "docker": {
        "services": {
            "app": {
                "image": "nginxdemos/hello:latest", 
                "expose_port": 80,
                "volumes": [
                    "./:/test"
                ]
            }, 
            "db": {
                "image": "mariadb:10.5"
            }
        }
    }
}

I want to make sure that expose_port inside the services children can only be defined once. So adding "expose_port": 1234 to "db" should invalidate the JSON.

That's my schema so far:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://saitho.me/project-configuration.schema.json",
  "title": "Project Configuration",
  "description": "Project configuration file",
  "type": "object",
  "definitions": {
    "docker-service": {
      "properties": {
        "image": {
          "description": "Name of Docker image",
          "type": "string"
        },
        "volumes": {
          "description": "Docker volumes to be mounted",
          "type": "array",
          "items": {
            "type": "string"
          },
          "minItems": 1
        }
      },
      "required": [ "image" ]
    }
  },
  "properties": {
    "domain": {
      "description": "The domain from which the app can be reached",
      "type": "string"
    },
    "docker": {
      "description": "Configuration for Docker containers that will be deployed",
      "type": "object",
      "properties": {
        "services": {
          "description": "List of Docker services to be started",
          "type": "object",
          "patternProperties": {
            ".*": {
              "$ref": "#/definitions/docker-service",
              "oneOf": [
                {
                      "properties": {
                        "expose_port": {
                          "description": "Port that receives web traffic (e.g. 80, 3000, 8080 for most applications). Only one in this file!",
                          "type": "integer"
                        }
                      }
                }
              ],
              "type": "object"
            }
          },
          "additionalProperties": false
        }
      },
      "required": [ "services" ]
    }
  },
  "required": [ "domain" ]
}

So far I've tried combining of allOf and oneOf but that seems only to work on the current child rather than looking at the siblings as well. Does anyone know a solution to my problem? :)

Thanks!

saitho
  • 899
  • 9
  • 17
  • Move `expose_port` one level up so that it resides in services? – Jack Apr 11 '20 at 11:41
  • Not possible, as the Port belongs to the service. ;) But I only want to expose one service. However I could turn the object into an array by moving the name into a property, if that helps... – saitho Apr 11 '20 at 11:44
  • One service can have multiple apps but only one app can have an expose_port? – Jack Apr 11 '20 at 11:49
  • The key is just a random name. It must not be "app". "app" and "db" are both services. Only one service can have expose_port – saitho Apr 11 '20 at 12:01
  • Okay, how about validating your other objects where they *not* contain `expose_port` – Jack Apr 11 '20 at 12:22
  • like [this](https://stackoverflow.com/questions/30515253/json-schema-valid-if-object-does-not-contain-a-particular-property) – Jack Apr 11 '20 at 12:28

1 Answers1

1

If you have a specific set of services (e.g. the exposed port will only ever be on either "app" or "db"), you may use "oneOf" to test that exactly one of the two has a property (or neither, as a third test).

If you have an arbitrary set of services, then you're no longer performing "structural" validation, but moved on into the realm of business logic. In this case, each value depends on the value of potentially all other values. Validating this is not possible with JSON Schema validation alone, see the page Scope of JSON Schema Validation for some more information.

However, you can layout the JSON document to match what you're trying to accomplish. If there's a property that may only be defined once, then factor out the property definition into a place where it may only be defined once:

{
    "domain": "example.com", 
    "docker": {
        "services": {
            "app": {
                "image": "nginxdemos/hello:latest", 
                "volumes": [
                    "./:/test"
                ]
            }, 
            "db": {
                "image": "mariadb:10.5"
            }
        },
        "expose_service": "app",
        "expose_port": 80
    }
}
awwright
  • 565
  • 3
  • 8