diff --git a/api/src/graphql_types/mutation.py b/api/src/graphql_types/mutation.py index 9f4941b..095d5e5 100644 --- a/api/src/graphql_types/mutation.py +++ b/api/src/graphql_types/mutation.py @@ -69,6 +69,64 @@ def githubEndpoint(self, endpoint: GithubEndpointInput) -> str: @strawberry.mutation def webEndpoint(self, endpoint: WebEndpointInput) -> str: + """ + # Update/Insert Web Endpoint + + Insert a Web Endpoint with "upsert" semantics. + + # Example + + ```graphql + mutation { + webEndpoint( + endpoint: { + url: "https://some-webapp.canada.ca" + kind: "Web" + accessibility: [ + { + url: "https://some-webapp.canada.ca/about", + areaAlt: { + checkPasses: null, + metadata: { + description: "Ensures elements of image maps have alternate text", + helpUrl: "https://dequeuniversity.com/rules/axe/4.8/area-alt?application=axe-puppeteer" + } + }, + ariaBrailleEquivalent: { + checkPasses: "false", + metadata: { + description: "Ensure aria-braillelabel and aria-brailleroledescription have a non-braille equivalent", + helpUrl: "https://dequeuniversity.com/rules/axe/4.8/aria-braille-equivalent?application=axe-puppeteer" + } + }, + ariaCommandName: { + checkPasses: null, + metadata: { + description: "Ensures every ARIA button, link and menuitem has an accessible name", + helpUrl: "https://dequeuniversity.com/rules/axe/4.8/aria-command-name?application=axe-puppeteer" + } + }, + ariaHiddenFocus: { + checkPasses: "true", + metadata: { + description: "Ensures aria-hidden elements are not focusable nor contain focusable elements", + helpUrl: "https://dequeuniversity.com/rules/axe/4.8/aria-hidden-focus?application=axe-puppeteer" + } + }, + ariaMeterName: { + checkPasses: "incomplete", + metadata: { + description: "Ensures every ARIA meter node has an accessible name", + helpUrl: "https://dequeuniversity.com/rules/axe/4.8/aria-meter-name?application=axe-puppeteer" + } + } + } + ] + } + ) + } + ``` + """ client = GraphDB() client.upsert_scanner_endpoint(endpoint) client.close() diff --git a/api/src/graphql_types/query.py b/api/src/graphql_types/query.py index 76cc360..c0f7f83 100644 --- a/api/src/graphql_types/query.py +++ b/api/src/graphql_types/query.py @@ -4,7 +4,7 @@ from typing import List -from graphql_types.typedef import Endpoint, GithubEndpoint, WebEndpoint +from graphql_types.typedef import Endpoint, GithubEndpoint, WebEndpoint, Accessibility, AccessibilityCheckPasses @strawberry.type @@ -12,7 +12,7 @@ class Query: @strawberry.field def github_endpoint(self, url: str) -> GithubEndpoint: """ - # Get properties of a Single Github Endpoint + # Get Properties of a Single Github Endpoint Given a url, retrieves the properties of a single Github Endpoint. @@ -70,6 +70,107 @@ def github_endpoints(self, limit: int) -> List[GithubEndpoint]: endpoint.pop("_id", None) endpoint.pop("_rev", None) return [GithubEndpoint(**endpoint) for endpoint in endpoints] + + @strawberry.field + def web_endpoint(self, url: str) -> WebEndpoint: + """ + # Get Properties of a Single Web Endpoint + + Given a url, retrieves the properties of a single Web Endpoint. + + # Example + + ```graphql + query { + webEndpoint(url: "https://safeinputs.phac.alpha.canada.ca") { + url + accessibility { + url + areaAlt { + checkPasses + metadata + } + } + } + } + ``` + """ + client = GraphDB() + endpoint = client.get_scanner_endpoint(url) + client.close() + # Remove unecessary db fields from the endpoint dict + endpoint.pop("_id", None) + endpoint.pop("_rev", None) + # Strawberry doesn't recursively resolve fields. The code below + # is a workaround to recursively resolve the accessibility field + # and the "check passes" fields contained within. + return WebEndpoint( + url=endpoint['url'], + kind=endpoint['kind'], + _key=endpoint['_key'], + accessibility=[ + Accessibility(**{ + k: AccessibilityCheckPasses(**v) + for k, v in ep.items() + if type(v) is not str + }, + url=ep['url']) + for ep in endpoint['accessibility'] + ] + ) + + @strawberry.field + def web_endpoints(self, limit: int) -> List[WebEndpoint]: + """ + # Get Multiple Web Endpoints + + Retrieves a list of Web Endpoints. The number of endpoints returned is + determined by the `limit` parameter. + + # Example + + ```graphql + query { + webEndpoints(limit: 10) { + url + accessibility { + url + areaAlt { + checkPasses + metadata + } + } + } + } + ``` + """ + client = GraphDB() + endpoints = client.get_scanner_endpoints("Web", limit) + client.close() + # Remove unecessary db fields from the endpoint dict + for endpoint in endpoints: + endpoint.pop("_id", None) + endpoint.pop("_rev", None) + # Strawberry doesn't recursively resolve fields. The code below + # is a workaround to recursively resolve the accessibility field + # and the "check passes" fields contained within. + return [ + WebEndpoint( + url=endpoint['url'], + kind=endpoint['kind'], + _key=endpoint['_key'], + accessibility=[ + Accessibility(**{ + k: AccessibilityCheckPasses(**v) + for k, v in ep.items() + if type(v) is not str + }, + url=ep['url']) + for ep in endpoint['accessibility'] + ] + ) + for endpoint in endpoints + ] @strawberry.field def endpoints(self, urls: List[str]) -> List[Endpoint]: diff --git a/api/src/graphql_types/typedef.py b/api/src/graphql_types/typedef.py index 4d994aa..0c9f3eb 100644 --- a/api/src/graphql_types/typedef.py +++ b/api/src/graphql_types/typedef.py @@ -21,6 +21,11 @@ class CheckPasses: check_passes: Optional[bool] metadata: Optional[JSON] +@strawberry.type +class AccessibilityCheckPasses: + check_passes: Optional[str] # true, false, incomplete, or null + metadata: Optional[JSON] + @strawberry.type class GithubEndpoint(Endpoint): url: str @@ -38,13 +43,13 @@ class GithubEndpoint(Endpoint): has_dependabot_yaml: CheckPasses @strawberry.type -class AccessibilityInput: +class Accessibility: url: str area_alt: Optional[CheckPasses] aria_braille_equivalent: Optional[CheckPasses] aria_command_name: Optional[CheckPasses] aria_hidden_focus: Optional[CheckPasses] - aria_input_field: Optional[CheckPasses] + aria_input_field_name: Optional[CheckPasses] aria_meter_name: Optional[CheckPasses] aria_progressbar_name: Optional[CheckPasses] aria_required_children: Optional[CheckPasses] @@ -76,7 +81,7 @@ class AccessibilityInput: server_side_image_map: Optional[CheckPasses] svg_img_alt: Optional[CheckPasses] td_headers_attr: Optional[CheckPasses] - td_has_data_cells: Optional[CheckPasses] + th_has_data_cells: Optional[CheckPasses] valid_lang: Optional[CheckPasses] video_caption: Optional[CheckPasses] no_autoplay_audio: Optional[CheckPasses] @@ -105,4 +110,5 @@ class AccessibilityInput: class WebEndpoint(Endpoint): url: str kind: str - accessibility: Optional[List[AccessibilityInput]] + _key: str + accessibility: Optional[List[Accessibility]]