blob: d232a4ffe124ef8f31b9abd7ec3fcfcf091d605e [file] [log] [blame]
#!/usr/bin/python
"""
This script reads in JSON files containing a set of expected entries to be
found within a given input JSON file. An optional "filtering" JSON
file may also be provided that contains a set of data that will be used to
filter configured expected entries from being checked in the input JSON.
"""
import os
import sys
import json
from argparse import ArgumentParser
def findEntry(entry, jsonObject):
if isinstance(jsonObject, dict):
for key in jsonObject:
if key == entry:
return jsonObject[key]
else:
found = findEntry(entry, jsonObject[key])
if found:
return found
def buildDict(entry, jsonObject, resultDict):
key = list(entry)[0]
jsonObject = findEntry(key, jsonObject)
if jsonObject is None:
return {}
entry = entry[key]
if isinstance(entry, dict):
resultDict[key] = buildDict(entry, jsonObject, resultDict)
else:
return {key: jsonObject}
def doAnd(andList, filters):
allTrue = True
for entry in andList:
# $and entries must be atleast a single layer dict
value = dict()
buildDict(entry, filters, value)
if value != entry:
allTrue = False
return allTrue
def doOr(orList, filters):
anyTrue = False
for entry in orList:
# $or entries must be atleast a single layer dict
value = dict()
buildDict(entry, filters, value)
if value == entry:
anyTrue = True
break
return anyTrue
def doNor(norList, filters):
allFalse = True
for entry in norList:
# $nor entries must be atleast a single layer dict
value = dict()
buildDict(entry, filters, value)
if value == entry:
allFalse = False
return allFalse
def doNot(notDict, filters):
# $not entry must be atleast a single layer dict
value = dict()
buildDict(notDict, filters, value)
if value == notDict:
return False
return True
def applyFilters(expected, filters):
switch = {
# $and - Performs an AND operation on an array with at least two
# expressions and returns the document that meets all the
# expressions. i.e.) {"$and": [{"age": 5}, {"name": "Joe"}]}
"$and": doAnd,
# $or - Performs an OR operation on an array with at least two
# expressions and returns the documents that meet at least one of
# the expressions. i.e.) {"$or": [{"age": 4}, {"name": "Joe"}]}
"$or": doOr,
# $nor - Performs a NOR operation on an array with at least two
# expressions and returns the documents that do not meet any of
# the expressions. i.e.) {"$nor": [{"age": 3}, {"name": "Moe"}]}
"$nor": doNor,
# $not - Performs a NOT operation on the specified expression and
# returns the documents that do not meet the expression.
# i.e.) {"$not": {"age": 4}}
"$not": doNot
}
isExpected = {}
for entry in expected:
expectedList = list()
if entry == "$op":
addInput = True
for op in expected[entry]:
if op != "$input":
func = switch.get(op)
if not func(expected[entry][op], filters):
addInput = False
if addInput:
expectedList = expected[entry]["$input"]
else:
expectedList = [dict({entry: expected[entry]})]
for i in expectedList:
for key in i:
isExpected[key] = i[key]
return isExpected
def findExpected(expected, input):
result = {}
for key in expected:
jsonObject = findEntry(key, input)
if isinstance(expected[key], dict) and expected[key] and jsonObject:
notExpected = findExpected(expected[key], jsonObject)
if notExpected:
result[key] = notExpected
else:
# If expected value is not "dont care" and
# does not equal what's expected
if str(expected[key]) != "{}" and expected[key] != jsonObject:
if jsonObject is None:
result[key] = None
else:
result[key] = expected[key]
return result
if __name__ == '__main__':
parser = ArgumentParser(
description="Expected JSON cross-checker. Similar to a JSON schema \
validator, however this cross-checks a set of expected \
property states against the contents of a JSON input \
file with the ability to apply an optional set of \
filters against what's expected based on the property \
states within the provided filter JSON.")
parser.add_argument('index',
help='Index name into a set of entries within the \
expected JSON file')
parser.add_argument('expected_json',
help='JSON input file containing the expected set of \
entries, by index name, to be contained within \
the JSON input file')
parser.add_argument('input_json',
help='JSON input file containing the JSON data to be \
cross-checked against what is expected')
parser.add_argument('-f', '--filters', dest='filter_json',
help='JSON file containing path:property:value \
associations to optional filters configured \
within the expected set of JSON entries')
args = parser.parse_args()
with open(args.expected_json, 'r') as expected_json:
expected = json.load(expected_json) or {}
with open(args.input_json, 'r') as input_json:
input = json.load(input_json) or {}
filters = {}
if args.filter_json:
with open(args.filter_json, 'r') as filters_json:
filters = json.load(filters_json) or {}
if args.index in expected and expected[args.index] is not None:
expected = applyFilters(expected[args.index], filters)
result = findExpected(expected, input)
if result:
print("NOT FOUND:")
for key in result:
print(key + ": " + str(result[key]))
else:
print("Error: " + args.index + " not found in " + args.expected_json)
sys.exit(1)