mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
Implement deep-recursion of API arrays
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
@@ -246,8 +246,9 @@ components:
|
||||
example: "127.0.0.1,::1"
|
||||
names:
|
||||
type: string
|
||||
description: Comma-separated list of hostnames
|
||||
example: "localhost"
|
||||
nullable: true
|
||||
description: Comma-separated list of hostnames (if available)
|
||||
example: "localhost,ip6-localhost"
|
||||
client:
|
||||
type: object
|
||||
properties:
|
||||
@@ -284,6 +285,7 @@ components:
|
||||
description: hostname (only available when {client} is an IP address)
|
||||
type: string
|
||||
readOnly: true
|
||||
nullable: true
|
||||
example: localhost
|
||||
groups:
|
||||
type: object
|
||||
|
||||
@@ -37,25 +37,118 @@ components:
|
||||
get:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["/api/dns/blocking", "/api/dns/cache", "/api/dns/port", "/api/domains", "/api/groups", "/api/lists"]
|
||||
type: object
|
||||
properties:
|
||||
uri:
|
||||
type: string
|
||||
description: URI
|
||||
parameters:
|
||||
type: string
|
||||
description: Parameters
|
||||
example:
|
||||
- uri: "/api/dns/blocking"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/cache"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/port"
|
||||
parameters: ''
|
||||
- uri: "/api/domains"
|
||||
parameters: ''
|
||||
- uri: "/api/groups"
|
||||
parameters: ''
|
||||
- uri: "/api/lists"
|
||||
parameters: ''
|
||||
post:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["/api/dns/blocking", "/api/dns/cache", "/api/dns/port", "/api/domains", "/api/groups", "/api/lists"]
|
||||
type: object
|
||||
properties:
|
||||
uri:
|
||||
type: string
|
||||
description: URI
|
||||
parameters:
|
||||
type: string
|
||||
description: Parameters
|
||||
example:
|
||||
- uri: "/api/dns/blocking"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/cache"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/port"
|
||||
parameters: ''
|
||||
- uri: "/api/domains"
|
||||
parameters: ''
|
||||
- uri: "/api/groups"
|
||||
parameters: ''
|
||||
- uri: "/api/lists"
|
||||
parameters: ''
|
||||
put:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["/api/dns/blocking", "/api/dns/cache", "/api/dns/port", "/api/domains", "/api/groups", "/api/lists"]
|
||||
type: object
|
||||
properties:
|
||||
uri:
|
||||
type: string
|
||||
description: URI
|
||||
parameters:
|
||||
type: string
|
||||
description: Parameters
|
||||
example:
|
||||
- uri: "/api/dns/blocking"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/cache"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/port"
|
||||
parameters: ''
|
||||
- uri: "/api/domains"
|
||||
parameters: ''
|
||||
- uri: "/api/groups"
|
||||
parameters: ''
|
||||
- uri: "/api/lists"
|
||||
parameters: ''
|
||||
patch:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["/api/dns/blocking", "/api/dns/cache", "/api/dns/port", "/api/domains", "/api/groups", "/api/lists"]
|
||||
properties:
|
||||
uri:
|
||||
type: string
|
||||
description: URI
|
||||
parameters:
|
||||
type: string
|
||||
description: Parameters
|
||||
example:
|
||||
- uri: "/api/dns/blocking"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/cache"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/port"
|
||||
parameters: ''
|
||||
- uri: "/api/domains"
|
||||
parameters: ''
|
||||
- uri: "/api/groups"
|
||||
parameters: ''
|
||||
- uri: "/api/lists"
|
||||
parameters: ''
|
||||
delete:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["/api/dns/blocking", "/api/dns/cache", "/api/dns/port", "/api/domains", "/api/groups", "/api/lists"]
|
||||
properties:
|
||||
uri:
|
||||
type: string
|
||||
description: URI
|
||||
parameters:
|
||||
type: string
|
||||
description: Parameters
|
||||
example:
|
||||
- uri: "/api/dns/blocking"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/cache"
|
||||
parameters: ''
|
||||
- uri: "/api/dns/port"
|
||||
parameters: ''
|
||||
- uri: "/api/domains"
|
||||
parameters: ''
|
||||
- uri: "/api/groups"
|
||||
parameters: ''
|
||||
- uri: "/api/lists"
|
||||
parameters: ''
|
||||
|
||||
@@ -505,7 +505,7 @@ components:
|
||||
type: number
|
||||
nullable: true
|
||||
description: Critical sensor value (if available, `null` otherwise)
|
||||
path:
|
||||
sensor:
|
||||
type: string
|
||||
description: Short path of temperature sensor
|
||||
example:
|
||||
|
||||
@@ -126,6 +126,9 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Interface name
|
||||
default:
|
||||
type: boolean
|
||||
description: If the interface is the default gateway
|
||||
carrier:
|
||||
type: boolean
|
||||
description: If the interface is connected
|
||||
@@ -248,6 +251,7 @@ components:
|
||||
name:
|
||||
type: string
|
||||
description: Associated hostname (can be null)
|
||||
nullable: true
|
||||
example: ubuntu-server
|
||||
lastSeen:
|
||||
type: integer
|
||||
|
||||
@@ -139,8 +139,8 @@ class ResponseVerifyer():
|
||||
|
||||
# Check for properties in FTL that are not in the API specs
|
||||
for property in FTLflat.keys():
|
||||
if property not in YAMLflat.keys():
|
||||
self.errors.append("Property '" + property + "' missing in the API specs")
|
||||
if property not in YAMLflat.keys() and len([p.startswith(property + ".") for p in YAMLflat.keys()]) == 0:
|
||||
self.errors.append("Property '" + property + "' missing in the API specs (have " + ",".join(YAMLflat.keys()) + ")")
|
||||
|
||||
elif expected_mimetype == "application/zip":
|
||||
file_like_object = io.BytesIO(FTLresponse)
|
||||
@@ -214,7 +214,7 @@ class ResponseVerifyer():
|
||||
all_okay = True
|
||||
|
||||
# Build flat path of this property
|
||||
flat_path = ".".join(props)
|
||||
flat_path = ".".join([str(p) for p in props])
|
||||
|
||||
# Check if the property is defined in the API specs
|
||||
if props[-1] not in YAMLprops:
|
||||
@@ -235,6 +235,43 @@ class ResponseVerifyer():
|
||||
# ... and check them recursively
|
||||
if not self.verify_property(YAMLprop['properties'], YAMLexamples, FTLprop, props + [prop]):
|
||||
all_okay = False
|
||||
elif YAMLprop['type'] == 'array':
|
||||
# Check if the FTL response is an array
|
||||
if type(FTLprop) is not list:
|
||||
self.errors.append("FTL's response is not an array in " + flat_path)
|
||||
return False
|
||||
# Check if the FTL response has the same number of items as the
|
||||
# YAML examples
|
||||
elif YAMLexamples is not None:
|
||||
for t in YAMLexamples:
|
||||
if 'value' not in YAMLexamples[t]:
|
||||
self.errors.append(f"Example {flat_path} does not have a 'value' property")
|
||||
return False
|
||||
example = YAMLexamples[t]['value']
|
||||
# Dive into the example to get to the property we want
|
||||
example_part = example
|
||||
for p in props:
|
||||
if p not in example_part:
|
||||
self.errors.append(f"Example {t} is missing '{flat_path}'")
|
||||
return False
|
||||
example_part = example_part[p]
|
||||
# Loop over all items in the array ...
|
||||
for i in range(len(FTLprop)):
|
||||
# ... and check them recursively if they are objects
|
||||
if not type(FTLprop[i]) is dict:
|
||||
if 'properties' in YAMLprop['items']:
|
||||
self.errors.append(flat_path + " is an array, but the API specs define it as an array of objects")
|
||||
return False
|
||||
else:
|
||||
# Simple array and declared as such, no need for further recursion
|
||||
continue
|
||||
if 'properties' not in YAMLprop['items'] and type(FTLprop[i]) is dict:
|
||||
self.errors.append(flat_path + " is an array of objects, but the API specs define it as a simple array")
|
||||
return False
|
||||
|
||||
for j in FTLprop[i]:
|
||||
if not self.verify_property(YAMLprop['items']['properties'], YAMLexamples, FTLprop[i], props + [i, str(j)]):
|
||||
all_okay = False
|
||||
else:
|
||||
# Check this property
|
||||
|
||||
@@ -266,11 +303,18 @@ class ResponseVerifyer():
|
||||
return False
|
||||
example = YAMLexamples[t]['value']
|
||||
# Dive into the example to get to the property we want
|
||||
skip_this = False
|
||||
for p in props:
|
||||
if p not in example:
|
||||
if type(example) == dict and p not in example:
|
||||
self.errors.append(f"Example {t} does not have an '{p}' item")
|
||||
return False
|
||||
if type(example) == list and p >= len(example):
|
||||
# We're out of bounds, so we can't check this example
|
||||
skip_this = True
|
||||
break
|
||||
example = example[p]
|
||||
if skip_this:
|
||||
continue
|
||||
# Check if the type of the example matches the type we defined in the API specs
|
||||
example_type = type(example)
|
||||
self.YAMLresponse[flat_path].append(example)
|
||||
|
||||
Reference in New Issue
Block a user