Merge pull request #11 from rrev/schema
JSON file validation using JSON Schema file
This commit is contained in:
Коммит
e634693764
|
@ -2,4 +2,5 @@ language: python
|
|||
python:
|
||||
- "2.7"
|
||||
|
||||
install: "pip install -r requirements.txt"
|
||||
script: python generate_devices_list.py
|
|
@ -0,0 +1,140 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"show-by-default": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"modes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"orientation": {
|
||||
"enum": ["vertical", "horizontal"]
|
||||
},
|
||||
"images": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/image"
|
||||
}
|
||||
},
|
||||
"page-rect": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"left": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"top": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": ["left", "top", "width", "height"]
|
||||
}
|
||||
},
|
||||
"required": ["title", "orientation"]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"screen": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"horizontal": {
|
||||
"$ref": "#/definitions/orientation"
|
||||
},
|
||||
"vertical": {
|
||||
"$ref": "#/definitions/orientation"
|
||||
},
|
||||
"device-pixel-ratio": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
}
|
||||
},
|
||||
"required": ["device-pixel-ratio", "vertical", "horizontal"]
|
||||
},
|
||||
"capabilities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": ["mobile", "touch"]
|
||||
}
|
||||
},
|
||||
"user-agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": ["phone", "tablet", "notebook", "desktop", "unknown"]
|
||||
}
|
||||
},
|
||||
"required": ["title", "type", "screen", "user-agent", "show-by-default"],
|
||||
"definitions": {
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"src": {
|
||||
"type": "string"
|
||||
},
|
||||
"scale": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": ["src", "scale"]
|
||||
},
|
||||
"orientation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 10000
|
||||
},
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 10000
|
||||
},
|
||||
"outline": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"images": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/image"
|
||||
}
|
||||
},
|
||||
"insets": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"left": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"top": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["images", "insets"]
|
||||
}
|
||||
},
|
||||
"required": ["width", "height"]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import numbers
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
|
@ -10,7 +9,7 @@ try:
|
|||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
POSSIBLE_TYPES = ["phone", "tablet", "notebook", "desktop", "unknown"]
|
||||
import jsonschema
|
||||
|
||||
def load_and_parse_json(file_name):
|
||||
try:
|
||||
|
@ -20,107 +19,32 @@ def load_and_parse_json(file_name):
|
|||
print 'ERROR: Failed to parse %s' % file_name
|
||||
raise
|
||||
|
||||
def raise_type_error(file_name, key, expected_type):
|
||||
raise Exception('ERROR: "' + key + '" must be of type "' + expected_type + '" (' + file_name + ')')
|
||||
|
||||
def parse_and_rebase_images(images, file_name, prefix, rebase_path):
|
||||
for (index, entry) in enumerate(images):
|
||||
i = str(index)
|
||||
if not isinstance(entry, dict):
|
||||
raise_type_error(file_name, prefix + "[" + i + "]", "object")
|
||||
if not ("src" in entry) or not isinstance(entry["src"], basestring):
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/src", "string")
|
||||
if not ("scale" in entry) or not isinstance(entry["scale"], numbers.Number):
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/scale", "number")
|
||||
def rebase_images(images, rebase_path):
|
||||
for entry in images:
|
||||
entry["src"] = os.path.join(rebase_path, entry["src"])
|
||||
|
||||
def parse_orientation(d, file_name, prefix, rebase_path):
|
||||
if not ("width" in d) or not isinstance(d["width"], numbers.Number) or d["width"] < 0 or d["width"] > 10000 or abs(d["width"]) != d["width"]:
|
||||
raise_type_error(file_name, prefix + "/width", "number")
|
||||
if not ("height" in d) or not isinstance(d["height"], numbers.Number) or d["height"] < 0 or d["height"] > 10000 or abs(d["height"]) != d["height"]:
|
||||
raise_type_error(file_name, prefix + "/height", "number")
|
||||
|
||||
def parse_orientation(d, rebase_path):
|
||||
if not ("outline" in d):
|
||||
return
|
||||
outline = d["outline"]
|
||||
if not isinstance(outline, dict):
|
||||
raise_type_error(file_name, prefix + "/outline", "object")
|
||||
if not ("images" in outline) or not isinstance(outline["images"], (list, tuple)):
|
||||
raise_type_error(file_name, prefix + "/outline/images", "array")
|
||||
parse_and_rebase_images(outline["images"], file_name, prefix + "/outline/images", rebase_path)
|
||||
if not ("insets" in outline) or not isinstance(outline["insets"], dict):
|
||||
raise_type_error(file_name, prefix + "/outline/insets", "object")
|
||||
if not ("left" in outline["insets"]) or not isinstance(outline["insets"]["left"], numbers.Number) or outline["insets"]["left"] < 0:
|
||||
raise_type_error(file_name, prefix + "/outline/insets/left", "number")
|
||||
if not ("top" in outline["insets"]) or not isinstance(outline["insets"]["top"], numbers.Number) or outline["insets"]["top"] < 0:
|
||||
raise_type_error(file_name, prefix + "/outline/insets/top", "number")
|
||||
|
||||
def parse_modes(modes, file_name, prefix, rebase_path):
|
||||
for (index, mode) in enumerate(modes):
|
||||
i = str(index)
|
||||
if not isinstance(mode, dict):
|
||||
raise_type_error(file_name, prefix + "[" + i + "]", "object")
|
||||
if not ("title" in mode) or not isinstance(mode["title"], basestring):
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/title", "string")
|
||||
if not ("orientation" in mode) or not isinstance(mode["orientation"], basestring):
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/orientation", "string")
|
||||
if mode["orientation"] != "vertical" and mode["orientation"] != "horizontal":
|
||||
raise Exception('ERROR: "' + prefix + '[' + i + ']/orientation" must be one of the following: [horizontal, vertical] (' + file_name + ')')
|
||||
rebase_images(d["outline"]["images"], rebase_path)
|
||||
|
||||
if not ("images" in mode) or not isinstance(mode["images"], (list, tuple)):
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/images", "array")
|
||||
parse_and_rebase_images(mode["images"], file_name, prefix + "[" + i + "]/images", rebase_path)
|
||||
|
||||
if not ("page-rect" in mode) or not isinstance(mode["page-rect"], dict):
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/page-rect", "object")
|
||||
if not ("left" in mode["page-rect"]) or not isinstance(mode["page-rect"]["left"], numbers.Number) or mode["page-rect"]["left"] < 0:
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/page-rect/left", "object")
|
||||
if not ("top" in mode["page-rect"]) or not isinstance(mode["page-rect"]["top"], numbers.Number) or mode["page-rect"]["top"] < 0:
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/page-rect/top", "object")
|
||||
if not ("width" in mode["page-rect"]) or not isinstance(mode["page-rect"]["width"], numbers.Number) or mode["page-rect"]["width"] < 0:
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/page-rect/width", "object")
|
||||
if not ("height" in mode["page-rect"]) or not isinstance(mode["page-rect"]["height"], numbers.Number) or mode["page-rect"]["height"] < 0:
|
||||
raise_type_error(file_name, prefix + "[" + i + "]/page-rect/height", "object")
|
||||
def parse_modes(modes, rebase_path):
|
||||
for mode in modes:
|
||||
rebase_images(mode["images"], rebase_path)
|
||||
|
||||
def parse_device_json(file_name, rebase_path):
|
||||
json = load_and_parse_json(file_name)
|
||||
device_file_schema = load_and_parse_json("device_schema.json")
|
||||
jsonschema.validate(json, device_file_schema)
|
||||
|
||||
if not ("title" in json) or not isinstance(json["title"], basestring):
|
||||
raise_type_error(file_name, "title", "string")
|
||||
screen = json["screen"]
|
||||
|
||||
if not ("type" in json) or not isinstance(json["type"], basestring):
|
||||
raise_type_error(file_name, "type", "string")
|
||||
if not (json["type"] in POSSIBLE_TYPES):
|
||||
raise Exception('ERROR: "type" must be one of the following: [' + ", ".join(POSSIBLE_TYPES) + '] (' + file_name + ')')
|
||||
|
||||
if not ("user-agent" in json) or not isinstance(json["user-agent"], basestring):
|
||||
raise_type_error(file_name, "user-agent", "string")
|
||||
|
||||
if not ("show-by-default" in json) or not isinstance(json["show-by-default"], bool):
|
||||
raise_type_error(file_name, "show-by-default", "boolean")
|
||||
|
||||
if not ("capabilities" in json) or not isinstance(json["capabilities"], (list, tuple)):
|
||||
raise_type_error(file_name, "capabilities", "array")
|
||||
for cap in json["capabilities"]:
|
||||
if not isinstance(cap, basestring):
|
||||
raise_type_error(file_name, "capability", "string")
|
||||
|
||||
if ("screen" in json) and isinstance(json["screen"], dict):
|
||||
screen = json["screen"]
|
||||
if not ("device-pixel-ratio" in screen) or not isinstance(screen["device-pixel-ratio"], numbers.Number) or screen["device-pixel-ratio"] < 0 or screen["device-pixel-ratio"] > 100:
|
||||
raise_type_error(file_name, "screen/device-pixel-ratio", "number")
|
||||
|
||||
for orientation in ["vertical", "horizontal"]:
|
||||
if not (orientation in screen) or not isinstance(screen[orientation], dict):
|
||||
raise_type_error(file_name, "screen/" + orientation, "object")
|
||||
parse_orientation(screen[orientation], file_name, "screen/" + orientation, rebase_path)
|
||||
else:
|
||||
raise_type_error(file_name, "screen", "object")
|
||||
for orientation in ["vertical", "horizontal"]:
|
||||
parse_orientation(screen[orientation], rebase_path)
|
||||
|
||||
if "modes" in json:
|
||||
if not isinstance(json["modes"], (list, tuple)):
|
||||
raise_type_error(file_name, "modes", "array")
|
||||
parse_modes(json["modes"], file_name, "modes", rebase_path)
|
||||
parse_modes(json["modes"], rebase_path)
|
||||
|
||||
return json
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
jsonschema==2.4.0
|
Загрузка…
Ссылка в новой задаче