Plugin Schemas¶
Plugins define their desired form of their config using draft 4 of the
JSON Schema specification. The schema for a plugin
should be stored schema
attribute of the plugin class. The schema is used
for several things including:
Validating the config format matches what is expected
Set defaults in the config that user did not provide
Generating a form for the webui config editor
You can run the test_config_schema.py test in the suite to test the validity of your plugin’s schema. The error messages it produces may help you fix your schema if you are having trouble. Note that this doesn’t check the schema validates what you want, just that it is a valid json schema.
The following list of keywords is not exhaustive, just a general primer, as well as some FlexGet specific notes. The JSON schema spec should be referred to for more details, or if a keyword is not covered here. Take not that our schemas will be defined as python objects equivalent to parsed JSON. The full list of valid keywords can be found in section 5 of the validaton spec.
Keywords¶
type
¶
The type
keyword specifies what primitive data type a given item in the
config should have. It can either be a single type, or a list of types. The
type
keyword should be specified in almost every schema, even when other
keywords are included which might make it redundant, as it is used to select
which branch should be chosen to show errors when the config can take multiple
forms. The types in JSON schema are slightly different than their python
counterparts. Here are the valid options, along with the python types they map
to:
JSON Schema type |
Python type |
---|---|
string |
unicode |
boolean |
bool |
number |
float |
integer |
int |
array |
list |
object |
dict |
null |
type(None) |
items
¶
This keyword is used to validate the content of a list (‘array’ in json schema terms.) Its value should be a schema that each item in the list matches.
The following example describes a list of strings:
{"type": "array", "items": {"type": "string"}}
properties
¶
This keyword is used to validate the values in a dictionary. It should be a dictionary mapping from key name, to schema which validates the value for that key.
The following example describes a dictionary with two keys, ‘a’, and ‘b’, both
of which must be integers (additionalProperties
will be explained below):
{
"type": "object",
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"}
},
"additionalProperties": False
}
additionalProperties
¶
By default, JSON schema will allow any keys which are not defined in the
properties
dictionary without validation. To disallow extra keys, use the
{"additionalProperties": False}
form, as in the above example. This should
be used in almost every schema which defines the properties
keyword. The
other use for this keyword is if you want to allow a dictionary with any keys,
but still require the values to match a schema.
The following example allows a dictionary with any keys, as long as the values are strings:
{
"type": "object",
"additionalProperties": {"type": "string"}
}
oneOf
and anyOf
¶
These keywords are used when the config could take more than one format. The value should be a list of schemas one of which, or any of which must match, depending on the keyword used.
The following schema will allow either a boolean or an integer:
{"oneOf": [{"type": "boolean"}, {"type": "integer"}]}
format
¶
The format keyword is used to make sure a string follows a specific format. Here are the format validators included with FlexGet, along with what they validate:
email addresses
- quality
FlexGet quality, e.g.
720p hdtv
- quality_requirements
FlexGet quality requirements specifier, e.g.
720p-1080p hdtv+
- interval
A text representation of a time interval, e.g.
3 hours
,10 minutes
Intervals in this format can be parsed to adatetime.timedelta
object using the utility functionflexget.utils.tools.parse_timedelta()
- regex
valid regular expression
- file
an existing file on the local filesystem
- path
an existing directory on the local filesystem (if path contains Jinja, only validates path exists before first Jinja component of path)
The following schema checks for valid regex:
{"type": "string", "format": "regex"}
$ref
¶
This keyword is used to reference a schema defined somewhere else. The most common use of this keyword will be to allow a plugin to take other plugins within their configuration. It takes the form of an URI reference. The fragment part should be a JSON pointer to a section of the referenced document. If only a fragment portion of an URI is specified, the base document is assumed to be the current schema.
The following schema allows a dictionary with keys equal to plugin names (which have input phase handlers,) and values equal to the configuration required for that plugin. We don’t actually define the validation keywords here, we are just referencing an already built schema which has been registered by some other plugin or component of FlexGet:
{"$ref": "/schema/plugins?phase=input"}
definitions
¶
This keyword does not affect validation, it is merely used to define parts of your schema that may get re-used in more than one place. It should be in the form of a dictionary mapping arbitrary names to a schema.
The following schema defines a definition called posNumber, and references it from two places within the schema:
{
"type": "object",
"properties": {
"numberA": {"$ref": "#/definitions/posNumber"},
"numberB": {"$ref": "#/definitions/posNumber"}
},
"additionalProperties": False,
"definitions": {
"posNumber": {"type": "number", "minimum": 0}
}
}
The $ref
used in this example included a fragment part of an URI only, so
it references this schema, and drills down into it with a JSON pointer.
title
and description
¶
The title
and description
keywords are not used during validation at
all. If provided, they will be used to display more information to the user
in the configuration editor.
default
¶
The default
keyword is not used during validation either. It will be used
to fill in default values for properties in the config that the user has not
provided. This will be done automatically before the parsed config is passed
to the plugin.
not
¶
The not
keyword will allow you to negate a specific schema. This is especially useful when wanting to create
mutually exclusive properties or groups:
{
"type": "object",
"properties": {
"this": {"type": "string"},
"that": {"type": "string"}
},
"not": {
"required": ["this", "that"]
},
"error_not": "Can not use both 'this' and 'that'
}
Another more complex example:
{
"type": "object",
"properties": {
"this": {"type": "string"},
"that": {"type": "string"},
"those": {"type": "string"}
},
"not": {
"anyOf": [
"required": ["this", "that"],
"required": ["this", "those"],
"required": ["that", "those"]
]
},
"error_not": "Can only use one of 'this', 'that' or 'those'
}
dependencies
¶
dependencies
are used to link a property to one or more other property, raising a validation error if not all
dependencies have been met:
{
"type": "object",
"properties": {
"this": {"type": "string"},
"that": {"type": "string"},
"another" {"type": "string"}
},
"dependencies": {
"this": ["that"]
}
}