From 3a67a77a08aa193115ccc67ea290dc648c875797 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 2 Nov 2023 05:38:19 +0100 Subject: [PATCH] Report number of checked endpoints in the result and warn if the number of specified endpoints in FTL and the OpenAPI specs do not match Signed-off-by: DL6ER --- src/api/docs/content/specs/docs.yaml | 22 +++++++++++++ src/api/docs/content/specs/info.yaml | 4 +-- src/api/docs/content/specs/network.yaml | 2 +- test/api/checkAPI.py | 41 +++++++++++++------------ test/api/libs/responseVerifyer.py | 16 ++++++---- 5 files changed, 56 insertions(+), 29 deletions(-) create mode 100644 src/api/docs/content/specs/docs.yaml diff --git a/src/api/docs/content/specs/docs.yaml b/src/api/docs/content/specs/docs.yaml new file mode 100644 index 00000000..48bbdc6b --- /dev/null +++ b/src/api/docs/content/specs/docs.yaml @@ -0,0 +1,22 @@ +openapi: 3.0.2 +components: + paths: + docs: + summary: Pi-hole's API documentation + get: + summary: Get the embedded API documentation rendered as HTML + tags: + - "Documentation" + operationId: "get_docs" + security: [] + description: | + This API hook returns the embedded API documentation rendered as HTML. + responses: + '200': + description: OK + content: + text/html: + schema: + description: HTML document + type: string + format: binary diff --git a/src/api/docs/content/specs/info.yaml b/src/api/docs/content/specs/info.yaml index 5ae1a756..a3ed864f 100644 --- a/src/api/docs/content/specs/info.yaml +++ b/src/api/docs/content/specs/info.yaml @@ -1193,9 +1193,9 @@ components: required: false example: 219 message_id: - in: query + in: path description: ID of the message to be deleted - name: id + name: message_id schema: type: integer required: true diff --git a/src/api/docs/content/specs/network.yaml b/src/api/docs/content/specs/network.yaml index 27252623..00f74afb 100644 --- a/src/api/docs/content/specs/network.yaml +++ b/src/api/docs/content/specs/network.yaml @@ -285,7 +285,7 @@ components: device_id: in: path description: Device ID - name: id + name: device_id schema: type: integer required: true diff --git a/test/api/checkAPI.py b/test/api/checkAPI.py index b315719a..f8a3003b 100644 --- a/test/api/checkAPI.py +++ b/test/api/checkAPI.py @@ -71,29 +71,30 @@ if __name__ == "__main__": # Verify that all the endpoint defined by GET /api/endpoints are documented # and that there are no undocumented endpoints - print("Verifying the /api/endpoints endpoint...") - verifyer = ResponseVerifyer(ftl, openapi) - errors = verifyer.verify_endpoints() - if len(errors) == 0: - print(" GET /api/endpoints: OK") - else: - print(" Errors:") - for error in errors: - print(" - " + error) - errs[2] += len(errors) + print("Comparing all endpoints defined in FTL against the OpenAPI specs...") + with ResponseVerifyer(ftl, openapi) as verifyer: + errors, checked = verifyer.verify_endpoints() + if len(errors) == 0: + print(" OK (" + str(checked) + " endpoints checked)") + else: + print(" Errors (" + str(checked) + " endpoints checked):") + for error in errors: + print(" - " + error) + errs[2] += len(errors) + print("") # Verify FTL Teleporter import print("Verifying FTL Teleporter import...") - verifyer = ResponseVerifyer(ftl, openapi) - errors = verifyer.verify_teleporter_zip(teleporter) - if len(errors) == 0: - print(" POST /api/teleporter: OK") - else: - print(" Errors:") - for error in errors: - print(" - " + error) - errs[2] += len(errors) - + with ResponseVerifyer(ftl, openapi) as verifyer: + errors = verifyer.verify_teleporter_zip(teleporter) + if len(errors) == 0: + print(" POST /api/teleporter: OK") + else: + print(" Errors:") + for error in errors: + print(" - " + error) + errs[2] += len(errors) + print("") # Print the number error (if any) if errs[0] > 0: diff --git a/test/api/libs/responseVerifyer.py b/test/api/libs/responseVerifyer.py index 090717a7..bbbd5dc4 100644 --- a/test/api/libs/responseVerifyer.py +++ b/test/api/libs/responseVerifyer.py @@ -355,6 +355,8 @@ class ResponseVerifyer(): def verify_endpoints(self): + checked_ftl = 0 + checked_openapi = 0 # Get FTL response authentication_method = random.choice([a for a in AuthenticationMethods]) FTLresponse = self.ftl.GET("/api/endpoints", authenticate = authentication_method) @@ -379,17 +381,13 @@ class ResponseVerifyer(): if method not in self.openapi.METHODS: # Skip keys like "parameters" and "summary" continue - #if "parameters" in self.openapi.paths[endpoint][method]: - # # Construct full URI to check (this is what we specify in OpenAPI specs) - # for param in self.openapi.paths[endpoint][method]["parameters"]: - # if param["in"] == "query": - # endpoint += "?" + param["name"] + "=" + urllib.parse.quote_plus(str(param["example"])) openapi[endpoint][method] = endpoint # Check if FTL reports endpoints not defined in the API specs for method in FTLresponse['endpoints']: for endpoint in FTLresponse['endpoints'][method]: m = method.upper() # type: str + checked_ftl += 1 if endpoint["full_uri"] not in self.openapi.paths or method not in self.openapi.paths[endpoint["full_uri"]]: self.errors.append("Endpoint " + m + " " + endpoint["full_uri"] + " not found in the OpenAPI specs") @@ -397,8 +395,14 @@ class ResponseVerifyer(): for endpoint in openapi: for method in openapi[endpoint]: full_uris = [ep["full_uri"] for ep in FTLresponse['endpoints'][method]] # type: list[str] + checked_openapi += 1 if endpoint not in full_uris: m = method.upper() # type: str self.errors.append("Endpoint " + m + " " + endpoint + " not found in FTL endpoints") - return self.errors + # Check if the number of endpoints checked is the same + if checked_ftl != checked_openapi: + self.errors.append("Number of endpoints checked does not match (FTL " + str(checked_ftl) + " vs. OpenAPI " + str(checked_openapi) + ")") + + checked = max(checked_ftl, checked_openapi) + return self.errors, checked