{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://netboxlabs.com/docs/designs/reference/design.schema.json",
  "title": "NetBox Design",
  "description": "A NetBox Designs design file. Design structure, in the NetBox Designs docs, is the reference.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The design's title."
    },
    "description": {
      "type": "string",
      "description": "What the design builds."
    },
    "version": {
      "type": "string",
      "description": "A label for this content, such as 1.2.0."
    },
    "netbox_versions": {
      "description": "The NetBox versions the design works with: '*' or a list of PEP 440 specifiers.",
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          },
          "minItems": 1
        }
      ]
    },
    "variables": {
      "type": "object",
      "description": "The design's inputs and computed values. Each is $USER (required), $USER_OPTIONAL, a default value, an expression, or a typed declaration: a mapping with a type (string, int, bool, cidr, netbox_ref, netbox_new, netbox_field) and what that type takes.",
      "additionalProperties": true
    },
    "instance_key": {
      "type": "string",
      "description": "Which variable identifies an instance of this design, when a deployment holds it more than once: a rack design's rack_name. Each distinct value is its own instance, with its own variables."
    },
    "infra": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/item"
      },
      "description": "The objects it builds."
    },
    "assert": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/assertion"
      },
      "description": "What must hold once the plan is applied (ADR 021): each entry counts the objects of a type, in the design and the target together, and says what the count must be. A failing assertion refuses the plan with its message."
    }
  },
  "required": [
    "netbox_versions"
  ],
  "additionalProperties": false,
  "$defs": {
    "item": {
      "type": "object",
      "description": "An object the design builds (infra_type), a design it includes (design: nbd:<name>), or either repeated (nbd_loop). Other keys are the object's fields, which depend on its type and the NetBox version; a list under a field holds the objects that belong to this one.",
      "properties": {
        "infra_type": {
          "type": "string",
          "description": "The object type's name, as in Object types."
        },
        "design": {
          "type": "string",
          "description": "The design to include: nbd:<catalogue name>, or nbd:<catalogue name>@<version or content hash>. The catalogue accepts nothing else; nbd validate also takes a local file path."
        },
        "design_template": {
          "type": "string",
          "deprecated": true,
          "description": "The old name for design."
        },
        "template_vars": {
          "type": "object",
          "description": "The included design's variables, each worked out in this design."
        },
        "nbd_loop": {
          "type": "object",
          "description": "Repeat the item once per pass. See Expressions and loops.",
          "properties": {
            "over": {
              "description": "{start, end} counts both included; an integer n counts 1 to n; a list is each item.",
              "oneOf": [
                {
                  "type": "object",
                  "required": [
                    "start",
                    "end"
                  ],
                  "properties": {
                    "start": {
                      "type": "integer"
                    },
                    "end": {
                      "type": "integer"
                    }
                  }
                },
                {
                  "type": "integer"
                },
                {
                  "type": "array"
                }
              ]
            },
            "as": {
              "type": "string",
              "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
              "default": "i",
              "description": "The name the pass is known by: loop.<as>."
            }
          },
          "required": [
            "over"
          ],
          "additionalProperties": false
        },
        "items": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/item"
          },
          "description": "With nbd_loop: a group of items repeated together."
        }
      },
      "additionalProperties": {
        "anyOf": [
          {
            "not": {
              "type": [
                "array",
                "object"
              ]
            }
          },
          {
            "type": "object",
            "not": {
              "required": [
                "infra_type"
              ]
            }
          },
          {
            "type": "object",
            "$ref": "#/$defs/item"
          },
          {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "not": {
                    "type": "object"
                  }
                },
                {
                  "type": "object",
                  "not": {
                    "anyOf": [
                      {
                        "required": [
                          "infra_type"
                        ]
                      },
                      {
                        "required": [
                          "design"
                        ]
                      },
                      {
                        "required": [
                          "design_template"
                        ]
                      },
                      {
                        "required": [
                          "nbd_loop"
                        ]
                      }
                    ]
                  }
                },
                {
                  "$ref": "#/$defs/item"
                }
              ]
            }
          }
        ]
      },
      "anyOf": [
        {
          "required": [
            "infra_type"
          ]
        },
        {
          "required": [
            "design"
          ]
        },
        {
          "required": [
            "design_template"
          ]
        },
        {
          "required": [
            "nbd_loop",
            "items"
          ]
        }
      ]
    },
    "variable_declaration": {
      "type": "object",
      "description": "A typed variable: what the deploy must give, checked before the design plans.",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "string",
            "int",
            "bool",
            "cidr",
            "netbox_ref",
            "netbox_new",
            "netbox_field"
          ],
          "description": "string, int, bool or cidr for a value of the design's own; netbox_field for a value of a NetBox field (its choices come from the data model); netbox_new for a new object's identifier (its length and pattern come from the data model); netbox_ref to select an object the NetBox has."
        },
        "required": {
          "type": "boolean",
          "description": "Whether the deploy must give it; true when there is no default."
        },
        "default": {
          "description": "The value when the deploy gives none."
        },
        "description": {
          "type": "string",
          "description": "What the input is, shown beside it on the deploy page."
        },
        "model": {
          "type": "string",
          "description": "The object type a netbox_* variable refers to, as infra_type names it: Site, Rack."
        },
        "field": {
          "type": "string",
          "description": "The field of that model, for netbox_field and netbox_new."
        },
        "filter": {
          "type": "object",
          "description": "For netbox_ref: field values the offered objects must have, such as {status: active}."
        },
        "min": {
          "type": "integer",
          "description": "For int: the least value."
        },
        "max": {
          "type": "integer",
          "description": "For int: the greatest value."
        },
        "choices": {
          "type": "array",
          "minItems": 1,
          "description": "For string: the values it may be."
        }
      },
      "required": [
        "type"
      ],
      "additionalProperties": false
    },
    "assertion": {
      "type": "object",
      "description": "What must hold once the plan is applied, in the author's words.",
      "properties": {
        "name": {
          "type": "string",
          "description": "What the assertion is called in the plan's refusal."
        },
        "for_each": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "description": "The object type, as infra_type names it: Interface, VLAN."
            },
            "where": {
              "type": "object",
              "description": "The field values the objects must have. A value may be an expression over variables.* and each; 'present' and 'absent' say whether a reference is set.",
              "additionalProperties": true
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "Check once per object of this type matching where, bound as `each` in count and message."
        },
        "count": {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "description": "The object type, as infra_type names it: Interface, VLAN."
            },
            "where": {
              "type": "object",
              "description": "The field values the objects must have. A value may be an expression over variables.* and each; 'present' and 'absent' say whether a reference is set.",
              "additionalProperties": true
            }
          },
          "required": [
            "type"
          ],
          "additionalProperties": false,
          "description": "The objects counted, in the design and the target together."
        },
        "is": {
          "type": "integer",
          "description": "The count must be exactly this."
        },
        "min": {
          "type": "integer",
          "description": "The count must be at least this."
        },
        "max": {
          "type": "integer",
          "description": "The count must be at most this."
        },
        "message": {
          "type": "string",
          "description": "Said when the assertion fails; may use {{ each.<field> }}, {{ count }} and {{ variables.<name> }}."
        },
        "severity": {
          "type": "string",
          "enum": [
            "error",
            "warning"
          ],
          "description": "error (the default) refuses the plan; warning shows on it and proceeds."
        }
      },
      "required": [
        "count",
        "message"
      ],
      "anyOf": [
        {
          "required": [
            "is"
          ]
        },
        {
          "required": [
            "min"
          ]
        },
        {
          "required": [
            "max"
          ]
        }
      ],
      "additionalProperties": false
    }
  }
}
