From e94f3ea08f1b5298fd503a33110447a3db0b0c04 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 23 Oct 2023 08:18:00 -0700 Subject: [PATCH 01/11] [atproxy] Remove atproxy (#283) Remove atproxy --- Makefile | 2 - assets/atproxy_sequence.puml | 21 - assets/generated/atproxy_sequence.png | Bin 32613 -> 0 bytes assets/generated/local_uss_qualifier.png | Bin 251187 -> 262924 bytes assets/local_uss_qualifier.gv | 10 - monitoring/README.md | 8 - monitoring/atproxy/README.md | 66 - monitoring/atproxy/__init__.py | 0 monitoring/atproxy/app/__init__.py | 12 - .../atproxy/atproxy.postman_collection.json | 1071 ----------------- monitoring/atproxy/config.py | 39 - monitoring/atproxy/database.py | 56 - monitoring/atproxy/gunicorn.conf.py | 36 - monitoring/atproxy/handling.py | 97 -- monitoring/atproxy/health_check.sh | 7 - monitoring/atproxy/oauth.py | 9 - monitoring/atproxy/requests.py | 108 -- monitoring/atproxy/routes.py | 70 -- monitoring/atproxy/routes_handler.py | 75 -- monitoring/atproxy/routes_rid_injection.py | 43 - monitoring/atproxy/routes_rid_observation.py | 34 - monitoring/atproxy/routes_scd.py | 86 -- monitoring/atproxy/run_locally.sh | 43 - monitoring/atproxy/start.sh | 30 - monitoring/mock_uss/README.md | 1 - monitoring/mock_uss/__init__.py | 5 - monitoring/mock_uss/atproxy_client/README.md | 1 - .../mock_uss/atproxy_client/__init__.py | 1 - monitoring/mock_uss/atproxy_client/config.py | 31 - monitoring/mock_uss/atproxy_client/daemon.py | 203 ---- monitoring/mock_uss/docker-compose.yaml | 48 - .../dev/library/environment_containers.yaml | 5 - .../dev/library/environment_localhost.yaml | 5 - requirements.txt | 1 - 34 files changed, 2224 deletions(-) delete mode 100644 assets/atproxy_sequence.puml delete mode 100644 assets/generated/atproxy_sequence.png delete mode 100644 monitoring/atproxy/README.md delete mode 100644 monitoring/atproxy/__init__.py delete mode 100644 monitoring/atproxy/app/__init__.py delete mode 100644 monitoring/atproxy/atproxy.postman_collection.json delete mode 100644 monitoring/atproxy/config.py delete mode 100644 monitoring/atproxy/database.py delete mode 100644 monitoring/atproxy/gunicorn.conf.py delete mode 100644 monitoring/atproxy/handling.py delete mode 100755 monitoring/atproxy/health_check.sh delete mode 100644 monitoring/atproxy/oauth.py delete mode 100644 monitoring/atproxy/requests.py delete mode 100644 monitoring/atproxy/routes.py delete mode 100644 monitoring/atproxy/routes_handler.py delete mode 100644 monitoring/atproxy/routes_rid_injection.py delete mode 100644 monitoring/atproxy/routes_rid_observation.py delete mode 100644 monitoring/atproxy/routes_scd.py delete mode 100755 monitoring/atproxy/run_locally.sh delete mode 100755 monitoring/atproxy/start.sh delete mode 100644 monitoring/mock_uss/atproxy_client/README.md delete mode 100644 monitoring/mock_uss/atproxy_client/__init__.py delete mode 100644 monitoring/mock_uss/atproxy_client/config.py delete mode 100644 monitoring/mock_uss/atproxy_client/daemon.py diff --git a/Makefile b/Makefile index 6368fad942..ff449bcf14 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,6 @@ stop-uss-mocks: collect-local-logs: mkdir -p logs -sh -c "build/dev/run_locally.sh logs --timestamps" > logs/local_infra.log 2>&1 - -docker logs atproxy > logs/atproxy.log 2>&1 -docker logs mock_uss_scdsc_a > logs/mock_uss_scdsc_a.log 2>&1 -docker logs mock_uss_scdsc_b > logs/mock_uss_scdsc_b.log 2>&1 -docker logs mock_uss_ridsp > logs/mock_uss_ridsp.log 2>&1 @@ -89,7 +88,6 @@ collect-local-logs: -docker logs mock_uss_geoawareness > logs/mock_uss_geoawareness.log 2>&1 -docker logs mock_uss_tracer > logs/mock_uss_tracer.log 2>&1 -docker logs mock_uss_tracer_v22a > logs/mock_uss_tracer_v22a.log 2>&1 - -docker logs mock_uss_atproxy_client > logs/mock_uss_atproxy_client.log 2>&1 .PHONY: stop-locally stop-locally: diff --git a/assets/atproxy_sequence.puml b/assets/atproxy_sequence.puml deleted file mode 100644 index d6f84f9d09..0000000000 --- a/assets/atproxy_sequence.puml +++ /dev/null @@ -1,21 +0,0 @@ -'To render with PlantUML: -' java -jar plantuml.jar -o generated atproxy_sequence.puml -@startuml -participant "Internal DP system" as IS -participant "Internal automated testing client" as IC -participant "atproxy" as atp -participant "uss_qualifier" as Q -IC -> atp: Any requests? -Note over atp: Some time elapses -atp --> IC: No requests right now -IC -> atp: Any requests? -Q -> atp: GET /observation/display_data -Note over atp: Stores request -atp --> IC: 1 request available -Note over IC: Parse request as display_data request -IC -> IS: (pretending to be Display Application)\nGet flights in specified view -IS --> IC: Flight and/or cluster list -IC -> atp: Request fulfillment -atp --> IC: Ok -atp --> Q: Flights and clusters -@enduml diff --git a/assets/generated/atproxy_sequence.png b/assets/generated/atproxy_sequence.png deleted file mode 100644 index daeb6f27f2a852ff2c7afa380166c4036f441917..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32613 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≻C72jMF$6mK8oph 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
-

{{ test_suite_name }} test suite

- - - - - - {% for capability in capabilities %} - - {% if capability.requirements %} - - - - - - - - {% for req in capability.requirements %} - {% for pc in req.passed_checks %} - - {% if loop.index == 1 %} - - {% endif %} - - - - - - {% endfor %} - {% for fc in req.failed_checks %} - - {% if loop.index == 1 %} - - {% endif %} - - - - - - {% endfor %} - {% if (len(req.passed_checks) == 0) and (len(req.failed_checks) == 0) %} - - - - - {% endif %} - {% endfor %} - {% endif %} - - {% if capability.capabilities %} - - {% if not capability.requirements %} - - {% endif %} - - - - - {% for child_cap in capability.capabilities %} - - - - - {% endfor %} - {% endif %} - - {% endfor %} -
CapabilityEvaluation
{{ capability.name }}RequirementTest checkResult
{{ req.requirement_id }}{{ pc }} ({{ req.passed_checks[pc] }}x)PASS
{{ req.requirement_id }}{{ fc.name }}FAIL
{{ req.requirement_id }}Not tested
{{ capability.name }}CapabilityResult
{{ child_cap.capability_id }}{{ 'VERIFIED' if child_cap.verified else 'MISSING' if child_cap.missing else 'UNVERIFIED' }}
-
- - diff --git a/monitoring/uss_qualifier/reports/templates/tested_roles/test_run_report.html b/monitoring/uss_qualifier/reports/templates/tested_roles/test_run_report.html deleted file mode 100644 index 3698a92c4e..0000000000 --- a/monitoring/uss_qualifier/reports/templates/tested_roles/test_run_report.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/monitoring/uss_qualifier/reports/tested_requirements.py b/monitoring/uss_qualifier/reports/tested_requirements.py index 2ec61863df..4bc4cfb2df 100644 --- a/monitoring/uss_qualifier/reports/tested_requirements.py +++ b/monitoring/uss_qualifier/reports/tested_requirements.py @@ -1,6 +1,5 @@ import os from dataclasses import dataclass -import re from functools import cmp_to_key from typing import List, Union, Dict, Set, Optional @@ -187,7 +186,7 @@ class TestRunInformation(object): def generate_tested_requirements( - report: TestRunReport, config: TestedRequirementsConfiguration + report: TestRunReport, config: TestedRequirementsConfiguration, output_path: str ) -> None: req_collections: Dict[ TestedRequirementsCollectionIdentifier, Set[RequirementID] @@ -211,8 +210,8 @@ def generate_tested_requirements( import_submodules(suites) import_submodules(action_generators) - os.makedirs(config.output_path, exist_ok=True) - index_file = os.path.join(config.output_path, "index.html") + os.makedirs(output_path, exist_ok=True) + index_file = os.path.join(output_path, "index.html") participant_ids = list(report.report.participant_ids()) participant_ids.sort() @@ -237,7 +236,7 @@ def generate_tested_requirements( participant_breakdown, participant_req_collections[participant_id] ) _sort_breakdown(participant_breakdown) - participant_file = os.path.join(config.output_path, f"{participant_id}.html") + participant_file = os.path.join(output_path, f"{participant_id}.html") other_participants = ", ".join( p for p in participant_ids if p != participant_id ) diff --git a/monitoring/uss_qualifier/reports/tested_roles.py b/monitoring/uss_qualifier/reports/tested_roles.py deleted file mode 100644 index 99192ce8ad..0000000000 --- a/monitoring/uss_qualifier/reports/tested_roles.py +++ /dev/null @@ -1,217 +0,0 @@ -import os -import shutil -from dataclasses import dataclass -from typing import List, Any, Union, Dict - -from monitoring.uss_qualifier.configurations.configuration import ParticipantID -from monitoring.uss_qualifier.reports import jinja_env -from monitoring.uss_qualifier.reports.report import ( - TestRunReport, - TestSuiteActionReport, - TestScenarioReport, - ActionGeneratorReport, - TestSuiteReport, - PassedCheck, - FailedCheck, - ParticipantCapabilityConditionEvaluationReport, -) -from monitoring.uss_qualifier.requirements.definitions import RequirementID - - -def generate_tested_roles(report: TestRunReport, output_path: str) -> None: - if os.path.exists(output_path): - shutil.rmtree(output_path) - participant_ids = report.report.participant_ids() - for participant_id in participant_ids: - _generate_suite_action_output( - report.report, participant_id, os.path.join(output_path, participant_id) - ) - template = jinja_env.get_template("tested_roles/test_run_report.html") - index = os.path.join(output_path, "index.html") - os.makedirs(os.path.dirname(index), exist_ok=True) - with open(index, "w") as f: - f.write(template.render(participant_ids=participant_ids)) - - -def _generate_suite_action_output( - report: TestSuiteActionReport, participant_id: ParticipantID, output_path: str -) -> None: - test_suite, test_scenario, action_generator = report.get_applicable_report() - if test_suite: - _generate_suite_output(report.test_suite, participant_id, output_path) - elif test_scenario: - _generate_scenario_output(report.test_scenario, participant_id, output_path) - elif action_generator: - _generate_action_generator_output( - report.action_generator, participant_id, output_path - ) - else: - raise ValueError("TestSuiteActionReport did not specify any report") - - -def _generate_scenario_output( - report: TestScenarioReport, participant_id: ParticipantID, output_path: str -) -> None: - pass - - -def _generate_action_generator_output( - report: ActionGeneratorReport, participant_id: ParticipantID, output_path: str -) -> None: - for i, action in enumerate(report.actions): - _generate_suite_action_output( - action, participant_id, os.path.join(output_path, f"action{i}") - ) - - -@dataclass -class PotentiallyCheckedRequirement(object): - requirement_id: RequirementID - passed_checks: Dict[str, int] - failed_checks: List[FailedCheck] - - @property - def rows(self) -> int: - if not self.passed_checks and not self.failed_checks: - return 1 - return len(self.passed_checks) + len(self.failed_checks) - - -@dataclass -class ChildCapability(object): - capability_id: str - verified: bool = False - missing: bool = False - - @property - def rows(self) -> int: - return 1 - - -@dataclass -class CapabilityEvalInfo(object): - requirements: List[PotentiallyCheckedRequirement] - capabilities: List[ChildCapability] - - -@dataclass -class CapabilityEvalReport(object): - name: str - verified: bool - requirements: List[PotentiallyCheckedRequirement] - capabilities: List[ChildCapability] - - @property - def rows(self) -> int: - n = 0 - if self.requirements: - n += 1 - n += sum(r.rows for r in self.requirements) - if self.capabilities: - n += 1 - n += sum(c.rows for c in self.capabilities) - return n - - -def _follow_jsonaddress(obj: dict, fields: Union[str, List[str]]) -> Any: - """Follows an explicit JSONAddress for an obj (much, much faster than treating path as a search).""" - # TODO: Change fields to be Union[JSONAddress, List[JSONAddress]] upon merging of #171 - - if isinstance(fields, str): - return _follow_jsonaddress(obj, fields.split(".")) - if not fields: - return obj - if fields[0] == "$" or fields[0] == "": - return _follow_jsonaddress(obj, fields[1:]) - - if len(fields) == 1: - field = fields[0] - if field[-1] == "]" and "[" in field: - field, index = field[0:-1].split("[") - items = _follow_jsonaddress(obj, field) - return items[int(index)] - else: - return obj[field] - else: - child = _follow_jsonaddress(obj, [fields[0]]) - return _follow_jsonaddress(child, fields[1:]) - - -def _collect_info_from_conditions( - report: TestSuiteReport, condition: ParticipantCapabilityConditionEvaluationReport -) -> CapabilityEvalInfo: - result = CapabilityEvalInfo(requirements=[], capabilities=[]) - if "all_conditions" in condition and condition.all_conditions: - for subcondition in ( - condition.all_conditions.satisfied_conditions - + condition.all_conditions.unsatisfied_conditions - ): - subresult = _collect_info_from_conditions(report, subcondition) - result.requirements.extend(subresult.requirements) - result.capabilities.extend(subresult.capabilities) - elif "requirements_checked" in condition and condition.requirements_checked: - for req_ref in ( - condition.requirements_checked.passed_requirements - + condition.requirements_checked.failed_requirements - ): - passed_checks = {} - failed_checks = [] - for req_path in req_ref.passed_checks: - pc: PassedCheck = _follow_jsonaddress(report, req_path) - passed_checks[pc.name] = passed_checks.get(pc.name, 0) + 1 - for req_path in req_ref.failed_checks: - failed_checks.append(_follow_jsonaddress(report, req_path)) - req = PotentiallyCheckedRequirement( - requirement_id=req_ref.requirement_id, - passed_checks=passed_checks, - failed_checks=failed_checks, - ) - result.requirements.append(req) - for req_id in condition.requirements_checked.untested_requirements: - result.requirements.append( - PotentiallyCheckedRequirement( - requirement_id=req_id, passed_checks={}, failed_checks=[] - ) - ) - elif "capability_verified" in condition and condition.capability_verified: - for c in condition.capability_verified.checked_capabilities: - result.capabilities.append( - ChildCapability( - capability_id=c.capability_id, verified=c.capability_verified - ) - ) - for c in condition.capability_verified.missing_capabilities: - result.capabilities.append(ChildCapability(capability_id=c, missing=True)) - return result - - -def _generate_suite_output( - report: TestSuiteReport, participant_id: ParticipantID, output_path: str -) -> None: - capabilities: List[CapabilityEvalReport] = [] - for capability in report.capability_evaluations: - if capability.participant_id != participant_id: - continue - info = _collect_info_from_conditions(report, capability.condition_evaluation) - capabilities.append( - CapabilityEvalReport( - name=capability.capability_id, - verified=capability.verified, - requirements=info.requirements, - capabilities=info.capabilities, - ) - ) - template = jinja_env.get_template("tested_roles/capability_evaluation_report.html") - index = os.path.join(output_path, "index.html") - os.makedirs(os.path.dirname(index)) - with open(index, "w") as f: - f.write( - template.render( - len=len, test_suite_name=report.name, capabilities=capabilities - ) - ) - - for i, action in enumerate(report.actions): - _generate_suite_action_output( - action, participant_id, os.path.join(output_path, f"action{i}") - ) diff --git a/monitoring/uss_qualifier/run_locally.sh b/monitoring/uss_qualifier/run_locally.sh index 4ac41fb7a5..da4423dc2a 100755 --- a/monitoring/uss_qualifier/run_locally.sh +++ b/monitoring/uss_qualifier/run_locally.sh @@ -58,7 +58,6 @@ else docker_args="-it" fi -start_time=$(date +%Y-%m-%dT%H:%M:%S) # shellcheck disable=SC2086 docker run ${docker_args} --name uss_qualifier \ --rm \ @@ -74,16 +73,3 @@ docker run ${docker_args} --name uss_qualifier \ -w /app/monitoring/uss_qualifier \ interuss/monitoring \ python main.py $QUALIFIER_OPTIONS - -# Set return code according to whether the test run was fully successful -reports_generated=$(find ./monitoring/uss_qualifier/output/report*.json -newermt "$start_time") -# shellcheck disable=SC2068 -for REPORT in ${reports_generated[@]}; do - successful=$(python build/dev/extract_json_field.py report.*.successful "$REPORT") - if echo "${successful}" | grep -iqF true; then - echo "Full success indicated by $REPORT" - else - echo "Could not establish that all uss_qualifier tests passed in $REPORT" - exit 1 - fi -done diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/ArtifactsConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/ArtifactsConfiguration.json index 65a548b622..b076160be7 100644 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/ArtifactsConfiguration.json +++ b/schemas/monitoring/uss_qualifier/configurations/configuration/ArtifactsConfiguration.json @@ -7,34 +7,23 @@ "description": "Path to content that replaces the $ref", "type": "string" }, - "graph": { - "description": "If specified, configuration describing a desired graph visualization summarizing the test run", - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "GraphConfiguration.json" - } - ] - }, - "redact_access_tokens": { - "description": "When True, look for instances of \"Authorization\" keys in the report with values starting \"Bearer \" and redact the signature from those access tokens", - "type": "boolean" + "output_path": { + "description": "Path to folder where artifacts should be written.", + "type": "string" }, - "report": { - "description": "Configuration for report generation", + "raw_report": { + "description": "Configuration for raw report generation", "oneOf": [ { "type": "null" }, { - "$ref": "ReportConfiguration.json" + "$ref": "RawReportConfiguration.json" } ] }, "report_html": { - "description": "If specified, configuration describing how an HTML version of the report should be generated", + "description": "If specified, configuration describing how an HTML version of the raw report should be generated", "oneOf": [ { "type": "null" @@ -60,30 +49,24 @@ "items": { "$ref": "TemplatedReportConfiguration.json" }, - "type": "array" - }, - "tested_requirements": { - "description": "If specified, configuration describing a desired report summarizing all tested requirements for each participant", - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "TestedRequirementsConfiguration.json" - } + "type": [ + "array", + "null" ] }, - "tested_roles": { - "description": "If specified, configuration describing a desired report summarizing tested requirements for each specified participant and role", - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "TestedRolesConfiguration.json" - } + "tested_requirements": { + "description": "If specified, list of configurations describing desired reports summarizing tested requirements for each participant", + "items": { + "$ref": "TestedRequirementsConfiguration.json" + }, + "type": [ + "array", + "null" ] } }, + "required": [ + "output_path" + ], "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json deleted file mode 100644 index 715d83454a..0000000000 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "monitoring.uss_qualifier.configurations.configuration.GraphConfiguration, as defined in monitoring/uss_qualifier/configurations/configuration.py", - "properties": { - "$ref": { - "description": "Path to content that replaces the $ref", - "type": "string" - }, - "gv_path": { - "description": "Path of GraphViz (.gv) text file to contain a visualization of the test run", - "type": "string" - } - }, - "required": [ - "gv_path" - ], - "type": "object" -} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/RawReportConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/RawReportConfiguration.json new file mode 100644 index 0000000000..860f02ef52 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/configurations/configuration/RawReportConfiguration.json @@ -0,0 +1,23 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/configurations/configuration/RawReportConfiguration.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.configurations.configuration.RawReportConfiguration, as defined in monitoring/uss_qualifier/configurations/configuration.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "indent": { + "description": "To pretty-print JSON content, specify an indent level (generally 2), or omit or set to None to write compactly.", + "type": [ + "integer", + "null" + ] + }, + "redact_access_tokens": { + "description": "When True, look for instances of \"Authorization\" keys in the report with values starting \"Bearer \" and redact the signature from those access tokens", + "type": "boolean" + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/ReportConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/ReportConfiguration.json deleted file mode 100644 index ea0cff118b..0000000000 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/ReportConfiguration.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/configurations/configuration/ReportConfiguration.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "monitoring.uss_qualifier.configurations.configuration.ReportConfiguration, as defined in monitoring/uss_qualifier/configurations/configuration.py", - "properties": { - "$ref": { - "description": "Path to content that replaces the $ref", - "type": "string" - }, - "report_path": { - "description": "File name of the report to write (if test_config provided) or read (if test_config not provided)", - "type": "string" - } - }, - "required": [ - "report_path" - ], - "type": "object" -} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/ReportHTMLConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/ReportHTMLConfiguration.json index 1877035482..a836ca7d1d 100644 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/ReportHTMLConfiguration.json +++ b/schemas/monitoring/uss_qualifier/configurations/configuration/ReportHTMLConfiguration.json @@ -7,13 +7,10 @@ "description": "Path to content that replaces the $ref", "type": "string" }, - "html_path": { - "description": "Path of HTML file to contain an HTML rendering of the raw test report object", - "type": "string" + "redact_access_tokens": { + "description": "When True, look for instances of \"Authorization\" keys in the report with values starting \"Bearer \" and redact the signature from those access tokens", + "type": "boolean" } }, - "required": [ - "html_path" - ], "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/SequenceViewConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/SequenceViewConfiguration.json index f1428d02a2..844cf2a771 100644 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/SequenceViewConfiguration.json +++ b/schemas/monitoring/uss_qualifier/configurations/configuration/SequenceViewConfiguration.json @@ -7,13 +7,10 @@ "description": "Path to content that replaces the $ref", "type": "string" }, - "output_path": { - "description": "Path of a folder into which report HTML files should be written", - "type": "string" + "redact_access_tokens": { + "description": "When True, look for instances of \"Authorization\" keys in the report with values starting \"Bearer \" and redact the signature from those access tokens", + "type": "boolean" } }, - "required": [ - "output_path" - ], "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/TemplatedReportConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/TemplatedReportConfiguration.json index 6e26232e54..3bc01fb371 100644 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/TemplatedReportConfiguration.json +++ b/schemas/monitoring/uss_qualifier/configurations/configuration/TemplatedReportConfiguration.json @@ -18,8 +18,8 @@ } ] }, - "output_path": { - "description": "Path of HTML file to contain the rendered templated report", + "report_name": { + "description": "Name of HTML file (without extension) to contain the rendered templated report", "type": "string" }, "template_url": { @@ -28,7 +28,7 @@ } }, "required": [ - "output_path", + "report_name", "template_url" ], "type": "object" diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/TestedRequirementsConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/TestedRequirementsConfiguration.json index 35ffb9dadc..15dec41d6f 100644 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/TestedRequirementsConfiguration.json +++ b/schemas/monitoring/uss_qualifier/configurations/configuration/TestedRequirementsConfiguration.json @@ -7,10 +7,6 @@ "description": "Path to content that replaces the $ref", "type": "string" }, - "output_path": { - "description": "Path of a folder into which report HTML files should be written", - "type": "string" - }, "participant_requirements": { "additionalProperties": { "type": "string" @@ -26,6 +22,10 @@ "null" ] }, + "report_name": { + "description": "Name of subfolder in output path to contain the rendered templated report", + "type": "string" + }, "requirement_collections": { "additionalProperties": { "$ref": "../../requirements/definitions/RequirementCollection.json" @@ -43,7 +43,7 @@ } }, "required": [ - "output_path" + "report_name" ], "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/TestedRolesConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/TestedRolesConfiguration.json deleted file mode 100644 index af8cf7a590..0000000000 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/TestedRolesConfiguration.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/configurations/configuration/TestedRolesConfiguration.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "monitoring.uss_qualifier.configurations.configuration.TestedRolesConfiguration, as defined in monitoring/uss_qualifier/configurations/configuration.py", - "properties": { - "$ref": { - "description": "Path to content that replaces the $ref", - "type": "string" - }, - "report_path": { - "description": "Path of folder to write HTML files containing a fulfilled-requirements-based view of the test report", - "type": "string" - } - }, - "required": [ - "report_path" - ], - "type": "object" -} \ No newline at end of file From 8a397659fd8a9be4c9c104bc87cf04b2722166eb Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 23 Oct 2023 10:58:53 -0700 Subject: [PATCH 07/11] [monitorlib, uss_qualifier] Add flight_planning client and resource (#284) * Add flight_planning client and resource * Fix message signing * Check execution style --- .../clients/flight_planning/client_v1.py | 175 ++++++++++++++++++ .../dev/f3548_self_contained.yaml | 8 +- .../dev/library/environment_containers.yaml | 8 +- .../dev/library/environment_localhost.yaml | 8 +- .../configurations/dev/message_signing.yaml | 6 +- .../flight_planning/flight_planner.py | 54 ++++-- .../flight_planning/record_planners.py | 2 +- .../FlightPlannerConfiguration.json | 19 +- 8 files changed, 243 insertions(+), 37 deletions(-) create mode 100644 monitoring/monitorlib/clients/flight_planning/client_v1.py diff --git a/monitoring/monitorlib/clients/flight_planning/client_v1.py b/monitoring/monitorlib/clients/flight_planning/client_v1.py new file mode 100644 index 0000000000..c64eb5039f --- /dev/null +++ b/monitoring/monitorlib/clients/flight_planning/client_v1.py @@ -0,0 +1,175 @@ +import uuid + +from implicitdict import ImplicitDict +from monitoring.monitorlib.clients.flight_planning.client import ( + FlightPlannerClient, + PlanningActivityError, +) +from monitoring.monitorlib.clients.flight_planning.test_preparation import ( + TestPreparationActivityResponse, +) + +from monitoring.monitorlib.clients.flight_planning.flight_info import ( + FlightInfo, + FlightID, + ExecutionStyle, +) +from monitoring.monitorlib.clients.flight_planning.planning import ( + PlanningActivityResponse, +) +from monitoring.monitorlib.fetch import query_and_describe +from monitoring.monitorlib.geotemporal import Volume4D +from monitoring.monitorlib.infrastructure import UTMClientSession + +from uas_standards.interuss.automated_testing.flight_planning.v1 import api +from uas_standards.interuss.automated_testing.flight_planning.v1.constants import Scope + + +class V1FlightPlannerClient(FlightPlannerClient): + _session: UTMClientSession + + def __init__(self, session: UTMClientSession): + self._session = session + + def _inject( + self, + flight_plan_id: FlightID, + flight_info: FlightInfo, + execution_style: ExecutionStyle, + ) -> PlanningActivityResponse: + flight_plan = ImplicitDict.parse(flight_info, api.FlightPlan) + req = api.UpsertFlightPlanRequest( + flight_plan=flight_plan, + execution_style=execution_style, + request_id=str(uuid.uuid4()), + ) + + op = api.OPERATIONS[api.OperationID.UpsertFlightPlan] + url = op.path.format(flight_plan_id=flight_plan_id) + query = query_and_describe( + self._session, op.verb, url, json=req, scope=Scope.Plan + ) + if query.status_code != 200 and query.status_code != 201: + raise PlanningActivityError( + f"Attempt to plan flight returned status {query.status_code} rather than 200 as expected", + query, + ) + try: + resp: api.UpsertFlightPlanResponse = ImplicitDict.parse( + query.response.json, api.UpsertFlightPlanResponse + ) + except ValueError as e: + raise PlanningActivityError( + f"Response to plan flight could not be parsed: {str(e)}", query + ) + + response = PlanningActivityResponse( + flight_id=flight_plan_id, + queries=[query], + activity_result=resp.planning_result, + flight_plan_status=resp.flight_plan_status, + ) + return response + + def try_plan_flight( + self, flight_info: FlightInfo, execution_style: ExecutionStyle + ) -> PlanningActivityResponse: + return self._inject(str(uuid.uuid4()), flight_info, execution_style) + + def try_update_flight( + self, + flight_id: FlightID, + updated_flight_info: FlightInfo, + execution_style: ExecutionStyle, + ) -> PlanningActivityResponse: + return self._inject(flight_id, updated_flight_info, execution_style) + + def try_end_flight( + self, flight_id: FlightID, execution_style: ExecutionStyle + ) -> PlanningActivityResponse: + if execution_style != ExecutionStyle.IfAllowed: + raise NotImplementedError( + "Only IfAllowed execution style is currently allowed" + ) + op = api.OPERATIONS[api.OperationID.DeleteFlightPlan] + url = op.path.format(flight_plan_id=flight_id) + query = query_and_describe(self._session, op.verb, url, scope=Scope.Plan) + if query.status_code != 200: + raise PlanningActivityError( + f"Attempt to delete flight plan returned status {query.status_code} rather than 200 as expected", + query, + ) + try: + resp: api.DeleteFlightPlanResponse = ImplicitDict.parse( + query.response.json, api.DeleteFlightPlanResponse + ) + except ValueError as e: + raise PlanningActivityError( + f"Response to delete flight plan could not be parsed: {str(e)}", query + ) + + response = PlanningActivityResponse( + flight_id=flight_id, + queries=[query], + activity_result=resp.planning_result, + flight_plan_status=resp.flight_plan_status, + ) + return response + + def report_readiness(self) -> TestPreparationActivityResponse: + op = api.OPERATIONS[api.OperationID.GetStatus] + query = query_and_describe( + self._session, op.verb, op.path, scope=Scope.DirectAutomatedTest + ) + if query.status_code != 200: + raise PlanningActivityError( + f"Attempt to get interface status returned status {query.status_code} rather than 200 as expected", + query, + ) + try: + resp: api.StatusResponse = ImplicitDict.parse( + query.response.json, api.StatusResponse + ) + except ValueError as e: + raise PlanningActivityError( + f"Response to get interface status could not be parsed: {str(e)}", query + ) + + if resp.status == api.StatusResponseStatus.Ready: + errors = [] + elif resp.status == api.StatusResponseStatus.Starting: + errors = ["Flight planning v1 interface is still starting (not ready)"] + else: + errors = [f"Unrecognized status '{resp.status}'"] + + return TestPreparationActivityResponse(errors=errors, queries=[query]) + + def clear_area(self, area: Volume4D) -> TestPreparationActivityResponse: + req = api.ClearAreaRequest( + request_id=str(uuid.uuid4()), extent=area.to_interuss_scd_api() + ) + + op = api.OPERATIONS[api.OperationID.ClearArea] + query = query_and_describe( + self._session, op.verb, op.path, json=req, scope=Scope.DirectAutomatedTest + ) + if query.status_code != 200: + raise PlanningActivityError( + f"Attempt to clear area returned status {query.status_code} rather than 200 as expected", + query, + ) + try: + resp: api.ClearAreaResponse = ImplicitDict.parse( + query.response.json, api.ClearAreaResponse + ) + except ValueError as e: + raise PlanningActivityError( + f"Response to clear area could not be parsed: {str(e)}", query + ) + + if resp.outcome.success: + errors = None + else: + errors = [f"[{resp.outcome.timestamp}]: {resp.outcome.message}"] + + return TestPreparationActivityResponse(errors=errors, queries=[query]) diff --git a/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml b/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml index cc74bc3a10..e87ccd0343 100644 --- a/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml +++ b/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml @@ -40,12 +40,12 @@ v1: auth_adapter: utm_auth specification: flight_planners: - # uss1 is the mock_uss directly exposing scdsc functionality + # uss1 is the mock_uss directly exposing flight planning functionality - participant_id: uss1 - injection_base_url: http://scdsc.uss1.localutm/scdsc - # uss2 is another mock_uss directly exposing scdsc functionality + scd_injection_base_url: http://scdsc.uss1.localutm/scdsc + # uss2 is another mock_uss directly exposing flight planning functionality - participant_id: uss2 - injection_base_url: http://scdsc.uss2.localutm/scdsc + scd_injection_base_url: http://scdsc.uss2.localutm/scdsc # Details of conflicting flights (used in nominal planning scenario) conflicting_flights: diff --git a/monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml b/monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml index 00111b7788..3070efcafd 100644 --- a/monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml +++ b/monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml @@ -119,11 +119,11 @@ all_flight_planners: specification: flight_planners: - participant_id: uss1 - injection_base_url: http://scdsc.uss1.localutm/scdsc + scd_injection_base_url: http://scdsc.uss1.localutm/scdsc local_debug: true - participant_id: uss2 - injection_base_url: http://scdsc.uss2.localutm/scdsc + scd_injection_base_url: http://scdsc.uss2.localutm/scdsc local_debug: true uss1_flight_planner: @@ -134,7 +134,7 @@ uss1_flight_planner: specification: flight_planner: participant_id: uss1 - injection_base_url: http://scdsc.uss1.localutm/scdsc + scd_injection_base_url: http://scdsc.uss1.localutm/scdsc local_debug: true uss2_flight_planner: @@ -145,7 +145,7 @@ uss2_flight_planner: specification: flight_planner: participant_id: uss2 - injection_base_url: http://scdsc.uss2.localutm/scdsc + scd_injection_base_url: http://scdsc.uss2.localutm/scdsc local_debug: true # ===== F3548 ===== diff --git a/monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml b/monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml index 637b2ff4ef..64971f949e 100644 --- a/monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml +++ b/monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml @@ -119,11 +119,11 @@ all_flight_planners: specification: flight_planners: - participant_id: uss1 - injection_base_url: http://localhost:8074/scdsc + scd_injection_base_url: http://localhost:8074/scdsc local_debug: true - participant_id: uss2 - injection_base_url: http://localhost:8094/scdsc + scd_injection_base_url: http://localhost:8094/scdsc local_debug: true uss1_flight_planner: @@ -134,7 +134,7 @@ uss1_flight_planner: specification: flight_planner: participant_id: uss1 - injection_base_url: http://localhost:8074/scdsc + scd_injection_base_url: http://localhost:8074/scdsc local_debug: true uss2_flight_planner: @@ -145,7 +145,7 @@ uss2_flight_planner: specification: flight_planner: participant_id: uss2 - injection_base_url: http://localhost:8094/scdsc + scd_injection_base_url: http://localhost:8094/scdsc local_debug: true # ===== F3548 ===== diff --git a/monitoring/uss_qualifier/configurations/dev/message_signing.yaml b/monitoring/uss_qualifier/configurations/dev/message_signing.yaml index dfb2ec8491..dde36e218e 100644 --- a/monitoring/uss_qualifier/configurations/dev/message_signing.yaml +++ b/monitoring/uss_qualifier/configurations/dev/message_signing.yaml @@ -21,11 +21,11 @@ v1: specification: flight_planners: - participant_id: uss1 - injection_base_url: http://host.docker.internal:8074/scdsc + scd_injection_base_url: http://host.docker.internal:8074/scdsc - participant_id: uss2 - injection_base_url: http://host.docker.internal:8074/scdsc + scd_injection_base_url: http://host.docker.internal:8074/scdsc - participant_id: mock_uss - injection_base_url: http://host.docker.internal:8074/scdsc + scd_injection_base_url: http://host.docker.internal:8074/scdsc mock_uss: resource_type: resources.interuss.mock_uss.client.MockUSSResource dependencies: diff --git a/monitoring/uss_qualifier/resources/flight_planning/flight_planner.py b/monitoring/uss_qualifier/resources/flight_planning/flight_planner.py index fae5d9eb01..accd64aaac 100644 --- a/monitoring/uss_qualifier/resources/flight_planning/flight_planner.py +++ b/monitoring/uss_qualifier/resources/flight_planning/flight_planner.py @@ -9,6 +9,9 @@ from monitoring.monitorlib.clients.flight_planning.client_scd import ( SCDFlightPlannerClient, ) +from monitoring.monitorlib.clients.flight_planning.client_v1 import ( + V1FlightPlannerClient, +) from monitoring.monitorlib.clients.flight_planning.flight_info import ( ExecutionStyle, FlightInfo, @@ -47,20 +50,33 @@ class FlightPlannerConfiguration(ImplicitDict): participant_id: str """ID of the flight planner into which test data can be injected""" - injection_base_url: str - """Base URL for the flight planner's implementation of the interfaces/automated-testing/scd/scd.yaml API""" + scd_injection_base_url: Optional[str] + """Base URL for the flight planner's implementation of the interfaces/automated_testing/scd/v1/scd.yaml API""" + + v1_base_url: Optional[str] + """Base URL for the flight planner's implementation of the interfaces/automated_testing/flight_planning/v1/flight_planning.yaml API""" timeout_seconds: Optional[float] = None """Number of seconds to allow for requests to this flight planner. If None, use default.""" def __init__(self, *args, **kwargs): super().__init__(**kwargs) - try: - urlparse(self.injection_base_url) - except ValueError: + if "v1_base_url" not in self and "scd_injection_base_url" not in self: raise ValueError( - "FlightPlannerConfiguration.injection_base_url must be a URL" + "One of `scd_injection_base_url` or `v1_base_url` must be specified" ) + if "scd_injection_base_url" in self and self.scd_injection_base_url: + try: + urlparse(self.scd_injection_base_url) + except ValueError: + raise ValueError( + "FlightPlannerConfiguration.scd_injection_base_url must be a URL" + ) + if "v1_base_url" in self and self.v1_base_url: + try: + urlparse(self.v1_base_url) + except ValueError: + raise ValueError("FlightPlannerConfiguration.v1_base_url must be a URL") class FlightPlanner: @@ -74,17 +90,23 @@ def __init__( auth_adapter: infrastructure.AuthAdapter, ): self.config = config - session = infrastructure.UTMClientSession( - self.config.injection_base_url, auth_adapter, config.timeout_seconds - ) - self.scd_client = SCDFlightPlannerClient(session) + if "scd_injection_base_url" in config and config.scd_injection_base_url: + session = infrastructure.UTMClientSession( + self.config.scd_injection_base_url, auth_adapter, config.timeout_seconds + ) + self.client = SCDFlightPlannerClient(session) + elif "v1_base_url" in config and config.v1_base_url: + session = infrastructure.UTMClientSession( + self.config.v1_base_url, auth_adapter, config.timeout_seconds + ) + self.client = V1FlightPlannerClient(session) # Flights injected by this target. self.created_flight_ids: Set[str] = set() def __repr__(self): return "FlightPlanner({}, {})".format( - self.config.participant_id, self.config.injection_base_url + self.config.participant_id, self.config.scd_injection_base_url ) @property @@ -145,7 +167,7 @@ def request_flight( if not flight_id: try: - resp = self.scd_client.try_plan_flight( + resp = self.client.try_plan_flight( flight_info, ExecutionStyle.IfAllowed ) except PlanningActivityError as e: @@ -153,7 +175,7 @@ def request_flight( flight_id = resp.flight_id else: try: - resp = self.scd_client.try_update_flight( + resp = self.client.try_update_flight( flight_id, flight_info, ExecutionStyle.IfAllowed ) except PlanningActivityError as e: @@ -193,7 +215,7 @@ def cleanup_flight( self, flight_id: str ) -> Tuple[DeleteFlightResponse, fetch.Query]: try: - resp = self.scd_client.try_end_flight(flight_id, ExecutionStyle.IfAllowed) + resp = self.client.try_end_flight(flight_id, ExecutionStyle.IfAllowed) except PlanningActivityError as e: raise QueryError(str(e), e.queries) @@ -214,14 +236,14 @@ def cleanup_flight( def get_readiness(self) -> Tuple[Optional[str], Query]: try: - resp = self.scd_client.report_readiness() + resp = self.client.report_readiness() except PlanningActivityError as e: return str(e), e.queries[0] return None, resp.queries[0] def clear_area(self, extent: Volume4D) -> Tuple[ClearAreaResponse, fetch.Query]: try: - resp = self.scd_client.clear_area(extent) + resp = self.client.clear_area(extent) except PlanningActivityError as e: raise QueryError(str(e), e.queries) success = False if resp.errors else True diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/record_planners.py b/monitoring/uss_qualifier/scenarios/flight_planning/record_planners.py index 39e39d8466..68c4aa2234 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/record_planners.py +++ b/monitoring/uss_qualifier/scenarios/flight_planning/record_planners.py @@ -15,7 +15,7 @@ def run(self): self.record_note( "Available flight planners", "\n".join( - f"* {fp.config.participant_id}: {fp.config.injection_base_url}" + f"* {fp.config.participant_id}: {fp.config.scd_injection_base_url}" for fp in self._flight_planners.flight_planners ), ) diff --git a/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json index 13e2532ed0..e6f7b535be 100644 --- a/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json +++ b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json @@ -7,24 +7,33 @@ "description": "Path to content that replaces the $ref", "type": "string" }, - "injection_base_url": { - "description": "Base URL for the flight planner's implementation of the interfaces/automated-testing/scd/scd.yaml API", - "type": "string" - }, "participant_id": { "description": "ID of the flight planner into which test data can be injected", "type": "string" }, + "scd_injection_base_url": { + "description": "Base URL for the flight planner's implementation of the interfaces/automated_testing/scd/v1/scd.yaml API", + "type": [ + "string", + "null" + ] + }, "timeout_seconds": { "description": "Number of seconds to allow for requests to this flight planner. If None, use default.", "type": [ "number", "null" ] + }, + "v1_base_url": { + "description": "Base URL for the flight planner's implementation of the interfaces/automated_testing/flight_planning/v1/flight_planning.yaml API", + "type": [ + "string", + "null" + ] } }, "required": [ - "injection_base_url", "participant_id" ], "type": "object" From d2a6dc9438ad9d731e0194d8608753495e2d1051 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 23 Oct 2023 22:57:29 -0700 Subject: [PATCH 08/11] [docs] Fix GitHub Pages links (#286) Fix GitHub Pages links --- github_pages/make_site_content.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github_pages/make_site_content.sh b/github_pages/make_site_content.sh index 566a7462a9..6acfc2bc9d 100755 --- a/github_pages/make_site_content.sh +++ b/github_pages/make_site_content.sh @@ -11,4 +11,4 @@ mkdir ./public cp -r ./monitoring/github_pages/static/* ./public mkdir -p ./public/artifacts/uss_qualifier/reports -cp -r ./artifacts/uss_qualifier/output ./public/artifacts/uss_qualifier/reports +cp -r ./artifacts/uss_qualifier/output/* ./public/artifacts/uss_qualifier/reports From a09aac7aa5db8c219900f9e98132120fcbd175af Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Tue, 24 Oct 2023 08:07:03 -0700 Subject: [PATCH 09/11] [uss_qualifier] Use FlightInfoTemplate for flight intent storage (#276) * Use FlightInfoTemplate for flight intent storage * Address comments --- .../flight_planning/flight_info_template.py | 56 + monitoring/monitorlib/geotemporal.py | 3 +- monitoring/monitorlib/uspace.py | 12 +- .../dev/f3548_self_contained.yaml | 2 +- .../configurations/dev/library/resources.yaml | 30 +- .../flight_planning/flight_intent.py | 84 +- .../flight_intents_resource.py | 70 +- .../uss_qualifier/resources/overrides.py | 14 +- .../flight_intent_validation.md | 5 +- .../flight_intent_validation.py | 5 +- .../conflict_equal_priority_not_permitted.md | 7 +- .../conflict_equal_priority_not_permitted.py | 5 +- .../conflict_higher_priority.md | 5 +- .../conflict_higher_priority.py | 5 +- .../uspace/flight_auth/validation.py | 3 +- .../flight_intents/conflicting_flights.json | 1041 ----------------- .../flight_intents/conflicting_flights.yaml | 210 ++++ .../flight_intents/invalid_flight_auths.json | 138 --- .../flight_intents/invalid_flight_auths.yaml | 62 + .../invalid_flight_intents.yaml | 162 +-- .../test_data/make_flight_intent_kml.py | 230 ++++ .../flight_intents/conflicting_flights.yaml | 122 -- .../invalid_flight_intents.yaml | 117 -- .../flight_intents/priority_preemption.yaml | 238 ---- 24 files changed, 744 insertions(+), 1882 deletions(-) delete mode 100644 monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.json create mode 100644 monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.yaml delete mode 100644 monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.json create mode 100644 monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.yaml create mode 100644 monitoring/uss_qualifier/test_data/make_flight_intent_kml.py delete mode 100644 monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/conflicting_flights.yaml delete mode 100644 monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/invalid_flight_intents.yaml delete mode 100644 monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/priority_preemption.yaml diff --git a/monitoring/monitorlib/clients/flight_planning/flight_info_template.py b/monitoring/monitorlib/clients/flight_planning/flight_info_template.py index eb8ffd4819..a46c7dec2d 100644 --- a/monitoring/monitorlib/clients/flight_planning/flight_info_template.py +++ b/monitoring/monitorlib/clients/flight_planning/flight_info_template.py @@ -13,6 +13,7 @@ FlightInfo, ) from monitoring.monitorlib.geotemporal import Volume4DTemplate, resolve_volume4d +from uas_standards.interuss.automated_testing.scd.v1 import api as scd_api class BasicFlightPlanInformationTemplate(ImplicitDict): @@ -51,3 +52,58 @@ def resolve(self, start_of_test: datetime) -> FlightInfo: kwargs = {k: v for k, v in self.items()} kwargs["basic_information"] = self.basic_information.resolve(start_of_test) return ImplicitDict.parse(kwargs, FlightInfo) + + def scd_inject_request( + self, start_of_test: datetime + ) -> scd_api.InjectFlightRequest: + """Render a legacy SCD injection API request object from this object.""" + + info = self.resolve(start_of_test) + if "astm_f3548_21" not in info or not info.astm_f3548_21: + raise ValueError( + f"Legacy SCD injection API requires astm_f3548_21 operational intent priority to be specified in FlightInfo" + ) + if ( + "uspace_flight_authorisation" not in info + or not info.uspace_flight_authorisation + ): + raise ValueError( + f"Legacy SCD injection API requires uspace_flight_authorisation to be specified in FlightInfo" + ) + volumes = [v.to_interuss_scd_api() for v in info.basic_information.area] + if info.basic_information.usage_state == AirspaceUsageState.Planned: + state = scd_api.OperationalIntentState.Accepted + off_nominal_volumes = [] + elif info.basic_information.usage_state == AirspaceUsageState.InUse: + if info.basic_information.uas_state == UasState.Nominal: + state = scd_api.OperationalIntentState.Activated + off_nominal_volumes = [] + elif info.basic_information.uas_state == UasState.OffNominal: + state = scd_api.OperationalIntentState.Nonconforming + off_nominal_volumes = volumes + volumes = [] + elif info.basic_information.uas_state == UasState.Contingent: + state = scd_api.OperationalIntentState.Contingent + off_nominal_volumes = volumes + volumes = [] + else: + raise ValueError( + f"Unrecognized uas_state '{info.basic_information.uas_state}'" + ) + else: + raise ValueError( + f"Unrecognized usage_state '{info.basic_information.usage_state}'" + ) + operational_intent = scd_api.OperationalIntentTestInjection( + state=state, + priority=scd_api.Priority(info.astm_f3548_21.priority), + volumes=volumes, + off_nominal_volumes=off_nominal_volumes, + ) + flight_authorisation = ImplicitDict.parse( + info.uspace_flight_authorisation, scd_api.FlightAuthorisationData + ) + return scd_api.InjectFlightRequest( + operational_intent=operational_intent, + flight_authorisation=flight_authorisation, + ) diff --git a/monitoring/monitorlib/geotemporal.py b/monitoring/monitorlib/geotemporal.py index a9f648b7b7..a7c00018a1 100644 --- a/monitoring/monitorlib/geotemporal.py +++ b/monitoring/monitorlib/geotemporal.py @@ -10,12 +10,11 @@ from implicitdict import ImplicitDict, StringBasedTimeDelta, StringBasedDateTime from pvlib.solarposition import get_solarposition import s2sphere as s2sphere -from uas_standards.astm.f3411.v22a.api import Polygon from uas_standards.astm.f3548.v21 import api as f3548v21 from uas_standards.interuss.automated_testing.scd.v1 import api as interuss_scd_api from monitoring.monitorlib import geo -from monitoring.monitorlib.geo import LatLngPoint, Circle, Altitude, Volume3D +from monitoring.monitorlib.geo import LatLngPoint, Circle, Altitude, Volume3D, Polygon class OffsetTime(ImplicitDict): diff --git a/monitoring/monitorlib/uspace.py b/monitoring/monitorlib/uspace.py index 28ccc4a144..ed4464112f 100644 --- a/monitoring/monitorlib/uspace.py +++ b/monitoring/monitorlib/uspace.py @@ -1,24 +1,28 @@ from typing import List from urllib.parse import urlparse -from uas_standards.interuss.automated_testing.scd.v1 import api as scd_injection_api +from monitoring.monitorlib.clients.flight_planning.flight_info import ( + FlightAuthorisationData, + UASClass, + FlightAuthorisationDataOperationCategory, +) from uas_standards.ansi_cta_2063_a import SerialNumber from uas_standards.en4709_02 import OperatorRegistrationNumber def problems_with_flight_authorisation( - flight_auth: scd_injection_api.FlightAuthorisationData, + flight_auth: FlightAuthorisationData, ) -> List[str]: problems: List[str] = [] if not SerialNumber(flight_auth.uas_serial_number).valid: problems.append("Invalid serial number") if not OperatorRegistrationNumber(flight_auth.operator_id).valid: problems.append("Invalid operator ID") - if flight_auth.uas_class == scd_injection_api.UASClass.Other: + if flight_auth.uas_class == UASClass.Other: problems.append("Invalid UAS class") if ( flight_auth.operation_category - == scd_injection_api.FlightAuthorisationDataOperationCategory.Unknown + == FlightAuthorisationDataOperationCategory.Unknown ): problems.append("Invalid operation category") if ( diff --git a/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml b/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml index e87ccd0343..5df2d2bcb6 100644 --- a/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml +++ b/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml @@ -53,7 +53,7 @@ v1: specification: planning_time: '0:05:00' file: - path: file://./test_data/che/flight_intents/conflicting_flights.json + path: file://./test_data/che/flight_intents/conflicting_flights.yaml # Details of priority-preemption flights (used in nominal planning priority scenario) priority_preemption_flights: diff --git a/monitoring/uss_qualifier/configurations/dev/library/resources.yaml b/monitoring/uss_qualifier/configurations/dev/library/resources.yaml index bdc07830bd..b3667c5a28 100644 --- a/monitoring/uss_qualifier/configurations/dev/library/resources.yaml +++ b/monitoring/uss_qualifier/configurations/dev/library/resources.yaml @@ -117,7 +117,7 @@ che_invalid_flight_auth_flights: specification: planning_time: '0:05:00' file: - path: file://./test_data/che/flight_intents/invalid_flight_auths.json + path: file://./test_data/che/flight_intents/invalid_flight_auths.yaml che_conflicting_flights: # Includes flight intents for both equal-priority-not-permitted and higher-priority @@ -126,9 +126,9 @@ che_conflicting_flights: specification: planning_time: '0:05:00' file: - path: file://./test_data/che/flight_intents/conflicting_flights.json + path: file://./test_data/che/flight_intents/conflicting_flights.yaml # Note that this hash_sha512 field can be safely deleted if the content changes - hash_sha512: 6467e88fb69702216dabe1f9723b68fe68a1a9d4b1a40e3af5c0b1ed233061a088c9c30be8a0ada447b18f2c6354db52d84aa12c1e7dfd13f99262d91ba173f4 + hash_sha512: 86b7172de3a6029efdb5c39dff00f96578f81d25b65b4a0a9d731a42a1e260ef632a0891b744b6d1f62fd7bec61cdad3cb8e6b6811a16cbc17ec2dbd081cbbf6 che_invalid_flight_intents: $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json @@ -138,30 +138,6 @@ che_invalid_flight_intents: file: path: test_data.che.flight_intents.invalid_flight_intents -kentland_conflicting_flights: - $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json - resource_type: resources.flight_planning.FlightIntentsResource - specification: - planning_time: '0:05:00' - file: - path: file://./test_data/usa/kentland/flight_intents/conflicting_flights.yaml - -kentland_priority_preemption_flights: - $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json - resource_type: resources.flight_planning.FlightIntentsResource - specification: - planning_time: '0:05:00' - file: - path: test_data.usa.kentland.flight_intents.priority_preemption - -kentland_invalid_flight_intents: - $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json - resource_type: resources.flight_planning.FlightIntentsResource - specification: - planning_time: '0:05:00' - file: - path: test_data.usa.kentland.flight_intents.invalid_flight_intents - # ===== General flight authorization ===== example_flight_check_table: diff --git a/monitoring/uss_qualifier/resources/flight_planning/flight_intent.py b/monitoring/uss_qualifier/resources/flight_planning/flight_intent.py index cf04dcf550..1aaf78d06c 100644 --- a/monitoring/uss_qualifier/resources/flight_planning/flight_intent.py +++ b/monitoring/uss_qualifier/resources/flight_planning/flight_intent.py @@ -1,24 +1,40 @@ +from __future__ import annotations + +import json from typing import Optional, Dict -from implicitdict import ImplicitDict, StringBasedDateTime, StringBasedTimeDelta +import arrow + +from implicitdict import ImplicitDict, StringBasedDateTime +from monitoring.monitorlib.clients.flight_planning.flight_info_template import ( + FlightInfoTemplate, +) -from monitoring.uss_qualifier.fileio import FileReference from monitoring.uss_qualifier.resources.files import ExternalFile +from monitoring.uss_qualifier.resources.overrides import apply_overrides from uas_standards.interuss.automated_testing.scd.v1.api import InjectFlightRequest class FlightIntent(ImplicitDict): + """DEPRECATED. Use FlightInfoTemplate instead.""" + reference_time: StringBasedDateTime """The time that all other times in the FlightInjectionAttempt are relative to. If this FlightInjectionAttempt is initiated by uss_qualifier at t_test, then each t_volume_original timestamp within test_injection should be adjusted to t_volume_adjusted such that t_volume_adjusted = t_test + planning_time when t_volume_original = reference_time""" request: InjectFlightRequest """Definition of the flight the user wants to create.""" + @staticmethod + def from_flight_info_template(info_template: FlightInfoTemplate) -> FlightIntent: + t = arrow.utcnow().datetime + request = info_template.scd_inject_request(t) + return FlightIntent(reference_time=StringBasedDateTime(t), request=request) + FlightIntentID = str -"""Identifier for a flight intent within a collection of flight intents. +"""Identifier for a flight planning intent within a collection of flight planning intents. -To be used only within uss_qualifier (not visible to participants under test) to select an appropriate flight intent from the collection.""" +To be used only within uss_qualifier (not visible to participants under test) to select an appropriate flight planning intent from the collection.""" class DeltaFlightIntent(ImplicitDict): @@ -28,29 +44,71 @@ class DeltaFlightIntent(ImplicitDict): """Base the flight intent for this element of a FlightIntentCollection on the element of the collection identified by this field.""" mutation: Optional[dict] - """For each subfield specified in this object, override the value in the corresponding subfield of the flight intent for this element with the specified value.""" + """For each leaf subfield specified in this object, override the value in the corresponding subfield of the flight intent for this element with the specified value. + + Consider subfields prefixed with + as leaf subfields.""" class FlightIntentCollectionElement(ImplicitDict): """Definition of a single flight intent within a FlightIntentCollection. Exactly one field must be specified.""" - full: Optional[FlightIntent] - """If specified, the full definition of the flight intent.""" + full: Optional[FlightInfoTemplate] + """If specified, the full definition of the flight planning intent.""" delta: Optional[DeltaFlightIntent] - """If specified, a flight intent based on another flight intent, but with some changes.""" + """If specified, a flight planning intent based on another flight intent, but with some changes.""" class FlightIntentCollection(ImplicitDict): """Specification for a collection of flight intents, each identified by a FlightIntentID.""" intents: Dict[FlightIntentID, FlightIntentCollectionElement] - """Flights that users want to create.""" + """Flight planning actions that users want to perform.""" + + def resolve(self) -> Dict[FlightIntentID, FlightInfoTemplate]: + """Resolve the underlying delta flight intents.""" + + # process intents in order of dependency to resolve deltas + processed_intents: Dict[FlightIntentID, FlightInfoTemplate] = {} + unprocessed_intent_ids = list(self.intents.keys()) + + while unprocessed_intent_ids: + nb_processed = 0 + for intent_id in unprocessed_intent_ids: + unprocessed_intent = self.intents[intent_id] + processed_intent: FlightInfoTemplate + + # copy intent and resolve delta + if unprocessed_intent.has_field_with_value("full"): + processed_intent = ImplicitDict.parse( + json.loads(json.dumps(unprocessed_intent.full)), + FlightInfoTemplate, + ) + elif unprocessed_intent.has_field_with_value("delta"): + if unprocessed_intent.delta.source not in processed_intents: + # delta source has not been processed yet + continue + + processed_intent = apply_overrides( + processed_intents[unprocessed_intent.delta.source], + unprocessed_intent.delta.mutation, + ) + else: + raise ValueError(f"{intent_id} is invalid") + + nb_processed += 1 + processed_intents[intent_id] = processed_intent + unprocessed_intent_ids.remove(intent_id) + + if nb_processed == 0 and unprocessed_intent_ids: + raise ValueError( + "Unresolvable dependency detected between intents: " + + ", ".join(i_id for i_id in unprocessed_intent_ids) + ) + + return processed_intents class FlightIntentsSpecification(ImplicitDict): - planning_time: StringBasedTimeDelta - """Time delta between the time uss_qualifier initiates this FlightInjectionAttempt and when a timestamp within the test_injection equal to reference_time occurs""" - file: ExternalFile - """Location of file to load""" + """Location of file to load, containing a FlightIntentCollection""" diff --git a/monitoring/uss_qualifier/resources/flight_planning/flight_intents_resource.py b/monitoring/uss_qualifier/resources/flight_planning/flight_intents_resource.py index 8655334f99..8d5ab9c145 100644 --- a/monitoring/uss_qualifier/resources/flight_planning/flight_intents_resource.py +++ b/monitoring/uss_qualifier/resources/flight_planning/flight_intents_resource.py @@ -1,85 +1,27 @@ -from datetime import timedelta import json from typing import Dict -import arrow -from implicitdict import ImplicitDict, StringBasedDateTime +from implicitdict import ImplicitDict +from monitoring.monitorlib.clients.flight_planning.flight_info_template import ( + FlightInfoTemplate, +) from monitoring.uss_qualifier.resources.files import load_dict -from monitoring.uss_qualifier.resources.overrides import apply_overrides from monitoring.uss_qualifier.resources.resource import Resource from monitoring.uss_qualifier.resources.flight_planning.flight_intent import ( FlightIntentCollection, FlightIntentsSpecification, - FlightIntent, FlightIntentID, ) class FlightIntentsResource(Resource[FlightIntentsSpecification]): - _planning_time: timedelta _intent_collection: FlightIntentCollection def __init__(self, specification: FlightIntentsSpecification): self._intent_collection = ImplicitDict.parse( load_dict(specification.file), FlightIntentCollection ) - self._planning_time = specification.planning_time.timedelta - - def get_flight_intents(self) -> Dict[FlightIntentID, FlightIntent]: - """Resolve the underlying delta flight intents and shift appropriately times.""" - - # process intents in order of dependency to resolve deltas - processed_intents: Dict[FlightIntentID, FlightIntent] = {} - unprocessed_intent_ids = list(self._intent_collection.intents.keys()) - - while unprocessed_intent_ids: - nb_processed = 0 - for intent_id in unprocessed_intent_ids: - unprocessed_intent = self._intent_collection.intents[intent_id] - processed_intent: FlightIntent - - # copy intent and resolve delta - if unprocessed_intent.has_field_with_value("full"): - processed_intent = ImplicitDict.parse( - json.loads(json.dumps(unprocessed_intent.full)), FlightIntent - ) - elif unprocessed_intent.has_field_with_value("delta"): - if unprocessed_intent.delta.source not in processed_intents: - # delta source has not been processed yet - continue - - processed_intent = apply_overrides( - processed_intents[unprocessed_intent.delta.source], - unprocessed_intent.delta.mutation, - ) - else: - raise ValueError(f"{intent_id} is invalid") - - nb_processed += 1 - processed_intents[intent_id] = processed_intent - unprocessed_intent_ids.remove(intent_id) - - if nb_processed == 0 and unprocessed_intent_ids: - raise ValueError( - "Unresolvable dependency detected between intents: " - + ", ".join(i_id for i_id in unprocessed_intent_ids) - ) - - # shift times - t0 = arrow.utcnow() + self._planning_time - - for intent_id, intent in processed_intents.items(): - dt = t0 - intent.reference_time.datetime - for volume in ( - intent.request.operational_intent.volumes - + intent.request.operational_intent.off_nominal_volumes - ): - volume.time_start.value = StringBasedDateTime( - volume.time_start.value.datetime + dt - ) - volume.time_end.value = StringBasedDateTime( - volume.time_end.value.datetime + dt - ) - return processed_intents + def get_flight_intents(self) -> Dict[FlightIntentID, FlightInfoTemplate]: + return self._intent_collection.resolve() diff --git a/monitoring/uss_qualifier/resources/overrides.py b/monitoring/uss_qualifier/resources/overrides.py index 60283c95ef..4bb504e089 100644 --- a/monitoring/uss_qualifier/resources/overrides.py +++ b/monitoring/uss_qualifier/resources/overrides.py @@ -37,9 +37,19 @@ def _apply_overrides(base_object, overrides): return result_list elif isinstance(overrides, dict): - result = ImplicitDict.parse(base_object, type(base_object)) + if isinstance(base_object, dict): + result = {k: v for k, v in base_object.items()} + else: + raise ValueError( + f"Attempted to override field with type {type(base_object)} with type {type(overrides)} ({json.dumps(base_object)} -> {json.dumps(overrides)})" + ) for field in overrides: - if field in base_object and base_object[field] is not None: + if field.startswith("+"): + replace = True + field = field[1:] + else: + replace = False + if field in base_object and base_object[field] is not None and not replace: result[field] = _apply_overrides(base_object[field], overrides[field]) else: result[field] = overrides[field] diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.md b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.md index d35466b351..fe98dbc53c 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.md @@ -18,9 +18,8 @@ FlightIntentsResource that provides the following flight intents: - `valid_conflict_tiny_overlap`: volumes mutation: has a volume that overlaps with `valid_op_intent` just above IntersectionMinimumPrecision = 1cm in a way that must result as a conflict Because the scenario involves activation of intents, all activated intents must be active during the execution of the -test scenario, i.e. they must start before the reference time plus planning time duration. Additionally, their end time -must leave sufficient time for the execution of the test scenario. For the sake of simplicity, it is recommended to set -the start and end times of all the intents to the same range. +test scenario. Additionally, their end time must leave sufficient time for the execution of the test scenario. For the +sake of simplicity, it is recommended to set the start and end times of all the intents to the same range. ### tested_uss FlightPlannerResource that will be tested for its validation of operational intents. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py index 733ff43461..f97af01bda 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py @@ -58,7 +58,10 @@ def __init__( self.tested_uss = tested_uss.flight_planner self.dss = dss.dss - _flight_intents = flight_intents.get_flight_intents() + _flight_intents = { + k: FlightIntent.from_flight_info_template(v) + for k, v in flight_intents.get_flight_intents().items() + } extents = [] for intent in _flight_intents.values(): diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.md b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.md index 323af0aab3..aab2519386 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.md @@ -21,7 +21,7 @@ Otherwise, the FlightIntentsResource must provide the following flight intents: - < + @@ -76,9 +76,8 @@ Otherwise, the FlightIntentsResource must provide the following flight intents:
Flight intent IDFlight intent ID Flight name Priority State
Because the scenario involves activation of intents, all activated intents must be active during the execution of the -test scenario, i.e. they must start before the reference time plus planning time duration. Additionally, their end time -must leave sufficient time for the execution of the test scenario. For the sake of simplicity, it is recommended to set -the start and end times of all the intents to the same range. +test scenario. Additionally, their end time must leave sufficient time for the execution of the test scenario. For the +sake of simplicity, it is recommended to set the start and end times of all the intents to the same range. ### tested_uss FlightPlannerResource that is under test and will manage flight 1. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py index b25af16ffc..1d7dd6fdbd 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py @@ -84,7 +84,10 @@ def __init__( ) raise ScenarioCannotContinueError(msg) - _flight_intents = flight_intents.get_flight_intents() + _flight_intents = { + k: FlightIntent.from_flight_info_template(v) + for k, v in flight_intents.get_flight_intents().items() + } extents = [] for intent in _flight_intents.values(): diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md index debfc437a0..3c9bd1dbad 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md @@ -74,9 +74,8 @@ FlightIntentsResource that provides the following flight intents: Because the scenario involves activation of intents, all activated intents must be active during the execution of the -test scenario, i.e. they must start before the reference time plus planning time duration. Additionally, their end time -must leave sufficient time for the execution of the test scenario. For the sake of simplicity, it is recommended to set -the start and end times of all the intents to the same range. +test scenario. Additionally, their end time must leave sufficient time for the execution of the test scenario. For the +sake of simplicity, it is recommended to set the start and end times of all the intents to the same range. ### tested_uss FlightPlannerResource that is under test and will manage flight 1. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py index d660ae127f..0aa29e08d5 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py @@ -75,7 +75,10 @@ def __init__( self.control_uss = control_uss.flight_planner self.dss = dss.dss - _flight_intents = flight_intents.get_flight_intents() + _flight_intents = { + k: FlightIntent.from_flight_info_template(v) + for k, v in flight_intents.get_flight_intents().items() + } extents = [] for intent in _flight_intents.values(): diff --git a/monitoring/uss_qualifier/scenarios/uspace/flight_auth/validation.py b/monitoring/uss_qualifier/scenarios/uspace/flight_auth/validation.py index 4adecdda32..8a362342d1 100644 --- a/monitoring/uss_qualifier/scenarios/uspace/flight_auth/validation.py +++ b/monitoring/uss_qualifier/scenarios/uspace/flight_auth/validation.py @@ -46,7 +46,8 @@ def __init__( ) self.invalid_flight_intents = [] - for fID, flight_intent in intents.items(): + for fID, info_template in intents.items(): + flight_intent = FlightIntent.from_flight_info_template(info_template) problems = problems_with_flight_authorisation( flight_intent.request.flight_authorisation ) diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.json b/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.json deleted file mode 100644 index dbeb97d9a1..0000000000 --- a/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.json +++ /dev/null @@ -1,1041 +0,0 @@ -{ - "intents": { - "flight_1_planned_vol_A": { - "full": { - "reference_time": "2023-02-12T10:34:14.483425+00:00", - "request": { - "operational_intent": { - "volumes": [ - { - "volume": { - "outline_polygon": { - "vertices": [ - { - "lng": 7.477423822749622, - "lat": 46.97491999984008 - }, - { - "lng": 7.477423821039847, - "lat": 46.97538499982026 - }, - { - "lng": 7.477424770457274, - "lat": 46.97539822817162 - }, - { - "lng": 7.477427609667229, - "lat": 46.975411329130466 - }, - { - "lng": 7.477432311328011, - "lat": 46.975424176527724 - }, - { - "lng": 7.477438830161459, - "lat": 46.97543664663613 - }, - { - "lng": 7.477447103388957, - "lat": 46.975448619361856 - }, - { - "lng": 7.477457051335981, - "lat": 46.975459979401066 - }, - { - "lng": 7.477468578199379, - "lat": 46.97547061735031 - }, - { - "lng": 7.477481572969964, - "lat": 46.97548043076024 - }, - { - "lng": 7.4774959105015855, - "lat": 46.97548932512221 - }, - { - "lng": 7.477511452716338, - "lat": 46.97549721477847 - }, - { - "lng": 7.47752804993433, - "lat": 46.97550402374711 - }, - { - "lng": 7.477545542315188, - "lat": 46.975509686453854 - }, - { - "lng": 7.477563761397435, - "lat": 46.97551414836356 - }, - { - "lng": 7.477582531720886, - "lat": 46.9755173665054 - }, - { - "lng": 7.477601672516463, - "lat": 46.975519309886806 - }, - { - "lng": 7.477620999447144, - "lat": 46.97551995979184 - }, - { - "lng": 7.478057000544437, - "lat": 46.97551995980457 - }, - { - "lng": 7.478076327475301, - "lat": 46.975519309900726 - }, - { - "lng": 7.478095468271412, - "lat": 46.97551736652059 - }, - { - "lng": 7.478114238595763, - "lat": 46.97551414838003 - }, - { - "lng": 7.478132457679282, - "lat": 46.97550968647161 - }, - { - "lng": 7.478149950061773, - "lat": 46.97550402376608 - }, - { - "lng": 7.478166547281737, - "lat": 46.97549721479852 - }, - { - "lng": 7.4781820894987705, - "lat": 46.97548932514323 - }, - { - "lng": 7.478196427032943, - "lat": 46.97548043078203 - }, - { - "lng": 7.478209421806301, - "lat": 46.97547061737267 - }, - { - "lng": 7.478220948672645, - "lat": 46.975459979423775 - }, - { - "lng": 7.478230896622737, - "lat": 46.97544861938469 - }, - { - "lng": 7.4782391698533655, - "lat": 46.97543664665883 - }, - { - "lng": 7.4782456886899595, - "lat": 46.975424176550064 - }, - { - "lng": 7.4782503903538515, - "lat": 46.97541132915219 - }, - { - "lng": 7.478253229566841, - "lat": 46.97539822819251 - }, - { - "lng": 7.478254178987183, - "lat": 46.975384999840095 - }, - { - "lng": 7.478254177277408, - "lat": 46.974919999820244 - }, - { - "lng": 7.478253227761765, - "lat": 46.97490677142331 - }, - { - "lng": 7.4782503884602685, - "lat": 46.97489367042681 - }, - { - "lng": 7.4782456867183855, - "lat": 46.97488082300081 - }, - { - "lng": 7.478239167817867, - "lat": 46.974868352873266 - }, - { - "lng": 7.478230894540598, - "lat": 46.974856380138455 - }, - { - "lng": 7.478220946563947, - "lat": 46.974845020100375 - }, - { - "lng": 7.478209419693374, - "lat": 46.974834382162385 - }, - { - "lng": 7.47819642493976, - "lat": 46.974824568773556 - }, - { - "lng": 7.4781820874502785, - "lat": 46.97481567444202 - }, - { - "lng": 7.478166545303149, - "lat": 46.97480778482489 - }, - { - "lng": 7.478149948177879, - "lat": 46.9748009759033 - }, - { - "lng": 7.4781324559137685, - "lat": 46.97479531325065 - }, - { - "lng": 7.478114236970597, - "lat": 46.97479085140123 - }, - { - "lng": 7.478095466806278, - "lat": 46.9747876333249 - }, - { - "lng": 7.478076326187155, - "lat": 46.97478569001336 - }, - { - "lng": 7.478056999447158, - "lat": 46.974785040181715 - }, - { - "lng": 7.477621000544429, - "lat": 46.97478504019445 - }, - { - "lng": 7.477601673804613, - "lat": 46.974785690027296 - } - ] - }, - "altitude_lower": { - "value": 605.0, - "reference": "W84", - "units": "M" - }, - "altitude_upper": { - "value": 635.0, - "reference": "W84", - "units": "M" - } - }, - "time_start": { - "value": "2023-02-12T10:27:14.483425+00:00", - "format": "RFC3339" - }, - "time_end": { - "value": "2023-02-12T10:52:14.483425+00:00", - "format": "RFC3339" - } - } - ], - "state": "Accepted", - "off_nominal_volumes": [], - "priority": 0 - }, - "flight_authorisation": { - "uas_serial_number": "1AF49UL5CC5J6K", - "operation_category": "Open", - "operation_mode": "Vlos", - "uas_class": "C0", - "identification_technologies": [ - "ASTMNetRID" - ], - "connectivity_methods": [ - "cellular" - ], - "endurance_minutes": 30, - "emergency_procedure_url": "https://example.interussplatform.org/emergency", - "operator_id": "CHEo5kut30e0mt01-qwe", - "uas_id": "", - "uas_type_certificate": "" - } - } - } - }, - "flight_1_activated_vol_A": { - "delta": { - "source": "flight_1_planned_vol_A", - "mutation": { - "request": { - "operational_intent": { - "state": "Activated" - } - } - } - } - }, - "flight_1_planned_vol_A_extended": { - "delta": { - "source": "flight_1_planned_vol_A", - "mutation": { - "request": { - "operational_intent": { - "volumes": [ - { - "volume": { - "altitude_lower": { - "value": 575.0 - } - } - } - ] - } - } - } - } - }, - "flight_1_activated_vol_A_extended": { - "delta": { - "source": "flight_1_planned_vol_A_extended", - "mutation": { - "request": { - "operational_intent": { - "state": "Activated" - } - } - } - } - }, - "flight_1_planned_vol_B": { - "delta": { - "source": "flight_1_planned_vol_A", - "mutation": { - "request": { - "operational_intent": { - "volumes": [ - { - "volume": { - "altitude_lower": { - "value": 650.0 - }, - "altitude_upper": { - "value": 705.0 - } - } - } - ] - } - } - } - } - }, - "flight_1_activated_vol_B": { - "delta": { - "source": "flight_1_planned_vol_B", - "mutation": { - "request": { - "operational_intent": { - "state": "Activated" - } - } - } - } - }, - "flight_2_planned_vol_A": { - "full": { - "reference_time": "2023-02-12T10:34:14.483425+00:00", - "request": { - "operational_intent": { - "volumes": [ - { - "volume": { - "outline_polygon": { - "vertices": [ - { - "lng": 7.474315728091042, - "lat": 46.97716400145211 - }, - { - "lng": 7.474303247689658, - "lat": 46.97717412304557 - }, - { - "lng": 7.474292276891787, - "lat": 46.9771850331586 - }, - { - "lng": 7.474282921352688, - "lat": 46.97719662672131 - }, - { - "lng": 7.474275271172041, - "lat": 46.97720879208173 - }, - { - "lng": 7.474269400026217, - "lat": 46.97722141208117 - }, - { - "lng": 7.4742653644586845, - "lat": 46.977234365182404 - }, - { - "lng": 7.474263203335437, - "lat": 46.97724752664021 - }, - { - "lng": 7.474262937470636, - "lat": 46.97726076970267 - }, - { - "lng": 7.474264569426098, - "lat": 46.97727396683188 - }, - { - "lng": 7.47426808348659, - "lat": 46.97728699093222 - }, - { - "lng": 7.474273445811106, - "lat": 46.97729971657432 - }, - { - "lng": 7.474280604758724, - "lat": 46.97731202120303 - }, - { - "lng": 7.4742894913859015, - "lat": 46.97732378631779 - }, - { - "lng": 7.47430002011039, - "lat": 46.977334898613684 - }, - { - "lng": 7.474312089535409, - "lat": 46.97734525107282 - }, - { - "lng": 7.4743255834261335, - "lat": 46.97735474399491 - }, - { - "lng": 7.47434037182908, - "lat": 46.977363285957416 - }, - { - "lng": 7.474356312323634, - "lat": 46.97737079469613 - }, - { - "lng": 7.47437325139364, - "lat": 46.977377197897326 - }, - { - "lng": 7.474391025905868, - "lat": 46.97738243389426 - }, - { - "lng": 7.474409464681103, - "lat": 46.977386452261086 - }, - { - "lng": 7.474428390142731, - "lat": 46.97738921429845 - }, - { - "lng": 7.4744476200269405, - "lat": 46.97739069340619 - }, - { - "lng": 7.474466969138073, - "lat": 46.97739087533958 - }, - { - "lng": 7.474486251132214, - "lat": 46.97738975834647 - }, - { - "lng": 7.474505280311834, - "lat": 46.97738735318418 - }, - { - "lng": 7.47452387341421, - "lat": 46.97738368301589 - }, - { - "lng": 7.474541851376407, - "lat": 46.97737878318756 - }, - { - "lng": 7.474559041059788, - "lat": 46.97737270088755 - }, - { - "lng": 7.474575276917473, - "lat": 46.9773654946921 - }, - { - "lng": 7.474590402588685, - "lat": 46.977357234001225 - }, - { - "lng": 7.474604272404609, - "lat": 46.97734799837037 - }, - { - "lng": 7.4780602717187445, - "lat": 46.97480899404357 - }, - { - "lng": 7.478072750859629, - "lat": 46.97479887202181 - }, - { - "lng": 7.47808372039712, - "lat": 46.97478796152745 - }, - { - "lng": 7.478093074689058, - "lat": 46.97477636763495 - }, - { - "lng": 7.478100723649223, - "lat": 46.97476420200029 - }, - { - "lng": 7.478106593614889, - "lat": 46.974751581785505 - }, - { - "lng": 7.478110628056212, - "lat": 46.97473862853046 - }, - { - "lng": 7.4781127881205816, - "lat": 46.9747254669823 - }, - { - "lng": 7.478113053006759, - "lat": 46.97471222389403 - }, - { - "lng": 7.478111420165148, - "lat": 46.974699026803876 - }, - { - "lng": 7.478107905322289, - "lat": 46.97468600280696 - }, - { - "lng": 7.478102542329355, - "lat": 46.974673277331334 - }, - { - "lng": 7.478095382836098, - "lat": 46.97466097293006 - }, - { - "lng": 7.478086495793389, - "lat": 46.9746492081009 - }, - { - "lng": 7.478075966789133, - "lat": 46.97463809614524 - }, - { - "lng": 7.47806389722399, - "lat": 46.97462774407688 - }, - { - "lng": 7.478050403334804, - "lat": 46.97461825159139 - }, - { - "lng": 7.4780356150751714, - "lat": 46.97460971010614 - }, - { - "lng": 7.478019674863899, - "lat": 46.97460220187985 - }, - { - "lng": 7.478002736213457, - "lat": 46.97459579922035 - }, - { - "lng": 7.477984962251586, - "lat": 46.97459056378839 - }, - { - "lng": 7.477966524150307, - "lat": 46.974586546003664 - }, - { - "lng": 7.477947599477487, - "lat": 46.974583784559336 - }, - { - "lng": 7.477928370486799, - "lat": 46.97458230604945 - }, - { - "lng": 7.477909022362576, - "lat": 46.974582124712704 - }, - { - "lng": 7.4778897414364325, - "lat": 46.97458324229544 - }, - { - "lng": 7.47787071339284, - "lat": 46.974585648034825 - }, - { - "lng": 7.477852121480946, - "lat": 46.974589318762405 - }, - { - "lng": 7.477834144749823, - "lat": 46.974594219127326 - }, - { - "lng": 7.47781695632419, - "lat": 46.97460030193667 - }, - { - "lng": 7.477800721737151, - "lat": 46.97460750861006 - }, - { - "lng": 7.47778559733606, - "lat": 46.974615769743735 - }, - { - "lng": 7.477771728776835, - "lat": 46.97462500577891 - } - ] - }, - "altitude_lower": { - "value": 605.0, - "reference": "W84", - "units": "M" - }, - "altitude_upper": { - "value": 635.0, - "reference": "W84", - "units": "M" - } - }, - "time_start": { - "value": "2023-02-12T10:27:14.483425+00:00", - "format": "RFC3339" - }, - "time_end": { - "value": "2023-02-12T10:52:14.483425+00:00", - "format": "RFC3339" - } - } - ], - "state": "Accepted", - "off_nominal_volumes": [], - "priority": 100 - }, - "flight_authorisation": { - "uas_serial_number": "1AF49UL5CC5J6K", - "operation_category": "Open", - "operation_mode": "Vlos", - "uas_class": "C0", - "identification_technologies": [ - "ASTMNetRID" - ], - "connectivity_methods": [ - "cellular" - ], - "endurance_minutes": 30, - "emergency_procedure_url": "https://example.interussplatform.org/emergency", - "operator_id": "CHEo5kut30e0mt01-qwe", - "uas_id": "", - "uas_type_certificate": "" - } - } - } - }, - "flight_2_activated_vol_A": { - "delta": { - "source": "flight_2_planned_vol_A", - "mutation": { - "request": { - "operational_intent": { - "state": "Activated" - } - } - } - } - }, - "flight_2_activated_vol_B": { - "delta": { - "source": "flight_2_activated_vol_A", - "mutation": { - "request": { - "operational_intent": { - "volumes": [ - { - "volume": { - "altitude_lower": { - "value": 650.0 - }, - "altitude_upper": { - "value": 705.0 - } - } - } - ] - } - } - } - } - }, - "flight_2_equal_prio_planned_vol_B": { - "delta": { - "source": "flight_2_activated_vol_B", - "mutation": { - "request": { - "operational_intent": { - "state": "Accepted", - "priority": 0 - } - } - } - } - }, - "flight_2_equal_prio_activated_vol_B": { - "delta": { - "source": "flight_2_equal_prio_planned_vol_B", - "mutation": { - "request": { - "operational_intent": { - "state": "Activated" - } - } - } - } - }, - "flight_2_equal_prio_nonconforming_vol_A": { - "delta": { - "source": "flight_2_equal_prio_activated_vol_B", - "mutation": { - "request": { - "operational_intent": { - "state": "Nonconforming", - "off_nominal_volumes": [ - { - "volume": { - "outline_polygon": { - "vertices": [ - { - "lng": 7.474315728091042, - "lat": 46.97716400145211 - }, - { - "lng": 7.474303247689658, - "lat": 46.97717412304557 - }, - { - "lng": 7.474292276891787, - "lat": 46.9771850331586 - }, - { - "lng": 7.474282921352688, - "lat": 46.97719662672131 - }, - { - "lng": 7.474275271172041, - "lat": 46.97720879208173 - }, - { - "lng": 7.474269400026217, - "lat": 46.97722141208117 - }, - { - "lng": 7.4742653644586845, - "lat": 46.977234365182404 - }, - { - "lng": 7.474263203335437, - "lat": 46.97724752664021 - }, - { - "lng": 7.474262937470636, - "lat": 46.97726076970267 - }, - { - "lng": 7.474264569426098, - "lat": 46.97727396683188 - }, - { - "lng": 7.47426808348659, - "lat": 46.97728699093222 - }, - { - "lng": 7.474273445811106, - "lat": 46.97729971657432 - }, - { - "lng": 7.474280604758724, - "lat": 46.97731202120303 - }, - { - "lng": 7.4742894913859015, - "lat": 46.97732378631779 - }, - { - "lng": 7.47430002011039, - "lat": 46.977334898613684 - }, - { - "lng": 7.474312089535409, - "lat": 46.97734525107282 - }, - { - "lng": 7.4743255834261335, - "lat": 46.97735474399491 - }, - { - "lng": 7.47434037182908, - "lat": 46.977363285957416 - }, - { - "lng": 7.474356312323634, - "lat": 46.97737079469613 - }, - { - "lng": 7.47437325139364, - "lat": 46.977377197897326 - }, - { - "lng": 7.474391025905868, - "lat": 46.97738243389426 - }, - { - "lng": 7.474409464681103, - "lat": 46.977386452261086 - }, - { - "lng": 7.474428390142731, - "lat": 46.97738921429845 - }, - { - "lng": 7.4744476200269405, - "lat": 46.97739069340619 - }, - { - "lng": 7.474466969138073, - "lat": 46.97739087533958 - }, - { - "lng": 7.474486251132214, - "lat": 46.97738975834647 - }, - { - "lng": 7.474505280311834, - "lat": 46.97738735318418 - }, - { - "lng": 7.47452387341421, - "lat": 46.97738368301589 - }, - { - "lng": 7.474541851376407, - "lat": 46.97737878318756 - }, - { - "lng": 7.474559041059788, - "lat": 46.97737270088755 - }, - { - "lng": 7.474575276917473, - "lat": 46.9773654946921 - }, - { - "lng": 7.474590402588685, - "lat": 46.977357234001225 - }, - { - "lng": 7.474604272404609, - "lat": 46.97734799837037 - }, - { - "lng": 7.4780602717187445, - "lat": 46.97480899404357 - }, - { - "lng": 7.478072750859629, - "lat": 46.97479887202181 - }, - { - "lng": 7.47808372039712, - "lat": 46.97478796152745 - }, - { - "lng": 7.478093074689058, - "lat": 46.97477636763495 - }, - { - "lng": 7.478100723649223, - "lat": 46.97476420200029 - }, - { - "lng": 7.478106593614889, - "lat": 46.974751581785505 - }, - { - "lng": 7.478110628056212, - "lat": 46.97473862853046 - }, - { - "lng": 7.4781127881205816, - "lat": 46.9747254669823 - }, - { - "lng": 7.478113053006759, - "lat": 46.97471222389403 - }, - { - "lng": 7.478111420165148, - "lat": 46.974699026803876 - }, - { - "lng": 7.478107905322289, - "lat": 46.97468600280696 - }, - { - "lng": 7.478102542329355, - "lat": 46.974673277331334 - }, - { - "lng": 7.478095382836098, - "lat": 46.97466097293006 - }, - { - "lng": 7.478086495793389, - "lat": 46.9746492081009 - }, - { - "lng": 7.478075966789133, - "lat": 46.97463809614524 - }, - { - "lng": 7.47806389722399, - "lat": 46.97462774407688 - }, - { - "lng": 7.478050403334804, - "lat": 46.97461825159139 - }, - { - "lng": 7.4780356150751714, - "lat": 46.97460971010614 - }, - { - "lng": 7.478019674863899, - "lat": 46.97460220187985 - }, - { - "lng": 7.478002736213457, - "lat": 46.97459579922035 - }, - { - "lng": 7.477984962251586, - "lat": 46.97459056378839 - }, - { - "lng": 7.477966524150307, - "lat": 46.974586546003664 - }, - { - "lng": 7.477947599477487, - "lat": 46.974583784559336 - }, - { - "lng": 7.477928370486799, - "lat": 46.97458230604945 - }, - { - "lng": 7.477909022362576, - "lat": 46.974582124712704 - }, - { - "lng": 7.4778897414364325, - "lat": 46.97458324229544 - }, - { - "lng": 7.47787071339284, - "lat": 46.974585648034825 - }, - { - "lng": 7.477852121480946, - "lat": 46.974589318762405 - }, - { - "lng": 7.477834144749823, - "lat": 46.974594219127326 - }, - { - "lng": 7.47781695632419, - "lat": 46.97460030193667 - }, - { - "lng": 7.477800721737151, - "lat": 46.97460750861006 - }, - { - "lng": 7.47778559733606, - "lat": 46.974615769743735 - }, - { - "lng": 7.477771728776835, - "lat": 46.97462500577891 - } - ] - }, - "altitude_lower": { - "value": 605.0, - "reference": "W84", - "units": "M" - }, - "altitude_upper": { - "value": 635.0, - "reference": "W84", - "units": "M" - } - }, - "time_start": { - "value": "2023-02-12T10:27:14.483425+00:00", - "format": "RFC3339" - }, - "time_end": { - "value": "2023-02-12T10:52:14.483425+00:00", - "format": "RFC3339" - } - } - ] - } - } - } - } - } - } -} diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.yaml b/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.yaml new file mode 100644 index 0000000000..b4f295cec5 --- /dev/null +++ b/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.yaml @@ -0,0 +1,210 @@ +intents: + flight_1_planned_vol_A: + full: + basic_information: + usage_state: Planned + uas_state: Nominal + area: + - outline_polygon: + vertices: + - lng: 7.477419 + lat: 46.974785 + - lng: 7.478261 + lat: 46.974776 + - lng: 7.478263 + lat: 46.975519 + - lng: 7.477415 + lat: 46.975528 + altitude_lower: + value: 605.01 + reference: W84 + units: M + altitude_upper: + value: 635 + reference: W84 + units: M + start_time: + offset_from: + starting_from: + start_of_test: {} + offset: -1s + end_time: + offset_from: + starting_from: + start_of_test: {} + offset: 8m + + astm_f3548_21: + priority: 0 + + uspace_flight_authorisation: + uas_serial_number: 1AF49UL5CC5J6K + operation_category: Open + operation_mode: Vlos + uas_class: C0 + identification_technologies: + - ASTMNetRID + connectivity_methods: + - cellular + endurance_minutes: 30 + emergency_procedure_url: https://example.interussplatform.org/emergency + operator_id: CHEo5kut30e0mt01-qwe + uas_id: '' + uas_type_certificate: '' + + flight_1_activated_vol_A: + delta: + source: flight_1_planned_vol_A + mutation: + basic_information: + usage_state: InUse + area: + - altitude_lower: + value: 605.02 + + flight_1_planned_vol_A_extended: + delta: + source: flight_1_planned_vol_A + mutation: + basic_information: + area: + - outline_polygon: + altitude_lower: + value: 575.03 + + flight_1_planned_vol_B: + delta: + source: flight_1_planned_vol_A + mutation: + basic_information: + area: + - altitude_lower: + value: 650.04 + altitude_upper: + value: 705 + + flight_1_activated_vol_A_extended: + delta: + source: flight_1_planned_vol_A_extended + mutation: + basic_information: + usage_state: InUse + area: + - altitude_lower: + value: 575.05 + + flight_1_activated_vol_B: + delta: + source: flight_1_planned_vol_B + mutation: + basic_information: + usage_state: InUse + area: + - altitude_lower: + value: 650.06 + + flight_2_planned_vol_A: + full: + basic_information: + usage_state: Planned + uas_state: Nominal + area: + - outline_polygon: + vertices: + - lng: 7.477918 + lat: 46.974515 + - lng: 7.478182 + lat: 46.974719 + - lng: 7.474489 + lat: 46.977435 + - lng: 7.474139 + lat: 46.977289 + altitude_lower: + value: 605.07 + reference: W84 + units: M + altitude_upper: + value: 635 + reference: W84 + units: M + start_time: + offset_from: + starting_from: + start_of_test: {} + offset: -1s + end_time: + offset_from: + starting_from: + start_of_test: {} + offset: 8m + + astm_f3548_21: + priority: 100 + + uspace_flight_authorisation: + uas_serial_number: 1AF49UL5CC5J6K + operation_category: Open + operation_mode: Vlos + uas_class: C0 + identification_technologies: + - ASTMNetRID + connectivity_methods: + - cellular + endurance_minutes: 30 + emergency_procedure_url: https://example.interussplatform.org/emergency + operator_id: CHEo5kut30e0mt01-qwe + uas_id: '' + uas_type_certificate: '' + + flight_2_activated_vol_A: + delta: + source: flight_2_planned_vol_A + mutation: + basic_information: + usage_state: InUse + area: + - altitude_lower: + value: 605.08 + + flight_2_activated_vol_B: + delta: + source: flight_2_activated_vol_A + mutation: + basic_information: + area: + - altitude_lower: + value: 650.09 + altitude_upper: + value: 705 + + flight_2_equal_prio_planned_vol_B: + delta: + source: flight_2_activated_vol_B + mutation: + basic_information: + usage_state: Planned + area: + - altitude_lower: + value: 650.10 + astm_f3548_21: + priority: 0 + + flight_2_equal_prio_activated_vol_B: + delta: + source: flight_2_equal_prio_planned_vol_B + mutation: + basic_information: + usage_state: InUse + area: + - altitude_lower: + value: 650.11 + + flight_2_equal_prio_nonconforming_vol_A: + delta: + source: flight_2_equal_prio_activated_vol_B + mutation: + basic_information: + uas_state: OffNominal + area: + - altitude_lower: + value: 605.12 diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.json b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.json deleted file mode 100644 index 2a72a323d3..0000000000 --- a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "intents": { - "invalid_flight_auth": { - "full": { - "reference_time": "2023-02-12T10:34:14.681217+00:00", - "request": { - "operational_intent": { - "volumes": [ - { - "volume": { - "outline_polygon": { - "vertices": [ - { - "lng": 7.477504823470508, - "lat": 46.97472299984816 - }, - { - "lng": 7.477504820370851, - "lat": 46.97556599981216 - }, - { - "lng": 7.477505769789705, - "lat": 46.97557922815897 - } - ] - }, - "altitude_lower": { - "value": 605.0, - "reference": "W84", - "units": "M" - }, - "altitude_upper": { - "value": 635.0, - "reference": "W84", - "units": "M" - } - }, - "time_start": { - "value": "2023-02-12T10:27:05.359502+00:00", - "format": "RFC3339" - }, - "time_end": { - "value": "2023-02-12T10:52:05.359502+00:00", - "format": "RFC3339" - } - } - ], - "state": "Accepted", - "off_nominal_volumes": [], - "priority": 0 - }, - "flight_authorisation": { - "uas_serial_number": "My serial number", - "operation_category": "Open", - "operation_mode": "Vlos", - "uas_class": "C0", - "identification_technologies": [ - "ASTMNetRID" - ], - "connectivity_methods": [ - "cellular" - ], - "endurance_minutes": 30, - "emergency_procedure_url": "https://example.interussplatform.org/emergency", - "operator_id": "CHEo5kut30e0mt01-qwe" - } - } - } - }, - "valid_flight_auth": { - "full": { - "reference_time": "2023-02-12T10:34:14.681217+00:00", - "request": { - "operational_intent": { - "volumes": [ - { - "volume": { - "outline_polygon": { - "vertices": [ - { - "lng": 7.477504823470508, - "lat": 46.97472299984816 - }, - { - "lng": 7.477504820370851, - "lat": 46.97556599981216 - }, - { - "lng": 7.477505769789705, - "lat": 46.97557922815897 - } - ] - }, - "altitude_lower": { - "value": 605.0, - "reference": "W84", - "units": "M" - }, - "altitude_upper": { - "value": 635.0, - "reference": "W84", - "units": "M" - } - }, - "time_start": { - "value": "2023-02-12T10:27:05.359502+00:00", - "format": "RFC3339" - }, - "time_end": { - "value": "2023-02-12T10:52:05.359502+00:00", - "format": "RFC3339" - } - } - ], - "state": "Accepted", - "off_nominal_volumes": [], - "priority": 0 - }, - "flight_authorisation": { - "uas_serial_number": "1AF49UL5CC5J6K", - "operation_category": "Open", - "operation_mode": "Vlos", - "uas_class": "C0", - "identification_technologies": [ - "ASTMNetRID" - ], - "connectivity_methods": [ - "cellular" - ], - "endurance_minutes": 30, - "emergency_procedure_url": "https://example.interussplatform.org/emergency", - "operator_id": "CHEo5kut30e0mt01-qwe" - } - } - } - } - } -} diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.yaml b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.yaml new file mode 100644 index 0000000000..ad1eb3eb85 --- /dev/null +++ b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.yaml @@ -0,0 +1,62 @@ +intents: + invalid_flight_auth: + full: + basic_information: + usage_state: Planned + uas_state: Nominal + area: + - outline_polygon: + vertices: + - lng: 7.477504823470508 + lat: 46.97472299984816 + - lng: 7.477504820370851 + lat: 46.97556599981216 + - lng: 7.477505769789705 + lat: 46.97557922815897 + altitude_lower: + value: 605 + reference: W84 + units: M + altitude_upper: + value: 635 + reference: W84 + units: M + start_time: + offset_from: + starting_from: + start_of_test: {} + offset: -1s + duration: 5m + + astm_f3548_21: + priority: 0 + + uspace_flight_authorisation: + uas_serial_number: My serial number + operation_category: Open + operation_mode: Vlos + uas_class: C0 + identification_technologies: + - ASTMNetRID + connectivity_methods: + - cellular + endurance_minutes: 30 + emergency_procedure_url: https://uav.example.com/emergency + operator_id: CHEo5kut30e0mt01-qwe + + valid_flight_auth: + delta: + source: invalid_flight_auth + mutation: + uspace_flight_authorisation: + uas_serial_number: 1AF49UL5CC5J6K + operation_category: Open + operation_mode: Vlos + uas_class: C0 + identification_technologies: + - ASTMNetRID + connectivity_methods: + - cellular + endurance_minutes: 30 + emergency_procedure_url: https://uav.example.com/emergency + operator_id: CHEo5kut30e0mt01-qwe diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_intents.yaml b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_intents.yaml index 31d2e98e7e..f3cb2f07a3 100644 --- a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_intents.yaml +++ b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_intents.yaml @@ -1,117 +1,81 @@ intents: valid_flight: full: - reference_time: '2023-02-12T10:34:14.681217+00:00' - request: - operational_intent: - volumes: - - volume: - outline_polygon: - vertices: - - lat: 46.1929645 - lng: 6.1965395 - - lat: 46.192911 - lng: 6.196423 - - lat: 46.192918 - lng: 6.196678 - altitude_lower: - value: 605 - reference: W84 - units: M - altitude_upper: - value: 635 - reference: W84 - units: M - time_start: - value: '2023-02-12T10:27:05.359502+00:00' - format: RFC3339 - time_end: - value: '2023-02-12T10:52:05.359502+00:00' - format: RFC3339 - state: Accepted - off_nominal_volumes: [ ] - priority: 0 - flight_authorisation: - uas_serial_number: 1AF49UL5CC5J6K - operation_category: Open - operation_mode: Vlos - uas_class: C0 - identification_technologies: - - ASTMNetRID - connectivity_methods: - - cellular - endurance_minutes: 30 - emergency_procedure_url: https://example.interussplatform.org/emergency - operator_id: CHEo5kut30e0mt01-qwe + basic_information: + usage_state: Planned + uas_state: Nominal + area: + - outline_polygon: + vertices: + - lat: 46.1929645 + lng: 6.1965395 + - lat: 46.192911 + lng: 6.196423 + - lat: 46.192918 + lng: 6.196678 + altitude_lower: + value: 605 + reference: W84 + units: M + altitude_upper: + value: 635 + reference: W84 + units: M + start_time: + offset_from: + starting_from: + start_of_test: {} + offset: -1s + duration: 5m - valid_activated: - delta: - source: valid_flight - mutation: - request: - operational_intent: - state: Activated + astm_f3548_21: + priority: 0 - invalid_accepted_offnominal: - delta: - source: valid_flight - mutation: - request: - operational_intent: - off_nominal_volumes: - - volume: - outline_polygon: - vertices: - - lat: 46.1929645 - lng: 6.1965395 - - lat: 46.192911 - lng: 6.196423 - - lat: 46.192918 - lng: 6.196678 - altitude_lower: - value: 605 - reference: W84 - units: M - altitude_upper: - value: 635 - reference: W84 - units: M - time_start: - value: '2023-02-12T10:27:05.359502+00:00' - format: RFC3339 - time_end: - value: '2023-02-12T10:57:05.359502+00:00' - format: RFC3339 + uspace_flight_authorisation: + uas_serial_number: 1AF49UL5CC5J6K + operation_category: Open + operation_mode: Vlos + uas_class: C0 + identification_technologies: + - ASTMNetRID + connectivity_methods: + - cellular + endurance_minutes: 30 + emergency_procedure_url: https://uav.example.com/emergency + operator_id: CHEo5kut30e0mt01-qwe - invalid_activated_offnominal: + valid_activated: delta: - source: invalid_accepted_offnominal + source: valid_flight mutation: - request: - operational_intent: - state: Activated + basic_information: + usage_state: InUse invalid_too_far_away: delta: source: valid_flight mutation: - # reference time pulled back of 32 days > OiMaxPlanHorizon = 30 days - reference_time: '2023-01-10T10:34:14.681217+00:00' + # 32 days > OiMaxPlanHorizon = 30 days + basic_information: + area: + - start_time: + offset_from: + starting_from: + start_of_test: { } + offset: 32d valid_conflict_tiny_overlap: delta: source: valid_flight mutation: - request: - operational_intent: - volumes: - - volume: - outline_polygon: - # polygon overlaps with polygon of valid_flight, distance between first vertex of each is 1.112cm - vertices: - - lat: 46.1929644 - lng: 6.1965395 - - lat: 46.193236 - lng: 6.196235 - - lat: 46.193174 - lng: 6.196925 + basic_information: + area: + - outline_polygon: + # polygon overlaps with polygon of valid_flight, distance between first vertex of each is 1.112cm + vertices: + - lat: 46.1929644 + lng: 6.1965395 + - lat: 46.193236 + lng: 6.196235 + - lat: 46.193174 + lng: 6.196925 diff --git a/monitoring/uss_qualifier/test_data/make_flight_intent_kml.py b/monitoring/uss_qualifier/test_data/make_flight_intent_kml.py new file mode 100644 index 0000000000..357eef2347 --- /dev/null +++ b/monitoring/uss_qualifier/test_data/make_flight_intent_kml.py @@ -0,0 +1,230 @@ +#!env/bin/python3 + +import argparse +import json +import os +import sys + +import arrow +from loguru import logger +from lxml import etree +from pykml.factory import KML_ElementMaker as kml +from pykml.util import format_xml_with_cdata +import yaml + +from implicitdict import ImplicitDict, StringBasedDateTime +from monitoring.monitorlib.geo import AltitudeDatum, Altitude, DistanceUnits +from monitoring.uss_qualifier.fileio import load_dict_with_references, resolve_filename +from monitoring.uss_qualifier.resources.flight_planning.flight_intent import ( + FlightIntentCollection, +) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Produce a KML file showing a FlightIntentCollection" + ) + + parser.add_argument( + "flight_intent_collection", + help="Path to file containing FlightIntentCollection (*.yaml or *.json). When using the file system, prefix with file://", + ) + + parser.add_argument( + "--start_of_test", + default=None, + help="When start_of_test should be. Defaults to now.", + ) + + parser.add_argument( + "--geoid_offset", + default=None, + help="Height of the EGM96 geoid above the WGS84 ellipsoid in the area (meters). Can be obtained as 'EGM96' at https://geographiclib.sourceforge.io/cgi-bin/GeoidEval", + ) + + return parser.parse_args() + + +def _altitude_mode_of(altitude: Altitude) -> str: + if altitude.reference == AltitudeDatum.W84: + return "absolute" + else: + raise NotImplementedError( + f"Altitude reference {altitude.reference} not yet supported" + ) + + +def _altitude_value_of(altitude: Altitude) -> float: + if altitude.units == DistanceUnits.M: + return altitude.value + else: + raise NotImplementedError(f"Altitude units {altitude.units} not yet supported") + + +def main() -> int: + args = parse_args() + + path = args.flight_intent_collection + output_path = os.path.splitext(resolve_filename(path))[0] + ".kml" + + start_of_test = StringBasedDateTime(args.start_of_test or arrow.utcnow().datetime) + if args.geoid_offset is None: + logger.warning( + "geoid_offset was not provided. Assuming 0 offset, and this may cause altitude errors of up to tens of meters." + ) + geoid_offset = 0 + else: + geoid_offset = float(args.geoid_offset) + + raw = load_dict_with_references(path) + collection: FlightIntentCollection = ImplicitDict.parse(raw, FlightIntentCollection) + flight_intents = collection.resolve() + + folders = [] + for name, template in flight_intents.items(): + flight_intent = template.resolve(start_of_test.datetime) + non_basic_info = json.loads( + json.dumps( + {k: v for k, v in flight_intent.items() if k != "basic_information"} + ) + ) + description = yaml.dump(non_basic_info) if non_basic_info else None + folder = kml.Folder(kml.name(name)) + basic_info = flight_intent.basic_information + for i, v4 in enumerate(basic_info.area): + if "outline_polygon" in v4.volume and v4.volume.outline_polygon: + # Create placemark + placemark = kml.Placemark( + kml.name(f"{name}: volume {i}"), + kml.styleUrl( + f"#{basic_info.usage_state.value}_{basic_info.uas_state.value}" + ), + ) + if description: + placemark.append(kml.description("
" + description + "
")) + + # Set time range + timespan = None + if "time_start" in v4 and v4.time_start: + timespan = kml.TimeSpan( + kml.begin(v4.time_start.datetime.isoformat()) + ) + if "time_end" in v4 and v4.time_end: + if timespan is None: + timespan = kml.TimeSpan() + timespan.append(kml.end(v4.time_end.datetime.isoformat())) + if timespan is not None: + placemark.append(timespan) + + # Create top and bottom of the volume + vertices = v4.volume.outline_polygon.vertices + lower_coords = [] + upper_coords = [] + alt_lo = _altitude_value_of(v4.volume.altitude_lower) - geoid_offset + alt_hi = _altitude_value_of(v4.volume.altitude_upper) - geoid_offset + for vertex in vertices: + lower_coords.append((vertex.lng, vertex.lat, alt_lo)) + upper_coords.append((vertex.lng, vertex.lat, alt_hi)) + geo = kml.MultiGeometry( + kml.Polygon( + kml.altitudeMode(_altitude_mode_of(v4.volume.altitude_lower)), + kml.outerBoundaryIs( + kml.LinearRing( + kml.coordinates( + " ".join( + ",".join(str(v) for v in c) + for c in lower_coords + ) + ) + ) + ), + ), + kml.Polygon( + kml.altitudeMode(_altitude_mode_of(v4.volume.altitude_upper)), + kml.outerBoundaryIs( + kml.LinearRing( + kml.coordinates( + " ".join( + ",".join(str(v) for v in c) + for c in upper_coords + ) + ) + ) + ), + ), + ) + + # We can only create the sides of the volume if the altitude references are the same + if ( + v4.volume.altitude_lower.reference + == v4.volume.altitude_upper.reference + ): + indices = list(range(len(vertices))) + for i1, i2 in zip(indices, indices[1:] + [0]): + coords = [ + (vertices[i1].lng, vertices[i1].lat, alt_lo), + (vertices[i1].lng, vertices[i1].lat, alt_hi), + (vertices[i2].lng, vertices[i2].lat, alt_hi), + (vertices[i2].lng, vertices[i2].lat, alt_lo), + ] + geo.append( + kml.Polygon( + kml.altitudeMode( + _altitude_mode_of(v4.volume.altitude_lower) + ), + kml.outerBoundaryIs( + kml.LinearRing( + kml.coordinates( + " ".join( + ",".join(str(v) for v in c) + for c in coords + ) + ) + ) + ), + ) + ) + + placemark.append(geo) + folder.append(placemark) + else: + raise NotImplementedError("Volume footprint type not supported") + folders.append(folder) + doc = kml.kml( + kml.Document( + kml.Style( + kml.LineStyle(kml.color("ff00c000"), kml.width(3)), + kml.PolyStyle(kml.color("80808080")), + id="Planned_Nominal", + ), + kml.Style( + kml.LineStyle(kml.color("ff00c000"), kml.width(3)), + kml.PolyStyle(kml.color("8000ff00")), + id="InUse_Nominal", + ), + kml.Style( + kml.LineStyle(kml.color("ff00ffff"), kml.width(5)), + kml.PolyStyle(kml.color("8000ff00")), + id="InUse_OffNominal", + ), + kml.Style( + kml.LineStyle(kml.color("ff0000ff"), kml.width(5)), + kml.PolyStyle(kml.color("8000ff00")), + id="InUse_Contingent", + ), + *folders, + ) + ) + + with open(output_path, "w") as f: + f.write( + etree.tostring(format_xml_with_cdata(doc), pretty_print=True).decode( + "utf-8" + ) + ) + + return os.EX_OK + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/conflicting_flights.yaml b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/conflicting_flights.yaml deleted file mode 100644 index cf7f2a9794..0000000000 --- a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/conflicting_flights.yaml +++ /dev/null @@ -1,122 +0,0 @@ -intents: - first_flight: - full: - reference_time: '2023-01-01T00:12:00+00:00' - request: - operational_intent: - volumes: - - volume: - outline_polygon: - vertices: - - lat: 37.19330428406196 - lng: -80.59314014213832 - - lat: 37.19026757574101 - lng: -80.59428502333573 - - lat: 37.18526788213324 - lng: -80.59005346211121 - - lat: 37.1837232669677 - lng: -80.58471856692397 - - lat: 37.18696946882951 - lng: -80.58072507989046 - - lat: 37.19117638063535 - lng: -80.57796221630451 - - lat: 37.19735957232225 - lng: -80.56931360695863 - - lat: 37.19892943598067 - lng: -80.57104154088566 - - lat: 37.19763628976819 - lng: -80.57674276197271 - - lat: 37.19465764212709 - lng: -80.58235632399915 - - lat: 37.19637327692024 - lng: -80.58569665094863 - altitude_lower: - value: 474 - reference: W84 - units: M - altitude_upper: - value: 560 - reference: W84 - units: M - time_start: - value: '2023-01-01T00:05:00+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:17:00+00:00' - format: RFC3339 - state: Accepted - off_nominal_volumes: [ ] - priority: 0 - # TODO: Remove flight_authorisation section when it is optional - flight_authorisation: - uas_serial_number: 1AF49UL5CC5J6K - operation_category: Open - operation_mode: Bvlos - uas_class: C0 - identification_technologies: [ 'N/A' ] - connectivity_methods: [ 'N/A' ] - endurance_minutes: 30 - emergency_procedure_url: https://example.uav.com/emergency - operator_id: CHEo5kut30e0mt01-qwe - uas_id: '' - uas_type_certificate: '' - - first_flight_activated: - delta: - source: first_flight - mutation: - request: - operational_intent: - state: Activated - - conflicting_flight: - full: - reference_time: '2023-01-01T00:12:00+00:00' - request: - operational_intent: - volumes: - - volume: - outline_polygon: - vertices: - - lat: 37.19060214692082 - lng: -80.57754923361475 - - lat: 37.1954872363992 - lng: -80.57106342715623 - - lat: 37.19906626886441 - lng: -80.56532842695125 - - lat: 37.19958021621874 - lng: -80.56597582177831 - - lat: 37.19672366937564 - lng: -80.57147209739449 - - lat: 37.19115600938851 - lng: -80.57855594239999 - altitude_lower: - value: 480 - reference: W84 - units: M - altitude_upper: - value: 540 - reference: W84 - units: M - time_start: - value: '2023-01-01T00:06:20+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:20:40+00:00' - format: RFC3339 - state: Accepted - off_nominal_volumes: [ ] - priority: 0 - # TODO: Remove flight_authorisation section when it is optional - flight_authorisation: - uas_serial_number: 1AF49UL5CC5J6K - operation_category: Open - operation_mode: Bvlos - uas_class: C0 - identification_technologies: [ 'N/A' ] - connectivity_methods: [ 'N/A' ] - endurance_minutes: 30 - emergency_procedure_url: https://example.uav.com/emergency - operator_id: CHEo5kut30e0mt01-qwe - uas_id: '' - uas_type_certificate: '' diff --git a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/invalid_flight_intents.yaml b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/invalid_flight_intents.yaml deleted file mode 100644 index 01ddb804a7..0000000000 --- a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/invalid_flight_intents.yaml +++ /dev/null @@ -1,117 +0,0 @@ -intents: - valid_flight: - full: - reference_time: '2023-01-01T00:12:00+00:00' - request: - operational_intent: - volumes: - - volume: - outline_polygon: - vertices: - - lat: 37.19330428406196 - lng: -80.59314014213832 - - lat: 37.1837232669677 - lng: -80.58471856692397 - - lat: 37.19735957232225 - lng: -80.56931360695863 - altitude_lower: - value: 474 - reference: W84 - units: M - altitude_upper: - value: 560 - reference: W84 - units: M - time_start: - value: '2023-01-01T00:05:00+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:17:00+00:00' - format: RFC3339 - state: Accepted - off_nominal_volumes: [ ] - priority: 0 - flight_authorisation: - uas_serial_number: 1AF49UL5CC5J6K - operation_category: Open - operation_mode: Bvlos - uas_class: C0 - identification_technologies: [ 'N/A' ] - connectivity_methods: [ 'N/A' ] - endurance_minutes: 30 - emergency_procedure_url: https://example.uav.com/emergency - operator_id: CHEo5kut30e0mt01-qwe - uas_id: '' - uas_type_certificate: '' - - valid_activated: - delta: - source: valid_flight - mutation: - request: - operational_intent: - state: Activated - - invalid_accepted_offnominal: - delta: - source: valid_flight - mutation: - request: - operational_intent: - off_nominal_volumes: - - volume: - outline_polygon: - vertices: - - lat: 37.19330428406196 - lng: -80.59314014213832 - - lat: 37.1837232669677 - lng: -80.58471856692397 - - lat: 37.19735957232225 - lng: -80.56931360695863 - altitude_lower: - value: 474 - reference: W84 - units: M - altitude_upper: - value: 560 - reference: W84 - units: M - time_start: - value: '2023-01-01T00:05:00+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:20:00+00:00' - format: RFC3339 - - invalid_activated_offnominal: - delta: - source: invalid_accepted_offnominal - mutation: - request: - operational_intent: - state: Activated - - invalid_too_far_away: - delta: - source: valid_flight - mutation: - # reference time pulled back of 32 days > OiMaxPlanHorizon = 30 days - reference_time: '2022-11-30T00:00:00+00:00' - - valid_conflict_tiny_overlap: - delta: - source: valid_flight - mutation: - request: - operational_intent: - volumes: - - volume: - outline_polygon: - # polygon overlaps with polygon of valid_flight, distance between first vertex of each is 1.112cm - vertices: - - lat: 37.1837231669677 - lng: -80.58471856692397 - - lat: 37.176167 - lng: -80.593150 - - lat: 37.176663 - lng: -80.575597 diff --git a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/priority_preemption.yaml b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/priority_preemption.yaml deleted file mode 100644 index fd663fd895..0000000000 --- a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/priority_preemption.yaml +++ /dev/null @@ -1,238 +0,0 @@ -intents: - flight_1_planned_vol_A: - full: - reference_time: '2023-01-01T00:12:00+00:00' - request: - operational_intent: - volumes: - - volume: - outline_polygon: - vertices: - - lat: 37.19330428406196 - lng: -80.59314014213832 - - lat: 37.19026757574101 - lng: -80.59428502333573 - - lat: 37.18526788213324 - lng: -80.59005346211121 - - lat: 37.1837232669677 - lng: -80.58471856692397 - - lat: 37.18696946882951 - lng: -80.58072507989046 - - lat: 37.19117638063535 - lng: -80.57796221630451 - - lat: 37.19735957232225 - lng: -80.56931360695863 - - lat: 37.19892943598067 - lng: -80.57104154088566 - - lat: 37.19763628976819 - lng: -80.57674276197271 - - lat: 37.19465764212709 - lng: -80.58235632399915 - - lat: 37.19637327692024 - lng: -80.58569665094863 - altitude_lower: - value: 474 - reference: W84 - units: M - altitude_upper: - value: 560 - reference: W84 - units: M - time_start: - value: '2023-01-01T00:05:00+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:17:00+00:00' - format: RFC3339 - state: Accepted - off_nominal_volumes: [ ] - priority: 0 - # TODO: Remove flight_authorisation section when it is optional - flight_authorisation: - uas_serial_number: 1AF49UL5CC5J6K - operation_category: Open - operation_mode: Bvlos - uas_class: C0 - identification_technologies: [ 'N/A' ] - connectivity_methods: [ 'N/A' ] - endurance_minutes: 30 - emergency_procedure_url: https://example.uav.com/emergency - operator_id: CHEo5kut30e0mt01-qwe - uas_id: '' - uas_type_certificate: '' - - flight_1_activated_vol_A: - delta: - source: flight_1_planned_vol_A - mutation: - request: - operational_intent: - state: Activated - - flight_1_planned_vol_A_extended: - delta: - source: flight_1_planned_vol_A - mutation: - request: - operational_intent: - volumes: - - volume: - altitude_lower: - value: 425 - - flight_1_activated_vol_A_extended: - delta: - source: flight_1_planned_vol_A_extended - mutation: - request: - operational_intent: - state: Activated - - flight_1_planned_vol_B: - delta: - source: flight_1_planned_vol_A - mutation: - request: - operational_intent: - volumes: - - volume: - altitude_lower: - value: 602 - altitude_upper: - value: 750 - - flight_1_activated_vol_B: - delta: - source: flight_1_planned_vol_B - mutation: - request: - operational_intent: - state: Activated - - flight_2_planned_vol_A: - full: - reference_time: '2023-01-01T00:12:00+00:00' - request: - operational_intent: - volumes: - - volume: - outline_polygon: - vertices: - - lat: 37.19389846157564 - lng: -80.57843722762006 - - lat: 37.19542873284961 - lng: -80.57744077506473 - - lat: 37.19621141165609 - lng: -80.578531141435 - - lat: 37.19610765396546 - lng: -80.57910946528506 - - lat: 37.19626017503177 - lng: -80.57953226320937 - - lat: 37.19516471659203 - lng: -80.58067042878767 - altitude_lower: - value: 483 - reference: W84 - units: M - altitude_upper: - value: 519 - reference: W84 - units: M - time_start: - value: '2023-01-01T00:05:00+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:17:00+00:00' - format: RFC3339 - state: Accepted - off_nominal_volumes: [ ] - priority: 100 - # TODO: Remove flight_authorisation section when it is optional - flight_authorisation: - uas_serial_number: 1AF49UL5CC5J6K - operation_category: Open - operation_mode: Bvlos - uas_class: C0 - identification_technologies: [ 'N/A' ] - connectivity_methods: [ 'N/A' ] - endurance_minutes: 30 - emergency_procedure_url: https://example.uav.com/emergency - operator_id: CHEo5kut30e0mt01-qwe - uas_id: '' - uas_type_certificate: '' - - flight_2_activated_vol_A: - delta: - source: flight_2_planned_vol_A - mutation: - request: - operational_intent: - state: Activated - - flight_2_activated_vol_B: - delta: - source: flight_2_activated_vol_A - mutation: - request: - operational_intent: - volumes: - - volume: - altitude_lower: - value: 483 - altitude_upper: - value: 519 - - flight_2_equal_prio_planned_vol_B: - delta: - source: flight_2_activated_vol_B - mutation: - request: - operational_intent: - state: Accepted - priority: 0 - - flight_2_equal_prio_activated_vol_B: - delta: - source: flight_2_equal_prio_planned_vol_B - mutation: - request: - operational_intent: - state: Activated - - flight_2_equal_prio_nonconforming_vol_A: - delta: - source: flight_2_equal_prio_activated_vol_B - mutation: - request: - operational_intent: - state: Nonconforming - off_nominal_volumes: - - volume: - outline_polygon: - vertices: - - lat: 37.19389846157564 - lng: -80.57843722762006 - - lat: 37.19542873284961 - lng: -80.57744077506473 - - lat: 37.19621141165609 - lng: -80.578531141435 - - lat: 37.19610765396546 - lng: -80.57910946528506 - - lat: 37.19626017503177 - lng: -80.57953226320937 - - lat: 37.19516471659203 - lng: -80.58067042878767 - altitude_lower: - value: 483 - reference: W84 - units: M - altitude_upper: - value: 519 - reference: W84 - units: M - time_start: - value: '2023-01-01T00:05:00+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:17:00+00:00' - format: RFC3339 From 64b0c4a2682db0bf2ea05587b72621956cb313d3 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Tue, 24 Oct 2023 08:25:47 -0700 Subject: [PATCH 10/11] [mock_uss] Change scdsc database to use FlightInfo (#277) Change mock_uss scdsc database to use FlightInfo --- monitoring/mock_uss/scdsc/database.py | 7 ++- monitoring/mock_uss/scdsc/flight_planning.py | 6 +- monitoring/mock_uss/scdsc/routes_injection.py | 31 ++++++---- monitoring/mock_uss/scdsc/routes_scdsc.py | 10 ++-- .../clients/flight_planning/flight_info.py | 56 ++++++++++++++++++- 5 files changed, 86 insertions(+), 24 deletions(-) diff --git a/monitoring/mock_uss/scdsc/database.py b/monitoring/mock_uss/scdsc/database.py index e836dd858b..bd7f1e69f6 100644 --- a/monitoring/mock_uss/scdsc/database.py +++ b/monitoring/mock_uss/scdsc/database.py @@ -1,6 +1,7 @@ import json from typing import Dict, Optional +from monitoring.monitorlib.clients.flight_planning.flight_info import FlightInfo from monitoring.monitorlib.multiprocessing import SynchronizedValue from uas_standards.interuss.automated_testing.scd.v1 import api as scd_injection_api from implicitdict import ImplicitDict @@ -13,9 +14,9 @@ class FlightRecord(ImplicitDict): """Representation of a flight in a USS""" - op_intent_injection: scd_injection_api.OperationalIntentTestInjection - flight_authorisation: scd_injection_api.FlightAuthorisationData - op_intent_reference: OperationalIntentReference + flight_info: FlightInfo + op_intent: OperationalIntent + locked: bool = False diff --git a/monitoring/mock_uss/scdsc/flight_planning.py b/monitoring/mock_uss/scdsc/flight_planning.py index 1d69c1e2c0..d0c9457fa9 100644 --- a/monitoring/mock_uss/scdsc/flight_planning.py +++ b/monitoring/mock_uss/scdsc/flight_planning.py @@ -115,7 +115,7 @@ def check_for_disallowed_conflicts( for op_intent in op_intents: if ( existing_flight - and existing_flight.op_intent_reference.id == op_intent.reference.id + and existing_flight.op_intent.reference.id == op_intent.reference.id ): log( f"intersection with {op_intent.reference.id} not considered: intersection with a past version of this flight" @@ -143,14 +143,14 @@ def check_for_disallowed_conflicts( modifying_activated = ( existing_flight - and existing_flight.op_intent_reference.state + and existing_flight.op_intent.reference.state == scd_api.OperationalIntentState.Activated and req_body.operational_intent.state == scd_api.OperationalIntentState.Activated ) if modifying_activated: preexisting_conflict = Volume4DCollection.from_interuss_scd_api( - existing_flight.op_intent_injection.volumes + existing_flight.op_intent.details.volumes ).intersects_vol4s(v2) if preexisting_conflict: log( diff --git a/monitoring/mock_uss/scdsc/routes_injection.py b/monitoring/mock_uss/scdsc/routes_injection.py index 9f9086df4a..712fa46398 100644 --- a/monitoring/mock_uss/scdsc/routes_injection.py +++ b/monitoring/mock_uss/scdsc/routes_injection.py @@ -10,12 +10,14 @@ from loguru import logger import requests.exceptions +from monitoring.monitorlib.clients.flight_planning.flight_info import FlightInfo from uas_standards.astm.f3548.v21 import api from uas_standards.astm.f3548.v21.api import ( OperationalIntent, PutOperationalIntentDetailsParameters, ImplicitSubscriptionParameters, PutOperationalIntentReferenceParameters, + OperationalIntentDetails, ) from uas_standards.interuss.automated_testing.scd.v1.api import ( InjectFlightRequest, @@ -79,7 +81,7 @@ def query_operational_intents( ) tx = db.value get_details_for = [] - own_flights = {f.op_intent_reference.id: f for f in tx.flights.values() if f} + own_flights = {f.op_intent.reference.id: f for f in tx.flights.values() if f} result = [] for op_intent_ref in op_intent_refs: if op_intent_ref.id in own_flights: @@ -214,7 +216,7 @@ def log(msg: str): try: # Check the transition is valid state_transition_from = ( - OperationalIntentState(existing_flight.op_intent_reference.state) + OperationalIntentState(existing_flight.op_intent.reference.state) if existing_flight else None ) @@ -280,13 +282,13 @@ def log(msg: str): new_subscription=ImplicitSubscriptionParameters(uss_base_url=base_url), ) if existing_flight: - id = existing_flight.op_intent_reference.id + id = existing_flight.op_intent.reference.id step_name = f"updating existing operational intent {id} in DSS" log(step_name) result = scd_client.update_operational_intent_reference( utm_client, id, - existing_flight.op_intent_reference.ovn, + existing_flight.op_intent.reference.ovn, req, ) else: @@ -320,10 +322,17 @@ def log(msg: str): # Store flight in database step_name = "storing flight in database" log("Storing flight in database") + op_intent = OperationalIntent( + reference=result.operational_intent_reference, + details=OperationalIntentDetails( + volumes=req_body.operational_intent.volumes, + off_nominal_volumes=req_body.operational_intent.off_nominal_volumes, + priority=req_body.operational_intent.priority, + ), + ) record = database.FlightRecord( - op_intent_reference=result.operational_intent_reference, - op_intent_injection=req_body.operational_intent, - flight_authorisation=req_body.flight_authorisation, + op_intent=op_intent, + flight_info=FlightInfo.from_scd_inject_flight_request(req_body), ) with db as tx: tx.flights[flight_id] = record @@ -422,12 +431,12 @@ def delete_flight(flight_id) -> Tuple[dict, int]: # Delete operational intent from DSS step_name = "performing unknown operation" try: - step_name = f"deleting operational intent {flight.op_intent_reference.id} with OVN {flight.op_intent_reference.ovn} from DSS" + step_name = f"deleting operational intent {flight.op_intent.reference.id} with OVN {flight.op_intent.reference.ovn} from DSS" logger.debug(f"[delete_flight/{pid}:{flight_id}] {step_name}") result = scd_client.delete_operational_intent_reference( utm_client, - flight.op_intent_reference.id, - flight.op_intent_reference.ovn, + flight.op_intent.reference.id, + flight.op_intent.reference.ovn, ) step_name = "notifying subscribers" @@ -558,7 +567,7 @@ def make_result(success: bool, msg: str) -> ClearAreaResponse: if record is None or record.locked: pending_flights.add(flight_id) continue - if record.op_intent_reference.id in deleted: + if record.op_intent.reference.id in deleted: flights_to_delete.append(flight_id) for flight_id in flights_to_delete: del tx.flights[flight_id] diff --git a/monitoring/mock_uss/scdsc/routes_scdsc.py b/monitoring/mock_uss/scdsc/routes_scdsc.py index cd955fd103..bb7fd61249 100644 --- a/monitoring/mock_uss/scdsc/routes_scdsc.py +++ b/monitoring/mock_uss/scdsc/routes_scdsc.py @@ -21,7 +21,7 @@ def scdsc_get_operational_intent_details(entityid: str): tx = db.value flight = None for f in tx.flights.values(): - if f.op_intent_reference.id == entityid: + if f.op_intent.reference.id == entityid: flight = f break @@ -47,11 +47,11 @@ def scdsc_get_operational_intent_details(entityid: str): def op_intent_from_flightrecord(flight: FlightRecord) -> OperationalIntent: return OperationalIntent( - reference=flight.op_intent_reference, + reference=flight.op_intent.reference, details=OperationalIntentDetails( - volumes=flight.op_intent_injection.volumes, - off_nominal_volumes=flight.op_intent_injection.off_nominal_volumes, - priority=flight.op_intent_injection.priority, + volumes=flight.op_intent.details.volumes, + off_nominal_volumes=flight.op_intent.details.off_nominal_volumes, + priority=flight.op_intent.details.priority, ), ) diff --git a/monitoring/monitorlib/clients/flight_planning/flight_info.py b/monitoring/monitorlib/clients/flight_planning/flight_info.py index 2dfb838bc0..f780aadd5b 100644 --- a/monitoring/monitorlib/clients/flight_planning/flight_info.py +++ b/monitoring/monitorlib/clients/flight_planning/flight_info.py @@ -1,12 +1,13 @@ +from __future__ import annotations from enum import Enum from typing import Optional, List from implicitdict import ImplicitDict from uas_standards.ansi_cta_2063_a import SerialNumber from uas_standards.en4709_02 import OperatorRegistrationNumber +from uas_standards.interuss.automated_testing.scd.v1 import api as scd_api -from monitoring.monitorlib.geotemporal import Volume4D - +from monitoring.monitorlib.geotemporal import Volume4D, Volume4DCollection # ===== ASTM F3548-21 ===== @@ -224,6 +225,57 @@ class FlightInfo(ImplicitDict): additional_information: Optional[dict] """Any information relevant to a particular jurisdiction or use case not described in the standard schema. The keys and values must be agreed upon between the test designers and USSs under test.""" + @staticmethod + def from_scd_inject_flight_request( + request: scd_api.InjectFlightRequest, + ) -> FlightInfo: + usage_states = { + scd_api.OperationalIntentState.Accepted: AirspaceUsageState.Planned, + scd_api.OperationalIntentState.Activated: AirspaceUsageState.InUse, + scd_api.OperationalIntentState.Nonconforming: AirspaceUsageState.InUse, + scd_api.OperationalIntentState.Contingent: AirspaceUsageState.InUse, + } + uas_states = { + scd_api.OperationalIntentState.Accepted: UasState.Nominal, + scd_api.OperationalIntentState.Activated: UasState.Nominal, + scd_api.OperationalIntentState.Nonconforming: UasState.OffNominal, + scd_api.OperationalIntentState.Contingent: UasState.Contingent, + } + if ( + request.operational_intent.state + in ( + scd_api.OperationalIntentState.Accepted, + scd_api.OperationalIntentState.Activated, + ) + and request.operational_intent.off_nominal_volumes + ): + # This invalid request can no longer be represented with a standard flight planning request + raise ValueError( + f"Request for nominal {request.operational_intent.state} operational intent is invalid because it contains off-nominal volumes" + ) + v4c = Volume4DCollection.from_interuss_scd_api( + request.operational_intent.volumes + ) + Volume4DCollection.from_interuss_scd_api( + request.operational_intent.off_nominal_volumes + ) + basic_information = BasicFlightPlanInformation( + usage_state=usage_states[request.operational_intent.state], + uas_state=uas_states[request.operational_intent.state], + area=v4c.volumes, + ) + astm_f3548v21 = ASTMF354821OpIntentInformation( + priority=request.operational_intent.priority + ) + uspace_flight_authorisation = ImplicitDict.parse( + request.flight_authorisation, FlightAuthorisationData + ) + flight_info = FlightInfo( + basic_information=basic_information, + astm_f3548_21=astm_f3548v21, + uspace_flight_authorisation=uspace_flight_authorisation, + ) + return flight_info + class ExecutionStyle(str, Enum): Hypothetical = "Hypothetical" From 5a685db2bef1f32e36edaff9c819a541e49abc1f Mon Sep 17 00:00:00 2001 From: Punam Verma Date: Tue, 24 Oct 2023 10:25:41 -0700 Subject: [PATCH 11/11] [monitorlib] Retrieve interactions from mock_uss (#270) * Recording interuss interactions from mock_uss * Fixed format * Fixing format * Removing import of non-exisitng module * Removing a file that should go in with scenario PR * Removing interaction recording in test steps as per PR review --- .../mock_uss/interaction_logging/logger.py | 2 +- .../routes_interactions_log.py | 24 +++++------ monitoring/uss_qualifier/reports/report.py | 2 + .../resources/interuss/mock_uss/client.py | 42 +++++++++++++++++++ .../scenarios/astm/utm/interuss/__init__.py | 1 + .../uss_qualifier/scenarios/scenario.py | 1 - 6 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 monitoring/uss_qualifier/scenarios/astm/utm/interuss/__init__.py diff --git a/monitoring/mock_uss/interaction_logging/logger.py b/monitoring/mock_uss/interaction_logging/logger.py index 106b4a2399..78f090541f 100644 --- a/monitoring/mock_uss/interaction_logging/logger.py +++ b/monitoring/mock_uss/interaction_logging/logger.py @@ -66,7 +66,7 @@ def interaction_log_after_request(response): datetime.datetime.utcnow() - flask.current_app.custom_profiler["start"] ).total_seconds() # TODO: Make this configurable instead of hardcoding exactly these query types - if flask.request.url_rule is not None and "/uss/v1/" in flask.request.url_rule.rule: + if "/uss/v1/" in flask.request.url: query = describe_flask_query(flask.request, response, elapsed_s) log_interaction(QueryDirection.Incoming, query) return response diff --git a/monitoring/mock_uss/interaction_logging/routes_interactions_log.py b/monitoring/mock_uss/interaction_logging/routes_interactions_log.py index 9a0e6fc6ec..739858fa50 100644 --- a/monitoring/mock_uss/interaction_logging/routes_interactions_log.py +++ b/monitoring/mock_uss/interaction_logging/routes_interactions_log.py @@ -39,18 +39,18 @@ def interaction_logs() -> Tuple[str, int]: try: obj = json.load(f) interaction = ImplicitDict.parse(obj, Interaction) - if ( - ("received_at" in interaction.query.request) - and interaction.query.request.received_at.datetime - >= from_time.datetime - ): - interactions.append(interaction) - elif ( - "initiated_at" in interaction.query.request - and interaction.query.request.initiated_at.datetime - >= from_time.datetime - ): - interactions.append(interaction) + if "received_at" in interaction.query.request: + if ( + interaction.query.request.received_at.datetime + >= from_time.datetime + ): + interactions.append(interaction) + elif "initiated_at" in interaction.query.request: + if ( + interaction.query.request.initiated_at.datetime + >= from_time.datetime + ): + interactions.append(interaction) else: raise ValueError( f"There is no received_at or initiated_at field in the request in {fname}" diff --git a/monitoring/uss_qualifier/reports/report.py b/monitoring/uss_qualifier/reports/report.py index 562f3f6d6f..b9e6ad8ed6 100644 --- a/monitoring/uss_qualifier/reports/report.py +++ b/monitoring/uss_qualifier/reports/report.py @@ -22,6 +22,8 @@ from monitoring.uss_qualifier.scenarios.definitions import TestScenarioTypeName from monitoring.uss_qualifier.suites.definitions import TestSuiteActionDeclaration +from monitoring.mock_uss.interaction_logging.interactions import Interaction + class FailedCheck(ImplicitDict): name: str diff --git a/monitoring/uss_qualifier/resources/interuss/mock_uss/client.py b/monitoring/uss_qualifier/resources/interuss/mock_uss/client.py index 2c6fd7f534..53c7a104e7 100644 --- a/monitoring/uss_qualifier/resources/interuss/mock_uss/client.py +++ b/monitoring/uss_qualifier/resources/interuss/mock_uss/client.py @@ -1,5 +1,6 @@ from typing import Tuple, Optional, List +from loguru import logger from implicitdict import ImplicitDict from monitoring.monitorlib import fetch @@ -7,6 +8,7 @@ GetLocalityResponse, PutLocalityRequest, ) +from monitoring.monitorlib.fetch import QueryError, Query from monitoring.monitorlib.infrastructure import AuthAdapter, UTMClientSession from monitoring.monitorlib.locality import LocalityCode from monitoring.monitorlib.scd_automated_testing.scd_injection_api import ( @@ -15,6 +17,12 @@ from monitoring.uss_qualifier.reports.report import ParticipantID from monitoring.uss_qualifier.resources.communications import AuthAdapterResource from monitoring.uss_qualifier.resources.resource import Resource +from monitoring.mock_uss.interaction_logging.interactions import ( + Interaction, + ListLogsResponse, +) +from typing import Tuple, List +from implicitdict import StringBasedDateTime MOCK_USS_CONFIG_SCOPE = "interuss.mock_uss.configure" @@ -29,6 +37,7 @@ def __init__( base_url: str, auth_adapter: AuthAdapter, ): + self.base_url = base_url self.session = UTMClientSession(base_url, auth_adapter) self.participant_id = participant_id @@ -68,6 +77,39 @@ def set_locality(self, locality_code: LocalityCode) -> fetch.Query: # TODO: Add other methods to interact with the mock USS in other ways (like starting/stopping message signing data collection) + def get_interactions(self, from_time: StringBasedDateTime) -> List[Interaction]: + """ + Requesting interuss interactions from mock_uss from a given time till now + Args: + from_time: the time from which the interactions are requested + Returns: + List of Interactions + """ + url = "{}/mock_uss/interuss_logging/logs?from_time={}".format( + self.base_url, from_time + ) + logger.debug(f"Getting interactions from {from_time} : {url}") + query = fetch.query_and_describe( + self.session, "GET", url, scope=SCOPE_SCD_QUALIFIER_INJECT + ) + if query.status_code != 200: + raise QueryError( + f"Request to mock uss {url} returned a {query.status_code} ", [query] + ) + try: + response = ImplicitDict.parse(query.response.get("json"), ListLogsResponse) + except KeyError: + raise QueryError( + msg=f"RecordedInteractionsResponse from mock_uss response did not contain JSON body", + queries=[query], + ) + except ValueError as e: + raise QueryError( + msg=f"RecordedInteractionsResponse from mock_uss response contained invalid JSON: {str(e)}", + queries=[query], + ) + return response.interactions + class MockUSSSpecification(ImplicitDict): mock_uss_base_url: str diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/interuss/__init__.py b/monitoring/uss_qualifier/scenarios/astm/utm/interuss/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/monitoring/uss_qualifier/scenarios/astm/utm/interuss/__init__.py @@ -0,0 +1 @@ + diff --git a/monitoring/uss_qualifier/scenarios/scenario.py b/monitoring/uss_qualifier/scenarios/scenario.py index da1bbda238..30db6314d6 100644 --- a/monitoring/uss_qualifier/scenarios/scenario.py +++ b/monitoring/uss_qualifier/scenarios/scenario.py @@ -4,7 +4,6 @@ from enum import Enum import inspect from typing import Callable, Dict, List, Optional, TypeVar, Union, Set, Type - import arrow from implicitdict import StringBasedDateTime from loguru import logger

)4r`I~$ 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 literal 251187 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�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 H^!q;me7_~W diff --git a/assets/local_uss_qualifier.gv b/assets/local_uss_qualifier.gv index 66044486c8..653ca65c95 100644 --- a/assets/local_uss_qualifier.gv +++ b/assets/local_uss_qualifier.gv @@ -33,13 +33,6 @@ digraph G { label=make start-uss-mocks
make stop-uss-mocks> style="dashed" - atproxy [label=atproxy
host port 8075
Disabled>] - subgraph force_same_level { - rank=same - mock_uss_atproxy_client [label=mock_uss_atproxy_client>] - InteropEcosystem [color=lightblue,label=communicate with DSS +
auth in local interoperability
ecosystem, as well as each
other, all according to ASTM
standards>] - } - mock_uss_geoawareness [label=mock_uss_geoawareness
host port 8076>] mock_uss_scdsc_a [label=mock_uss_scdsc_a
host port 8074>] @@ -53,8 +46,6 @@ digraph G { mock_uss_tracer [label=mock_uss_tracer
host port 8078>] mock_uss_tracer_v19 [label=mock_uss_tracer_v19
host port 8088>] - - atproxy -> mock_uss_atproxy_client } subgraph cluster_uss_qualifier { @@ -94,7 +85,6 @@ digraph G { mock_uss_scdsc_b -> InteropEcosystem mock_uss_tracer -> InteropEcosystem mock_uss_tracer_v19 -> InteropEcosystem - mock_uss_atproxy_client -> InteropEcosystem ResourceUSSAuth -> Auth ResourceTestDirectorAuth -> Auth diff --git a/monitoring/README.md b/monitoring/README.md index 86004ae8ed..fab18f56e7 100644 --- a/monitoring/README.md +++ b/monitoring/README.md @@ -16,14 +16,6 @@ observed. It is intended to be run in a production-like shared test environment to verify the interoperability of all participants' systems before promoting any system changes to production. -### atproxy - -[atproxy](atproxy) exposes automated testing endpoints that can be implemented -by calling other exposed handler endpoints. This means that the automated -testing implementation of a USS can make only outgoing calls to atproxy, which -can be hosted in an entirely differently location than the automated testing -implementation. - ### load_test The [DSS load test](loadtest) sends a large number of concurrent requests to a diff --git a/monitoring/atproxy/README.md b/monitoring/atproxy/README.md deleted file mode 100644 index 1adbef93a1..0000000000 --- a/monitoring/atproxy/README.md +++ /dev/null @@ -1,66 +0,0 @@ -`atproxy` ("automated testing proxy") contains a proxy server to host automated -testing endpoints whose implementations are fulfilled by a client in a separate -environment. - -## Motivation - -For some USSs, exposing additional endpoints that interact with the internals of -their system can be a difficult and/or time-consuming process. Since -uss_qualifier requires USSs under test to implement additional endpoints in -order to be tested, this means implementing automated testing can be difficult -for that reason. atproxy attempts to reduce this difficulty by providing a -webserver implementing all USS endpoints required by uss_qualifier. Whenever a -request arrives to one of these endpoints, the request is placed in a queue -which is accessible through handler endpoints. Pending requests can be listed -by polling the appropriate handler endpoint, and then handled by writing to a -different handler endpoint. The intended sequence of events to handle a -uss_qualifier call to GET /observation/display_data for RID testing is shown -below. - -![uss_qualifer flow with atproxy](../../assets/generated/atproxy_sequence.png) - -In this way, the "Internal automated testing client" (the USS's implementation -of required automated testing functionality) can be written using only outgoing -HTTP calls, and so is likely to be easier to integrate into the internal -environment. atproxy can be hosted anywhere by the USS, including outside its -core systems. - -## Usage - -### Overview - -atproxy must be hosted somewhere both uss_qualifier and the internal automated -testing client can access its endpoints. This generally means it must be -exposed to the general Internet, but it does not need access to the USS's -internal systems. - -Once atproxy is hosted, a USS must implement their own internal automated -testing client. This client should poll atproxy for incoming requests, handle a -request internally, and then send the result back to atproxy (which will then -forward that result back to uss_qualifier). - -### Setup - -[`run_locally.sh`](run_locally.sh) provides a demonstration of the server in the -user's local environment. The following parameters must be set appropriately -when running in a real environment: - -* ATPROXY_PUBLIC_KEY: Public key to validate access tokens used to access automated testing endpoints, or a JWKS URL at which the public key is located. -* ATPROXY_TOKEN_AUDIENCE: Domain name (or comma-separated domain names) of the audience expected in access tokens used to access automated testing endpoints. -* ATPROXY_CLIENT_BASIC_AUTH: Username and password (in the form `username:password`) of HTTP Basic authentication used to access handler endpoints. - -To access the handler endpoints (see [routes_handler.py](routes_handler.py)), -HTTP Basic authentication matching ATPROXY_CLIENT_BASIC_AUTH must be provided. - -### Internal automated testing client - -A USS's internal automated testing client should poll GET /handler/queries for -incoming queries. When a query is found, it should be handled and then the -response written back to PUT /handler/queries/{query_id}. - -The response format of GET /handler/queries is defined by -[ListQueriesResponse](routes_handler.py), and the format of the `request` field -depends on what automated testing endpoint is being queried (per `type` field). -The JSON request body of PUT /handler/queries/{query_id} should follow the -[PutQueryRequest](routes_handler.py) generally, and the format of the `response` -field should correspond to the request type. diff --git a/monitoring/atproxy/__init__.py b/monitoring/atproxy/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/monitoring/atproxy/app/__init__.py b/monitoring/atproxy/app/__init__.py deleted file mode 100644 index 4e84566368..0000000000 --- a/monitoring/atproxy/app/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -import flask -from flask_httpauth import HTTPBasicAuth - -from monitoring.atproxy import config - -webapp = flask.Flask(__name__) -basic_auth = HTTPBasicAuth() - -webapp.config.from_object(config.Config) -users = config.get_users(webapp.config[config.KEY_CLIENT_BASIC_AUTH]) - -from monitoring.atproxy import routes diff --git a/monitoring/atproxy/atproxy.postman_collection.json b/monitoring/atproxy/atproxy.postman_collection.json deleted file mode 100644 index 3dad77672b..0000000000 --- a/monitoring/atproxy/atproxy.postman_collection.json +++ /dev/null @@ -1,1071 +0,0 @@ -{ - "info": { - "_postman_id": "2cfe8504-8f49-4f63-b259-1cffc467abde", - "name": "atproxy", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Authorization", - "item": [ - { - "name": "Get RID DP token from dummy OAuth", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var data = JSON.parse(responseBody);", - "postman.setEnvironmentVariable(\"rid_dp_access_token\", data.access_token);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8085/token?grant_type=client_credentials&scope=rid.display_provider&intended_audience=localhost&issuer=localhost", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8085", - "path": [ - "token" - ], - "query": [ - { - "key": "grant_type", - "value": "client_credentials" - }, - { - "key": "scope", - "value": "rid.display_provider" - }, - { - "key": "intended_audience", - "value": "localhost" - }, - { - "key": "issuer", - "value": "localhost" - } - ] - } - }, - "response": [] - }, - { - "name": "Get RID injection token from dummy OAuth", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var data = JSON.parse(responseBody);", - "postman.setEnvironmentVariable(\"rid_inject_access_token\", data.access_token);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8085/token?grant_type=client_credentials&scope=rid.inject_test_data&intended_audience=localhost&issuer=localhost", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8085", - "path": [ - "token" - ], - "query": [ - { - "key": "grant_type", - "value": "client_credentials" - }, - { - "key": "scope", - "value": "rid.inject_test_data" - }, - { - "key": "intended_audience", - "value": "localhost" - }, - { - "key": "issuer", - "value": "localhost" - } - ] - } - }, - "response": [] - }, - { - "name": "Get SCD injection token from dummy OAuth", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var data = JSON.parse(responseBody);", - "postman.setEnvironmentVariable(\"scd_inject_access_token\", data.access_token);", - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8085/token?grant_type=client_credentials&scope=utm.inject_test_data&intended_audience=localhost&issuer=localhost", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8085", - "path": [ - "token" - ], - "query": [ - { - "key": "grant_type", - "value": "client_credentials" - }, - { - "key": "scope", - "value": "utm.inject_test_data" - }, - { - "key": "intended_audience", - "value": "localhost" - }, - { - "key": "issuer", - "value": "localhost" - } - ] - } - }, - "response": [] - } - ] - }, - { - "name": "RID Observation", - "item": [ - { - "name": "Observation: display_data", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{rid_dp_access_token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8075/riddp/observation/display_data?view=asdf", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "riddp", - "observation", - "display_data" - ], - "query": [ - { - "key": "view", - "value": "asdf" - } - ] - } - }, - "response": [] - }, - { - "name": "Observation: flight_details", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{rid_dp_access_token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8075/riddp/observation/display_data/asdf", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "riddp", - "observation", - "display_data", - "asdf" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "RID Injection", - "item": [ - { - "name": "Create test", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{rid_inject_access_token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"requested_flights\":[]}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/ridsp/injection/tests/702255ac-22b5-4be9-ba10-c68411724280", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "ridsp", - "injection", - "tests", - "702255ac-22b5-4be9-ba10-c68411724280" - ] - } - }, - "response": [] - }, - { - "name": "Delete test", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{rid_inject_access_token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "url": { - "raw": "http://localhost:8075/ridsp/injection/tests/702255ac-22b5-4be9-ba10-c68411724280/asdf", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "ridsp", - "injection", - "tests", - "702255ac-22b5-4be9-ba10-c68411724280", - "asdf" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "SCD Injection", - "item": [ - { - "name": "Status", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{scd_inject_access_token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8075/scd/v1/status", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "scd", - "v1", - "status" - ] - } - }, - "response": [] - }, - { - "name": "Capabilities", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{scd_inject_access_token}}", - "type": "string" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8075/scd/v1/capabilities", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "scd", - "v1", - "capabilities" - ] - } - }, - "response": [] - }, - { - "name": "Flights", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{scd_inject_access_token}}", - "type": "string" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"operational_intent\": {\n \"state\": \"Accepted\",\n \"priority\": 0,\n \"volumes\": [],\n \"off_nominal_volumes\": []\n },\n \"flight_authorisation\": {\n \"uas_serial_number\": \"\",\n \"operation_mode\": \"Undeclared\",\n \"uas_class\": \"Other\",\n \"identification_technologies\": [],\n \"operator_id\": \"\",\n \"operation_category\": \"Unknown\",\n \"connectivity_methods\": [],\n \"endurance_minutes\": 0,\n \"emergency_procedure_url\": \"\"\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/scd/v1/flights/c320b2ee-d944-418b-baad-dcdfb61ef0d6", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "scd", - "v1", - "flights", - "c320b2ee-d944-418b-baad-dcdfb61ef0d6" - ] - } - }, - "response": [] - }, - { - "name": "Flights", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{scd_inject_access_token}}", - "type": "string" - } - ] - }, - "method": "DELETE", - "header": [], - "url": { - "raw": "http://localhost:8075/scd/v1/flights/c320b2ee-d944-418b-baad-dcdfb61ef0d6", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "scd", - "v1", - "flights", - "c320b2ee-d944-418b-baad-dcdfb61ef0d6" - ] - } - }, - "response": [] - }, - { - "name": "Clear area", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{scd_inject_access_token}}", - "type": "string" - } - ] - }, - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"request_id\": \"f49191cb-202d-4555-8bb3-4da25b1b17f7\",\n \"extent\": {\n \"volume\": {}\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/scd/v1/clear_area_requests", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "scd", - "v1", - "clear_area_requests" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Handler", - "item": [ - { - "name": "RID Observation", - "item": [ - { - "name": "Put display_data response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{\"flights\":[],\"clusters\":[]}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Put flight_details response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Put flight_details response (404)", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":404,\"response\":{\"message\":\"Requested flight ID not found\"}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "RID Injection", - "item": [ - { - "name": "Create test response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{\"injected_flights\":[],\"version\":\"asdf\"}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Delete test response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{\"injected_flights\":[]}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "SCD Injection", - "item": [ - { - "name": "Status response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{\"status\":\"Ready\"}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Capabilities response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{\"capabilities\":[\"FlightAuthorisationValidation\",\"BasicStrategicConflictDetection\",\"HighPriorityFlights\"]}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - }, - { - "name": "(PUT) Flights response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{\"response_example_not_complete\": true}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - }, - { - "name": "(DELETE) Flights response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{\"response_example_not_complete\": true}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Clear area response", - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\"return_code\":200,\"response\":{\"outcome\":{\"timestamp\":\"2022-12-12T00:00.00Z\",\"success\":true}}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8075/handler/queries/{{handler_request_id}}", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries", - "{{handler_request_id}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "List queries", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var data = JSON.parse(responseBody);", - "if (data.requests.length > 0) {", - " postman.setEnvironmentVariable(\"handler_request_id\", data.requests[0].id);", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "basic", - "basic": [ - { - "key": "password", - "value": "local_client", - "type": "string" - }, - { - "key": "username", - "value": "local_client", - "type": "string" - }, - { - "key": "showPassword", - "value": false, - "type": "boolean" - } - ] - }, - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8075/handler/queries", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8075", - "path": [ - "handler", - "queries" - ] - } - }, - "response": [] - } - ] - } - ] -} \ No newline at end of file diff --git a/monitoring/atproxy/config.py b/monitoring/atproxy/config.py deleted file mode 100644 index a491194883..0000000000 --- a/monitoring/atproxy/config.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -from typing import Dict - -from werkzeug.security import generate_password_hash - -from monitoring.monitorlib import auth_validation - -ENV_KEY_PREFIX = "ATPROXY" -ENV_KEY_PUBLIC_KEY = "{}_PUBLIC_KEY".format(ENV_KEY_PREFIX) -ENV_KEY_TOKEN_AUDIENCE = "{}_TOKEN_AUDIENCE".format(ENV_KEY_PREFIX) -ENV_KEY_CLIENT_BASIC_AUTH = "{}_CLIENT_BASIC_AUTH".format(ENV_KEY_PREFIX) -ENV_KEY_QUERY_TIMEOUT = "{}_QUERY_TIMEOUT".format(ENV_KEY_PREFIX) - -# These keys map to entries in the Config class -KEY_TOKEN_PUBLIC_KEY = "TOKEN_PUBLIC_KEY" -KEY_TOKEN_AUDIENCE = "TOKEN_AUDIENCE" -KEY_CLIENT_BASIC_AUTH = "CLIENT_BASIC_AUTH" -KEY_QUERY_TIMEOUT = "QUERY_TIMEOUT" - -KEY_CODE_VERSION = "MONITORING_VERSION" - -workspace_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "workspace") - - -class Config(object): - TOKEN_PUBLIC_KEY = auth_validation.fix_key( - os.environ.get(ENV_KEY_PUBLIC_KEY, "") - ).encode("utf-8") - TOKEN_AUDIENCE = os.environ.get(ENV_KEY_TOKEN_AUDIENCE, "") - CLIENT_BASIC_AUTH = os.environ[ENV_KEY_CLIENT_BASIC_AUTH] - QUERY_TIMEOUT = float(os.environ.get(ENV_KEY_QUERY_TIMEOUT, "59")) - CODE_VERSION = os.environ.get(KEY_CODE_VERSION, "Unknown") - - -def get_users(basic_auth: str) -> Dict[str, str]: - user_pass = [v.strip() for v in basic_auth.split(":")] - if len(user_pass) != 2: - raise ValueError('Expected "username:password", got "{}"'.format(basic_auth)) - return {user_pass[0]: generate_password_hash(user_pass[1])} diff --git a/monitoring/atproxy/database.py b/monitoring/atproxy/database.py deleted file mode 100644 index ef3e074964..0000000000 --- a/monitoring/atproxy/database.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import annotations -import enum -import json -from typing import Dict, Optional - -from monitoring.monitorlib.multiprocessing import SynchronizedValue -from implicitdict import ImplicitDict - - -# --- All queries --- -class QueryState(str, enum.Enum): - """Whether a query is being handled, or has already been handled.""" - - Queued = "Queued" - BeingHandled = "BeingHandled" - Complete = "Complete" - - -class Query(ImplicitDict): - """An incoming call to an automated testing endpoint.""" - - type: str - """The endpoint being queried. - - Uses the form .., and is populated using the - appropriate request's request_type_name() method. - """ - - request: dict - """Information about the query request. - - Schema corresponds to the *Request object in requests.py whose - request_type_name() matches `type`. - """ - - state: QueryState = QueryState.Queued - - return_code: Optional[int] = None - """For a Query in the Complete `state`, the HTTP return code to return.""" - - response: Optional[dict] = None - """For a Query in the Complete `state`, the JSON response body to return.""" - - -# --- Cross-process synchronization --- -class Database(ImplicitDict): - """The body of data shared and synchronized across handler processes.""" - - queries: Dict[str, Query] = {} - """Mapping between query ID and query content.""" - - -db = SynchronizedValue( - Database(), - decoder=lambda b: ImplicitDict.parse(json.loads(b.decode("utf-8")), Database), -) diff --git a/monitoring/atproxy/gunicorn.conf.py b/monitoring/atproxy/gunicorn.conf.py deleted file mode 100644 index 9f34160706..0000000000 --- a/monitoring/atproxy/gunicorn.conf.py +++ /dev/null @@ -1,36 +0,0 @@ -import os - -from gunicorn.http import Request -from gunicorn.http.wsgi import Response -from gunicorn.workers.base import Worker -from loguru import logger - - -def pre_request(worker: Worker, req: Request): - """gunicorn server hook called just before a worker processes the request.""" - logger.debug( - "gunicorn pre_request from worker {} (OS PID {}): {} {}", - worker.pid, - os.getpid(), - req.method, - req.path, - ) - - -def post_request(worker: Worker, req: Request, environ: dict, resp: Response): - """gunicorn server hook called after a worker processes the request.""" - logger.debug( - "gunicorn post_request from worker {} (OS PID {}): {} {} -> {}", - worker.pid, - os.getpid(), - req.method, - req.path, - resp.status_code, - ) - - -def worker_abort(worker: Worker): - """gunicorn server hook called when a worker received the SIGABRT signal.""" - logger.debug( - "gunicorn worker_abort from worker {} (OS PID {})", worker.pid, os.getpid() - ) diff --git a/monitoring/atproxy/handling.py b/monitoring/atproxy/handling.py deleted file mode 100644 index 1ec291a4ff..0000000000 --- a/monitoring/atproxy/handling.py +++ /dev/null @@ -1,97 +0,0 @@ -import os -from datetime import datetime, timedelta -import time -from typing import Tuple, List, Optional -import uuid - -import flask -from loguru import logger - -from implicitdict import ImplicitDict -from .database import db, Query, QueryState - - -class PendingRequest(ImplicitDict): - """A pending query the handler client is expected to handle.""" - - id: str - """ID of the query; used to PUT /handler/queries/ the result.""" - - type: str - """Type of query -- matches a request_type_name() in requests.py.""" - - request: dict - """All relevant information about the request in the *Request descriptor from requests.py matching `type`.""" - - -class ListQueriesResponse(ImplicitDict): - """Response body schema for GET /handler/queries.""" - - requests: List[PendingRequest] - """All of the queries available for the handler client to handle.""" - - -class PutQueryRequest(ImplicitDict): - """Request body schema for PUT /handler/queries/.""" - - response: Optional[dict] = None - """JSON body of the response, or None for no JSON body.""" - - return_code: int - """HTTP return code.""" - - -def fulfill_query(req: ImplicitDict, timeout: timedelta) -> Tuple[str, int]: - """Fulfill an incoming automated testing query. - - :param req: Request descriptor from requests.py. - :param timeout: Maximum amount of time client has to fulfill query. - :return: Flask endpoint handler result (content, HTTP code). - """ - t_start = datetime.utcnow() - query = Query(type=req.request_type_name(), request=req) - id = str(uuid.uuid4()) - logger.debug( - "Attempting to fulfill {} query {} from worker {}", query.type, id, os.getpid() - ) - - # Add query to be handled to the set of handleable queries - with db as tx: - tx.queries[id] = query - logger.debug("Added {} query {} to handler queue".format(query.type, id)) - - # Frequently check if the query has been fulfilled - while datetime.utcnow() < t_start + timeout: - time.sleep(0.1) - with db as tx: - if tx.queries[id].state == QueryState.Complete: - # Query was successfully fulfilled; return the result - logger.debug("Fulfilling {} query {}".format(query.type, id)) - query = tx.queries.pop(id) - logger.debug( - "Fulfilled {} query {} with {} from worker {}", - query.type, - id, - query.return_code, - os.getpid(), - ) - if query.response is not None: - return flask.jsonify(query.response), query.return_code - else: - return "", query.return_code - - # Time expired; remove request from queue and indicate error - with db as tx: - tx.queries.pop(id) - logger.debug( - "Failed to fulfill {} query {} in time (backend handler did not provide a response) from worker {}", - query.type, - id, - os.getpid(), - ) - return ( - flask.jsonify( - {"message": "Backend handler did not respond within the alotted time"} - ), - 500, - ) diff --git a/monitoring/atproxy/health_check.sh b/monitoring/atproxy/health_check.sh deleted file mode 100755 index 5b33322929..0000000000 --- a/monitoring/atproxy/health_check.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh - -# This script is intended to be called from within a Docker container running -# atproxy via the interuss/monitoring image to determine the health status of -# the container. - -curl --fail http://localhost:5000/ || exit 1 diff --git a/monitoring/atproxy/oauth.py b/monitoring/atproxy/oauth.py deleted file mode 100644 index 5b97abefd1..0000000000 --- a/monitoring/atproxy/oauth.py +++ /dev/null @@ -1,9 +0,0 @@ -from monitoring.monitorlib import auth_validation -from . import config -from .app import webapp - - -requires_scope = auth_validation.requires_scope_decorator( - webapp.config.get(config.KEY_TOKEN_PUBLIC_KEY), - webapp.config.get(config.KEY_TOKEN_AUDIENCE), -) diff --git a/monitoring/atproxy/requests.py b/monitoring/atproxy/requests.py deleted file mode 100644 index 08cc2263be..0000000000 --- a/monitoring/atproxy/requests.py +++ /dev/null @@ -1,108 +0,0 @@ -from enum import Enum - -from monitoring.monitorlib.rid_automated_testing import injection_api -from implicitdict import ImplicitDict -from uas_standards.interuss.automated_testing.scd.v1.api import ( - ClearAreaRequest, - InjectFlightRequest, -) - - -class RequestType(str, Enum): - RID_Observation_GetDisplayData = "rid.observation.getDisplayData" - RID_Observation_GetDetails = "rid.observation.getDetails" - RID_Injection_CreateTest = "rid.injection.createTest" - RID_Injection_DeleteTest = "rid.injection.deleteTest" - SCD_GetStatus = "scd.getStatus" - SCD_GetCapabilities = "scd.getCapabilities" - SCD_PutFlight = "scd.putFlight" - SCD_DeleteFlight = "scd.deleteFlight" - SCD_CreateClearAreaRequest = "scd.createClearAreaRequest" - - -SCD_REQUESTS = { - RequestType.SCD_GetStatus, - RequestType.SCD_GetCapabilities, - RequestType.SCD_PutFlight, - RequestType.SCD_DeleteFlight, - RequestType.SCD_CreateClearAreaRequest, -} - - -# Each request descriptor in this file is expected to implement a static -# request_type_name() method which indicates the type of request corresponding -# with the descriptor. Handler clients will use this type name to determine -# what kind of query each query is. - -# --- RID observation (interfaces/automated_testing/rid/observation.yaml) --- -class RIDObservationGetDisplayDataRequest(ImplicitDict): - @staticmethod - def request_type_name() -> RequestType: - return RequestType.RID_Observation_GetDisplayData - - view: str - - -class RIDObservationGetDetailsRequest(ImplicitDict): - @staticmethod - def request_type_name() -> RequestType: - return RequestType.RID_Observation_GetDetails - - id: str - - -# --- RID injection (interfaces/automated_testing/rid/injection.yaml) --- -class RIDInjectionCreateTestRequest(ImplicitDict): - @staticmethod - def request_type_name() -> RequestType: - return RequestType.RID_Injection_CreateTest - - test_id: str - request_body: injection_api.CreateTestParameters - - -class RIDInjectionDeleteTestRequest(ImplicitDict): - @staticmethod - def request_type_name() -> RequestType: - return RequestType.RID_Injection_DeleteTest - - test_id: str - version: str - - -# --- SCD injection (interfaces/automated_testing/scd/v1/scd.yaml) --- -class SCDInjectionStatusRequest(ImplicitDict): - @staticmethod - def request_type_name() -> RequestType: - return RequestType.SCD_GetStatus - - -class SCDInjectionCapabilitiesRequest(ImplicitDict): - @staticmethod - def request_type_name() -> RequestType: - return RequestType.SCD_GetCapabilities - - -class SCDInjectionPutFlightRequest(ImplicitDict): - @staticmethod - def request_type_name() -> RequestType: - return RequestType.SCD_PutFlight - - flight_id: str - request_body: InjectFlightRequest - - -class SCDInjectionDeleteFlightRequest(ImplicitDict): - @staticmethod - def request_type_name() -> RequestType: - return RequestType.SCD_DeleteFlight - - flight_id: str - - -class SCDInjectionClearAreaRequest(ImplicitDict): - @staticmethod - def request_type_name() -> str: - return RequestType.SCD_CreateClearAreaRequest - - request_body: ClearAreaRequest diff --git a/monitoring/atproxy/routes.py b/monitoring/atproxy/routes.py deleted file mode 100644 index 19a381708e..0000000000 --- a/monitoring/atproxy/routes.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Tuple - -import flask -from loguru import logger -from werkzeug.exceptions import HTTPException -from werkzeug.security import check_password_hash - -from monitoring.monitorlib import auth_validation, versioning -from .app import webapp, basic_auth, users - - -@webapp.route("/") -def root() -> Tuple[str, int]: - return "ok", 200 - - -@webapp.route("/favicon.ico") -def favicon(): - flask.abort(404) - - -@webapp.route("/status") -@basic_auth.login_required -def status(): - return "atproxy ok {}".format(versioning.get_code_version()) - - -@webapp.errorhandler(Exception) -def handle_exception(e): - logger.error("Reporting exception {}: {}", type(e).__name__, str(e)) - if isinstance(e, HTTPException): - return e - elif isinstance(e, auth_validation.InvalidScopeError): - return ( - flask.jsonify( - { - "message": "Invalid scope; expected one of {%s}, but received only {%s}" - % (" ".join(e.permitted_scopes), " ".join(e.provided_scopes)) - } - ), - 403, - ) - elif isinstance(e, auth_validation.InvalidAccessTokenError): - return flask.jsonify({"message": e.message}), 401 - elif isinstance(e, auth_validation.ConfigurationError): - return ( - flask.jsonify( - {"message": "Auth validation configuration error: " + e.message} - ), - 500, - ) - elif isinstance(e, ValueError): - return flask.jsonify({"message": str(e)}), 400 - - return ( - flask.jsonify({"message": "Unhandled {}: {}".format(type(e).__name__, str(e))}), - 500, - ) - - -@basic_auth.verify_password -def verify_password(username, password): - if username in users and check_password_hash(users[username], password): - return username - - -from . import routes_handler -from . import routes_rid_observation -from . import routes_rid_injection -from . import routes_scd diff --git a/monitoring/atproxy/routes_handler.py b/monitoring/atproxy/routes_handler.py deleted file mode 100644 index d86eadb177..0000000000 --- a/monitoring/atproxy/routes_handler.py +++ /dev/null @@ -1,75 +0,0 @@ -import os -from datetime import datetime, timedelta -import time -from typing import Tuple - -import flask -from loguru import logger - -from implicitdict import ImplicitDict -from .app import webapp, basic_auth -from .database import db, Query, QueryState -from .handling import ListQueriesResponse, PendingRequest, PutQueryRequest - - -@webapp.route("/handler/queries", methods=["GET"]) -@basic_auth.login_required -def list_queries() -> Tuple[str, int]: - """Lists outstanding queries to be handled. - - See ListQueriesResponse for response body schema. - """ - t_start = datetime.utcnow() - logger.debug("Handler requesting queries from worker {}", os.getpid()) - max_timeout = timedelta(seconds=5) - while datetime.utcnow() < t_start + max_timeout: - with db as tx: - response = ListQueriesResponse( - requests=[ - PendingRequest(id=id, type=q.type, request=q.request) - for id, q in tx.queries.items() - if q.state == QueryState.Queued - ] - ) - if response.requests: - logger.debug( - "Provided handler {} queries".format(len(response.requests)) - ) - return flask.jsonify(response) - time.sleep(0.1) - logger.debug("No queries available for handler from worker {}", os.getpid()) - return flask.jsonify(ListQueriesResponse(requests=[])) - - -@webapp.route("/handler/queries/", methods=["PUT"]) -@basic_auth.login_required -def put_query_result(id: str) -> Tuple[str, int]: - """Fulfills an outstanding query. - - See PutQueryRequest for request body schema. - """ - logger.debug( - "Handler instructed to fulfill request {} from worker {}", id, os.getpid() - ) - try: - request: PutQueryRequest = ImplicitDict.parse( - flask.request.json, PutQueryRequest - ) - except ValueError as e: - msg = f"Could not parse PutQueryRequest due to {type(e).__name__} on worker {os.getpid()}: {str(e)}" - logger.error(msg) - return flask.jsonify({"message": msg}), 400 - with db as tx: - if id not in tx.queries: - msg = f"No outstanding request with ID {id} exists on worker {os.getpid()}" - logger.error(msg) - return flask.jsonify({"message": msg}), 400 - query: Query = tx.queries[id] - logger.debug( - "{} query {} handled with code {}", query.type, id, request.return_code - ) - query.return_code = request.return_code - query.response = request.response - query.state = QueryState.Complete - logger.debug("Handler fulfilled request {} from worker {}", id, os.getpid()) - return "", 204 diff --git a/monitoring/atproxy/routes_rid_injection.py b/monitoring/atproxy/routes_rid_injection.py deleted file mode 100644 index c101544251..0000000000 --- a/monitoring/atproxy/routes_rid_injection.py +++ /dev/null @@ -1,43 +0,0 @@ -from datetime import timedelta -from typing import Tuple - -import flask - -from . import handling -from .app import webapp -from .config import KEY_QUERY_TIMEOUT -from .oauth import requires_scope -from .requests import RIDInjectionCreateTestRequest, RIDInjectionDeleteTestRequest -from monitoring.monitorlib.rid_automated_testing import injection_api -from implicitdict import ImplicitDict - -timeout = timedelta(seconds=webapp.config[KEY_QUERY_TIMEOUT]) - - -@webapp.route("/ridsp/injection/tests/", methods=["PUT"]) -@requires_scope([injection_api.SCOPE_RID_QUALIFIER_INJECT]) -def rid_injection_create_test(test_id: str) -> Tuple[str, int]: - """Implements test creation in RID automated testing injection API.""" - try: - json = flask.request.json - if json is None: - raise ValueError("Request did not contain a JSON payload") - req_body: injection_api.CreateTestParameters = ImplicitDict.parse( - json, injection_api.CreateTestParameters - ) - except ValueError as e: - msg = "Create test {} unable to parse JSON: {}".format(test_id, e) - return msg, 400 - - return handling.fulfill_query( - RIDInjectionCreateTestRequest(test_id=test_id, request_body=req_body), timeout - ) - - -@webapp.route("/ridsp/injection/tests//", methods=["DELETE"]) -@requires_scope([injection_api.SCOPE_RID_QUALIFIER_INJECT]) -def rid_injection_delete_test(test_id: str, version: str) -> Tuple[str, int]: - """Implements test deletion in RID automated testing injection API.""" - return handling.fulfill_query( - RIDInjectionDeleteTestRequest(test_id=test_id, version=version), timeout - ) diff --git a/monitoring/atproxy/routes_rid_observation.py b/monitoring/atproxy/routes_rid_observation.py deleted file mode 100644 index 7c0c84a179..0000000000 --- a/monitoring/atproxy/routes_rid_observation.py +++ /dev/null @@ -1,34 +0,0 @@ -from datetime import timedelta -from typing import Tuple - -import flask -from uas_standards.astm.f3411.v19.constants import Scope - -from . import handling -from .app import webapp -from .config import KEY_QUERY_TIMEOUT -from .oauth import requires_scope -from .requests import ( - RIDObservationGetDisplayDataRequest, - RIDObservationGetDetailsRequest, -) - -timeout = timedelta(seconds=webapp.config[KEY_QUERY_TIMEOUT]) - - -@webapp.route("/riddp/observation/display_data", methods=["GET"]) -@requires_scope([Scope.Read]) -def rid_observation_display_data() -> Tuple[str, int]: - """Implements retrieval of current display data per automated testing API.""" - return handling.fulfill_query( - RIDObservationGetDisplayDataRequest(view=flask.request.args["view"]), timeout - ) - - -@webapp.route("/riddp/observation/display_data/", methods=["GET"]) -@requires_scope([Scope.Read]) -def rid_observation_flight_details(flight_id: str) -> Tuple[str, int]: - """Implements get flight details endpoint per automated testing API.""" - return handling.fulfill_query( - RIDObservationGetDetailsRequest(id=flight_id), timeout - ) diff --git a/monitoring/atproxy/routes_scd.py b/monitoring/atproxy/routes_scd.py deleted file mode 100644 index e76e82ad03..0000000000 --- a/monitoring/atproxy/routes_scd.py +++ /dev/null @@ -1,86 +0,0 @@ -from datetime import timedelta -from typing import Tuple - -import flask -from implicitdict import ImplicitDict -from uas_standards.interuss.automated_testing.scd.v1.api import ( - InjectFlightRequest, - ClearAreaRequest, -) - -from . import handling -from .app import webapp -from .config import KEY_QUERY_TIMEOUT -from .oauth import requires_scope -from .requests import ( - SCDInjectionStatusRequest, - SCDInjectionCapabilitiesRequest, - SCDInjectionPutFlightRequest, - SCDInjectionDeleteFlightRequest, - SCDInjectionClearAreaRequest, -) -from monitoring.monitorlib.scd_automated_testing.scd_injection_api import ( - SCOPE_SCD_QUALIFIER_INJECT, -) - - -timeout = timedelta(seconds=webapp.config[KEY_QUERY_TIMEOUT]) - - -@webapp.route("/scd/v1/status", methods=["GET"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) -def scd_injection_status() -> Tuple[str, int]: - """Implements status in SCD automated testing injection API.""" - return handling.fulfill_query(SCDInjectionStatusRequest(), timeout) - - -@webapp.route("/scd/v1/capabilities", methods=["GET"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) -def scd_injection_capabilities() -> Tuple[str, int]: - """Implements capabilities in SCD automated testing injection API.""" - return handling.fulfill_query(SCDInjectionCapabilitiesRequest(), timeout) - - -@webapp.route("/scd/v1/flights/", methods=["PUT"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) -def scd_injection_put_flight(flight_id: str) -> Tuple[str, int]: - """Implements PUT flight in SCD automated testing injection API.""" - try: - json = flask.request.json - if json is None: - raise ValueError("Request did not contain a JSON payload") - req_body: InjectFlightRequest = ImplicitDict.parse(json, InjectFlightRequest) - except ValueError as e: - msg = "Upsert flight {} unable to parse JSON: {}".format(flight_id, e) - return msg, 400 - return handling.fulfill_query( - SCDInjectionPutFlightRequest(flight_id=flight_id, request_body=req_body), - timeout, - ) - - -@webapp.route("/scd/v1/flights/", methods=["DELETE"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) -def scd_injection_delete_flight(flight_id: str) -> Tuple[str, int]: - """Implements flight deletion in SCD automated testing injection API.""" - return handling.fulfill_query( - SCDInjectionDeleteFlightRequest(flight_id=flight_id), timeout - ) - - -@webapp.route("/scd/v1/clear_area_requests", methods=["POST"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) -def scd_injection_clear_area() -> Tuple[str, int]: - """Implements area clearing in RID automated testing injection API.""" - try: - json = flask.request.json - if json is None: - raise ValueError("Request did not contain a JSON payload") - req_body: ClearAreaRequest = ImplicitDict.parse(json, ClearAreaRequest) - except ValueError as e: - msg = "Clear area request unable to parse JSON: {}".format(e) - return msg, 400 - - return handling.fulfill_query( - SCDInjectionClearAreaRequest(request_body=req_body), timeout - ) diff --git a/monitoring/atproxy/run_locally.sh b/monitoring/atproxy/run_locally.sh deleted file mode 100755 index 5f47c622e8..0000000000 --- a/monitoring/atproxy/run_locally.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail - -# Find and change to repo root directory -OS=$(uname) -if [[ "$OS" == "Darwin" ]]; then - # OSX uses BSD readlink - BASEDIR="$(dirname "$0")" -else - BASEDIR=$(readlink -e "$(dirname "$0")") -fi -cd "${BASEDIR}/../.." || exit 1 - -( -cd monitoring || exit 1 -make image -) - -CLIENT_BASIC_AUTH="local_client:local_client" -PUBLIC_KEY="/var/test-certs/auth2.pem" -AUD=${MOCK_USS_TOKEN_AUDIENCE:-localhost,host.docker.internal} - -PORT=8075 - -if [ "$CI" == "true" ]; then - docker_args="--add-host host.docker.internal:host-gateway" # Required to reach other containers in Ubuntu (used for Github Actions) -else - docker_args="-it" -fi - -# shellcheck disable=SC2086 -docker run ${docker_args} --name atproxy \ - -e ATPROXY_CLIENT_BASIC_AUTH="${CLIENT_BASIC_AUTH}" \ - -e ATPROXY_PUBLIC_KEY="${PUBLIC_KEY}" \ - -e ATPROXY_TOKEN_AUDIENCE="${AUD}" \ - -e ATPROXY_QUERY_TIMEOUT="${ATPROXY_QUERY_TIMEOUT:-5}" \ - -e PYTHONUNBUFFERED=TRUE \ - -p ${PORT}:5000 \ - -v "$(pwd)/build/test-certs:/var/test-certs:ro" \ - "$@" \ - interuss/monitoring \ - atproxy/start.sh diff --git a/monitoring/atproxy/start.sh b/monitoring/atproxy/start.sh deleted file mode 100755 index e1ec9c1f04..0000000000 --- a/monitoring/atproxy/start.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -# This script is intended to be called from within a Docker container running -# atproxy via the interuss/monitoring image. In that context, this script is -# the entrypoint into the atproxy server. - -# Ensure atproxy is the working directory -OS=$(uname) -if [[ $OS == "Darwin" ]]; then - # OSX uses BSD readlink - BASEDIR="$(dirname "$0")" -else - BASEDIR=$(readlink -e "$(dirname "$0")") -fi -cd "${BASEDIR}" || exit 1 - -# Use atproxy's health check -cp health_check.sh /app - -# Start atproxy server -port=${ATPROXY_PORT:-5000} -gunicorn \ - --preload \ - --workers=4 \ - --threads=2 \ - --timeout 60 \ - "--bind=0.0.0.0:${port}" \ - --log-level debug \ - --config ./gunicorn.conf.py \ - monitoring.atproxy.app:webapp diff --git a/monitoring/mock_uss/README.md b/monitoring/mock_uss/README.md index d59e0fac81..91488cc899 100644 --- a/monitoring/mock_uss/README.md +++ b/monitoring/mock_uss/README.md @@ -14,7 +14,6 @@ The same mock_uss binary can be configured to behave as one of many different types of USSs by enabling and configuring different sets of functionality. The available functionality sets are: -* [`atproxy_client`](atproxy_client): [atproxy](../atproxy) client/backend * [`geoawareness`](geoawareness): Geo-awareness provider * [`msgsigning`](msgsigning): [IETF HTTP Message Signatures](https://datatracker.ietf.org/doc/draft-ietf-httpbis-message-signatures/) * [`riddp`](riddp): Remote ID Display Provider diff --git a/monitoring/mock_uss/__init__.py b/monitoring/mock_uss/__init__.py index b326ea32b1..faa8e4edfa 100644 --- a/monitoring/mock_uss/__init__.py +++ b/monitoring/mock_uss/__init__.py @@ -10,7 +10,6 @@ SERVICE_RIDDP = "riddp" SERVICE_SCDSC = "scdsc" SERVICE_MESSAGESIGNING = "msgsigning" -SERVICE_ATPROXY_CLIENT = "atproxy_client" SERVICE_TRACER = "tracer" SERVICE_INTERACTION_LOGGING = "interaction_logging" SERVICE_VERSIONING = "versioning" @@ -94,10 +93,6 @@ def require_config_value(config_key: str) -> None: from monitoring.mock_uss.interaction_logging import logger as interactions_logger from monitoring.mock_uss.interaction_logging import routes_interactions_log -if SERVICE_ATPROXY_CLIENT in webapp.config[config.KEY_SERVICES]: - enabled_services.add(SERVICE_ATPROXY_CLIENT) - from monitoring.mock_uss import atproxy_client - if SERVICE_TRACER in webapp.config[config.KEY_SERVICES]: enabled_services.add(SERVICE_TRACER) from monitoring.mock_uss import tracer diff --git a/monitoring/mock_uss/atproxy_client/README.md b/monitoring/mock_uss/atproxy_client/README.md deleted file mode 100644 index 488efc5134..0000000000 --- a/monitoring/mock_uss/atproxy_client/README.md +++ /dev/null @@ -1 +0,0 @@ -[atproxy](../../atproxy) is a webserver that "implements" [InterUSS automated testing interfaces](../../../interfaces/automated_testing) by queuing requests and expecting a backend process to retrieve (via periodic polling of atproxy) and fulfill those requests. When this `atproxy_client` [mock_uss](..) functionality is enabled, mock_uss will act as this backend process, periodically polling an atproxy instance for outstanding requests, fulfilling those requests, and then posting the responses back to atproxy. diff --git a/monitoring/mock_uss/atproxy_client/__init__.py b/monitoring/mock_uss/atproxy_client/__init__.py deleted file mode 100644 index 7302f1b827..0000000000 --- a/monitoring/mock_uss/atproxy_client/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import daemon diff --git a/monitoring/mock_uss/atproxy_client/config.py b/monitoring/mock_uss/atproxy_client/config.py deleted file mode 100644 index 0e70b9cf41..0000000000 --- a/monitoring/mock_uss/atproxy_client/config.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations -from dataclasses import dataclass -from typing import Tuple - -from monitoring.mock_uss import import_environment_variable - - -@dataclass -class BasicAuth(object): - username: str - password: str - - @property - def tuple(self) -> Tuple[str, str]: - return self.username, self.password - - @staticmethod - def create(str_value) -> BasicAuth: - auth_components = tuple(s.strip() for s in str_value.split(":")) - if len(auth_components) != 2: - raise ValueError( - f'Invalid basic auth specification; expected : but instead found "{str_value}"' - ) - return BasicAuth(username=auth_components[0], password=auth_components[1]) - - -KEY_ATPROXY_BASE_URL = "MOCK_USS_ATPROXY_BASE_URL" -KEY_ATPROXY_BASIC_AUTH = "MOCK_USS_ATPROXY_BASIC_AUTH" - -import_environment_variable(KEY_ATPROXY_BASE_URL) -import_environment_variable(KEY_ATPROXY_BASIC_AUTH, mutator=BasicAuth.create) diff --git a/monitoring/mock_uss/atproxy_client/daemon.py b/monitoring/mock_uss/atproxy_client/daemon.py deleted file mode 100644 index 4c6cbf32a8..0000000000 --- a/monitoring/mock_uss/atproxy_client/daemon.py +++ /dev/null @@ -1,203 +0,0 @@ -import json -from datetime import timedelta, datetime -import time -from typing import Tuple, Optional - -from loguru import logger -import requests -from uas_standards.interuss.automated_testing.scd.v1.api import ( - ClearAreaRequest, - InjectFlightRequest, -) - -from implicitdict import ImplicitDict -from monitoring import mock_uss -from monitoring.atproxy.requests import ( - RequestType, - SCDInjectionPutFlightRequest, - SCDInjectionDeleteFlightRequest, - SCDInjectionClearAreaRequest, - SCD_REQUESTS, -) -from monitoring.atproxy.handling import ( - ListQueriesResponse, - PutQueryRequest, - PendingRequest, -) -from monitoring.mock_uss.atproxy_client import config -from monitoring.mock_uss import webapp -from monitoring.mock_uss.scdsc.routes_injection import ( - injection_status, - scd_capabilities, - inject_flight, - delete_flight, - clear_area, -) -from monitoring.monitorlib import fetch - -TASK_POLL_ATPROXY = "poll atproxy" -MAX_DAEMON_PROCESSES = 1 -ATPROXY_WAIT_TIMEOUT = timedelta(minutes=5) - - -@webapp.setup_task("verify atproxy connectivity") -def _wait_for_atproxy() -> None: - """Wait for atproxy to be available""" - base_url = mock_uss.webapp.config[config.KEY_ATPROXY_BASE_URL] - basic_auth = mock_uss.webapp.config[config.KEY_ATPROXY_BASIC_AUTH].tuple - timeout = datetime.utcnow() + ATPROXY_WAIT_TIMEOUT - status_url = f"{base_url}/status" - while not webapp.is_stopping(): - resp = None - try: - resp = requests.get(status_url, auth=basic_auth, timeout=8) - if resp.status_code == 200: - break - logger.info( - "atproxy at {} is not yet ready; received {} at /status: {}", - base_url, - resp.status_code, - resp.content.decode(), - ) - except requests.exceptions.ConnectionError as e: - logger.info("atproxy at {} is not yet reachable: {}", base_url, str(e)) - if datetime.utcnow() > timeout: - raise RuntimeError( - f"Timeout while trying to connect to atproxy at {status_url}; latest attempt yielded {resp.status_code if resp else 'ConnectionError'}" - ) - time.sleep(5) - - # Enable polling - webapp.set_task_period(TASK_POLL_ATPROXY, timedelta(seconds=0)) - - -@webapp.periodic_task(TASK_POLL_ATPROXY) -def _poll_atproxy() -> None: - """Poll atproxy for new requests and handle any unhandled requests""" - base_url = mock_uss.webapp.config[config.KEY_ATPROXY_BASE_URL] - query_url = f"{base_url}/handler/queries" - basic_auth = mock_uss.webapp.config[config.KEY_ATPROXY_BASIC_AUTH].tuple - - # Poll atproxy to see if there are any requests pending - query = fetch.query_and_describe(None, "GET", query_url, auth=basic_auth, timeout=8) - if query.status_code != 200: - logger.error( - "Error {} polling {}:\n{}", - query.status_code, - query_url, - json.dumps(query, indent=2), - ) - return - try: - queries_resp: ListQueriesResponse = ImplicitDict.parse( - query.response.json, ListQueriesResponse - ) - except ValueError as e: - logger.error( - "Error parsing atproxy response to request for queries: {}", str(e) - ) - return - if not queries_resp.requests: - logger.debug("No queries currently pending.") - return - - # Identify a request to handle - request_to_handle = queries_resp.requests[0] - - # Handle the request - logger.info( - "Handling {} request, id {}", request_to_handle.type, request_to_handle.id - ) - fulfillment = PutQueryRequest( - return_code=500, - response={"message": "Unknown error in mock_uss atproxy client handler"}, - ) - try: - content, code = _fulfill_request(request_to_handle) - logger.info(f"Request {request_to_handle.id} fulfillment has code {code}") - fulfillment = PutQueryRequest(return_code=code, response=content) - except ValueError as e: - msg = f"mock_uss atproxy client handler encountered ValueError: {e}" - logger.error(msg) - fulfillment = PutQueryRequest( - return_code=400, - response={"message": msg}, - ) - except NotImplementedError as e: - msg = f"mock_uss atproxy client handler encountered NotImplementedError: {e}" - logger.error(msg) - fulfillment = PutQueryRequest( - return_code=500, - response={"message": msg}, - ) - finally: - for attempt in range(1, 4): - query = fetch.query_and_describe( - None, - "PUT", - f"{query_url}/{request_to_handle.id}", - json=fulfillment, - auth=basic_auth, - timeout=(1, 2), - ) - if query.status_code != 204: - logger.error( - "Error {} reporting response {} to query {} on attempt {}; details:\n{}", - query.status_code, - fulfillment.return_code, - request_to_handle.id, - attempt, - json.dumps(query, indent=2), - ) - if query.status_code != 999 and query.status_code != 500: - # Error response is not retryable - break - else: - logger.info( - f"Delivered response to request {request_to_handle.id} to atproxy on attempt {attempt}" - ) - break - - -def _fulfill_request(request_to_handle: PendingRequest) -> Tuple[Optional[dict], int]: - """Fulfill a PendingRequest from atproxy by invoking appropriate handler logic - - Args: - request_to_handle: PendingRequest to handle - - Returns: - * dict content of response, or None for no response JSON body - * HTTP status code of response - """ - req_type = request_to_handle.type - - if req_type in SCD_REQUESTS: - if mock_uss.SERVICE_SCDSC not in mock_uss.enabled_services: - raise ValueError( - f"mock_uss cannot handle {req_type} request because {mock_uss.SERVICE_SCDSC} is not one of the enabled services ({', '.join(mock_uss.enabled_services)})" - ) - - if req_type == RequestType.SCD_GetStatus: - return injection_status() - elif req_type == RequestType.SCD_GetCapabilities: - return scd_capabilities() - elif req_type == RequestType.SCD_PutFlight: - req = ImplicitDict.parse( - request_to_handle.request, SCDInjectionPutFlightRequest - ) - body = ImplicitDict.parse(req.request_body, InjectFlightRequest) - return inject_flight(req.flight_id, body) - elif req_type == RequestType.SCD_DeleteFlight: - req = ImplicitDict.parse( - request_to_handle.request, SCDInjectionDeleteFlightRequest - ) - return delete_flight(req.flight_id) - elif req_type == RequestType.SCD_CreateClearAreaRequest: - req = ImplicitDict.parse( - request_to_handle.request, SCDInjectionClearAreaRequest - ) - body = ImplicitDict.parse(req.request_body, ClearAreaRequest) - return clear_area(body) - else: - # TODO: Add RID injection & observation support - raise NotImplementedError(f"Unsupported request type: {request_to_handle.type}") diff --git a/monitoring/mock_uss/docker-compose.yaml b/monitoring/mock_uss/docker-compose.yaml index 681a58bffe..d092c5362b 100644 --- a/monitoring/mock_uss/docker-compose.yaml +++ b/monitoring/mock_uss/docker-compose.yaml @@ -244,54 +244,6 @@ services: extra_hosts: - host.docker.internal:host-gateway - - # atproxy is currently disabled to troubleshoot occasional connection issues -# atproxy: -# container_name: atproxy -# hostname: atproxy.uss5.localutm -# image: interuss/monitoring -# command: atproxy/start.sh -# environment: -# - ATPROXY_CLIENT_BASIC_AUTH=local_client:local_client -# - ATPROXY_PUBLIC_KEY=/var/test-certs/auth2.pem -# - ATPROXY_TOKEN_AUDIENCE=atproxy.uss5.localutm,localhost,host.docker.internal -# - ATPROXY_QUERY_TIMEOUT=5 -# - ATPROXY_PORT=80 -# - PYTHONUNBUFFERED=TRUE -# expose: -# - 80 -# ports: -# - 8075:80 -# volumes: -# - ../../build/test-certs:/var/test-certs:ro -# restart: always -# networks: -# - interop_ecosystem_network -# extra_hosts: -# - host.docker.internal:host-gateway -# -# atproxy_client: -# container_name: atproxy_client -# image: interuss/monitoring -# command: mock_uss/start.sh -# environment: -# - MOCK_USS_AUTH_SPEC=DummyOAuth(http://oauth.authority.localutm:8085/token,uss5) -# - MOCK_USS_DSS_URL=http://dss.uss1.localutm -# - MOCK_USS_PUBLIC_KEY=/var/test-certs/auth2.pem -# - MOCK_USS_TOKEN_AUDIENCE=atproxy.uss5.localutm,localhost,host.docker.internal -# - MOCK_USS_BASE_URL=http://atproxy.uss5.localutm -# - MOCK_USS_ATPROXY_BASE_URL=http://atproxy.uss5.localutm -# - MOCK_USS_ATPROXY_BASIC_AUTH=local_client:local_client -# - MOCK_USS_SERVICES=atproxy_client,scdsc,ridsp,riddp -# - MOCK_USS_PORT=80 -# volumes: -# - ../../build/test-certs:/var/test-certs:ro -# restart: always -# networks: -# - interop_ecosystem_network -# extra_hosts: -# - host.docker.internal:host-gateway - networks: interop_ecosystem_network: external: true diff --git a/monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml b/monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml index 7e593e6127..00111b7788 100644 --- a/monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml +++ b/monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml @@ -122,11 +122,6 @@ all_flight_planners: injection_base_url: http://scdsc.uss1.localutm/scdsc local_debug: true - # The atproxy has been disabled since it is suspected to be responsible of issue #28. Replaced by another simple mock_uss - # # uss2 uses atproxy, with requests being fulfilled by mock_uss with atproxy_client functionality enabled - # - participant_id: uss2 - # injection_base_url: http://host.docker.internal:8075/scd - - participant_id: uss2 injection_base_url: http://scdsc.uss2.localutm/scdsc local_debug: true diff --git a/monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml b/monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml index e25be6824d..637b2ff4ef 100644 --- a/monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml +++ b/monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml @@ -122,11 +122,6 @@ all_flight_planners: injection_base_url: http://localhost:8074/scdsc local_debug: true - # The atproxy has been disabled since it is suspected to be responsible of issue #28. Replaced by another simple mock_uss - # # uss2 uses atproxy, with requests being fulfilled by mock_uss with atproxy_client functionality enabled - # - participant_id: uss2 - # injection_base_url: http://host.docker.internal:8075/scd - - participant_id: uss2 injection_base_url: http://localhost:8094/scdsc local_debug: true diff --git a/requirements.txt b/requirements.txt index 83e6e350b7..dacb668e78 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,6 @@ bc-jsonpath-ng==1.5.9 # uss_qualifier cryptography==39.0.1 faker===8.1.0 # uss_qualifier flask==1.1.2 -Flask-HTTPAuth==4.7.0 # atproxy geojson===2.5.0 # uss_qualifier gevent==22.10.2 # mock_uss / gunicorn worker google-auth==1.6.3 From 6c484c36f55257fecdac567f120a542acb1ef130 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 23 Oct 2023 08:38:01 -0700 Subject: [PATCH 02/11] [uss_qualifier] Add local test suite resources (#279) Add local test suite resources --- .../configurations/dev/library/resources.yaml | 8 -- .../configurations/dev/uspace.yaml | 3 - .../uss_qualifier/suites/definitions.py | 23 ++++- monitoring/uss_qualifier/suites/suite.py | 17 +++- .../suites/uspace/required_services.yaml | 6 +- .../definitions/TestSuiteDeclaration.json | 50 +++++------ .../definitions/TestSuiteDefinition.json | 84 +++++++++++-------- 7 files changed, 114 insertions(+), 77 deletions(-) diff --git a/monitoring/uss_qualifier/configurations/dev/library/resources.yaml b/monitoring/uss_qualifier/configurations/dev/library/resources.yaml index ff155301c1..5837c2da8f 100644 --- a/monitoring/uss_qualifier/configurations/dev/library/resources.yaml +++ b/monitoring/uss_qualifier/configurations/dev/library/resources.yaml @@ -365,11 +365,3 @@ locality_che: resource_type: resources.interuss.mock_uss.locality.LocalityResource specification: locality_code: CHE - -# ===== System versioning ===== - -uspace_system_identity: - $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json - resource_type: resources.versioning.SystemIdentityResource - specification: - system_identity: uspace.ussp diff --git a/monitoring/uss_qualifier/configurations/dev/uspace.yaml b/monitoring/uss_qualifier/configurations/dev/uspace.yaml index eac7881a40..aa8c389757 100644 --- a/monitoring/uss_qualifier/configurations/dev/uspace.yaml +++ b/monitoring/uss_qualifier/configurations/dev/uspace.yaml @@ -4,7 +4,6 @@ v1: resources: resource_declarations: locality_che: {$ref: 'library/resources.yaml#/locality_che'} - uspace_system_identity: {$ref: 'library/resources.yaml#/uspace_system_identity'} che_conflicting_flights: {$ref: 'library/resources.yaml#/che_conflicting_flights'} che_invalid_flight_intents: {$ref: 'library/resources.yaml#/che_invalid_flight_intents'} che_invalid_flight_auth_flights: {$ref: 'library/resources.yaml#/che_invalid_flight_auth_flights'} @@ -41,7 +40,6 @@ v1: locality: locality_che version_providers: scd_version_providers - system_identity: uspace_system_identity conflicting_flights: che_conflicting_flights priority_preemption_flights: che_conflicting_flights @@ -67,7 +65,6 @@ v1: suite_type: suites.uspace.required_services resources: version_providers: version_providers - system_identity: system_identity conflicting_flights: conflicting_flights priority_preemption_flights: priority_preemption_flights diff --git a/monitoring/uss_qualifier/suites/definitions.py b/monitoring/uss_qualifier/suites/definitions.py index a7d73b67fd..21f0b8e8ce 100644 --- a/monitoring/uss_qualifier/suites/definitions.py +++ b/monitoring/uss_qualifier/suites/definitions.py @@ -11,7 +11,12 @@ from monitoring.uss_qualifier.reports.capability_definitions import ( ParticipantCapabilityDefinition, ) -from monitoring.uss_qualifier.resources.definitions import ResourceID, ResourceTypeName +from monitoring.uss_qualifier.resources.definitions import ( + ResourceID, + ResourceTypeName, + ResourceCollection, + ResourceDeclaration, +) from monitoring.uss_qualifier.scenarios.definitions import ( TestScenarioDeclaration, ) @@ -24,7 +29,7 @@ class TestSuiteDeclaration(ImplicitDict): suite_definition: Optional[TestSuiteDefinition] """Definition of test suite internal to the configuration -- specified instead of `suite_type`.""" - resources: Dict[ResourceID, ResourceID] + resources: Optional[Dict[ResourceID, ResourceID]] """Mapping of the ID a resource will be known by in the child test suite -> the ID a resource is known by in the parent test suite. The child suite resource is supplied by the parent suite resource . @@ -136,6 +141,9 @@ class TestSuiteDefinition(ImplicitDict): resources: Dict[ResourceID, ResourceTypeNameSpecifyingOptional] """Enumeration of the resources used by this test suite""" + local_resources: Optional[Dict[ResourceID, ResourceDeclaration]] + """Declarations of resources originating in this test suite.""" + actions: List[TestSuiteActionDeclaration] """The actions to take when running the test suite. Components will be executed in order.""" @@ -145,6 +153,17 @@ class TestSuiteDefinition(ImplicitDict): participant_verifiable_capabilities: Optional[List[ParticipantCapabilityDefinition]] """Definitions of capabilities verified by this test suite for individual participants.""" + def __init__(self, *args, **kwargs): + super(TestSuiteDefinition, self).__init__(*args, **kwargs) + inherits_resources = "resources" in self and self.resources + local_resources = "local_resources" in self and self.local_resources + if inherits_resources and local_resources: + for k in self.resources: + if k in self.local_resources: + raise ValueError( + f"Resource '{k}' found in both `resources` and `local_resources`; resource IDs must be unique" + ) + @staticmethod def load_from_declaration( declaration: TestSuiteDeclaration, diff --git a/monitoring/uss_qualifier/suites/suite.py b/monitoring/uss_qualifier/suites/suite.py index 79cedd14d9..3769a4b2d1 100644 --- a/monitoring/uss_qualifier/suites/suite.py +++ b/monitoring/uss_qualifier/suites/suite.py @@ -38,6 +38,7 @@ ResourceType, make_child_resources, MissingResourceError, + create_resources, ) from monitoring.uss_qualifier.scenarios.scenario import ( TestScenario, @@ -185,10 +186,18 @@ def __init__( self.declaration = declaration self.definition = TestSuiteDefinition.load_from_declaration(declaration) - self.local_resources = { - local_resource_id: resources[parent_resource_id] - for local_resource_id, parent_resource_id in declaration.resources.items() - } + if "resources" in declaration and declaration.resources: + self.local_resources = { + local_resource_id: resources[parent_resource_id] + for local_resource_id, parent_resource_id in declaration.resources.items() + } + else: + self.local_resources = {} + if "local_resources" in self.definition and self.definition.local_resources: + local_resources = create_resources(self.definition.local_resources) + for local_resource_id, resource in local_resources.items(): + self.local_resources[local_resource_id] = resource + for resource_id, resource_type in self.definition.resources.items(): is_optional = resource_type.endswith("?") if is_optional: diff --git a/monitoring/uss_qualifier/suites/uspace/required_services.yaml b/monitoring/uss_qualifier/suites/uspace/required_services.yaml index 3e22acf037..b03100497a 100644 --- a/monitoring/uss_qualifier/suites/uspace/required_services.yaml +++ b/monitoring/uss_qualifier/suites/uspace/required_services.yaml @@ -1,7 +1,6 @@ name: U-space required services resources: version_providers: resources.versioning.VersionProvidersResource - system_identity: resources.versioning.SystemIdentityResource conflicting_flights: resources.flight_planning.FlightIntentsResource priority_preemption_flights: resources.flight_planning.FlightIntentsResource @@ -19,6 +18,11 @@ resources: id_generator: resources.interuss.IDGeneratorResource service_area: resources.netrid.ServiceAreaResource problematically_big_area: resources.VerticesResource +local_resources: + system_identity: + resource_type: resources.versioning.SystemIdentityResource + specification: + system_identity: uspace.ussp actions: - test_scenario: scenario_type: scenarios.versioning.GetSystemVersions diff --git a/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDeclaration.json b/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDeclaration.json index aca1fbc705..b9f7ec300c 100644 --- a/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDeclaration.json +++ b/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDeclaration.json @@ -1,12 +1,30 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDeclaration.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.suites.definitions.TestSuiteDeclaration, as defined in monitoring/uss_qualifier/suites/definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "resources": { + "additionalProperties": { + "type": "string" + }, + "description": "Mapping of the ID a resource will be known by in the child test suite -> the ID a resource is known by in the parent test suite.\n\nThe child suite resource is supplied by the parent suite resource .", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + } + }, + "type": [ + "object", + "null" + ] }, "suite_definition": { + "description": "Definition of test suite internal to the configuration -- specified instead of `suite_type`.", "oneOf": [ { "type": "null" @@ -14,33 +32,15 @@ { "$ref": "TestSuiteDefinition.json" } - ], - "description": "Definition of test suite internal to the configuration -- specified instead of `suite_type`." - }, - "resources": { - "type": "object", - "properties": { - "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" - } - }, - "additionalProperties": { - "type": "string" - }, - "description": "Mapping of the ID a resource will be known by in the child test suite -> the ID a resource is known by in the parent test suite.\n\nThe child suite resource is supplied by the parent suite resource ." + ] }, "suite_type": { + "description": "Type/location of test suite. Usually expressed as the file name of the suite definition (without extension) qualified relative to the `uss_qualifier` folder", "type": [ "string", "null" - ], - "description": "Type/location of test suite. Usually expressed as the file name of the suite definition (without extension) qualified relative to the `uss_qualifier` folder" + ] } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDeclaration.json", - "description": "monitoring.uss_qualifier.suites.definitions.TestSuiteDeclaration, as defined in monitoring/uss_qualifier/suites/definitions.py", - "required": [ - "resources" - ] + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDefinition.json b/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDefinition.json index dc091f2848..070c94d92e 100644 --- a/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDefinition.json +++ b/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDefinition.json @@ -1,16 +1,51 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDefinition.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Schema for the definition of a test suite, analogous to the Python TestScenario subclass for scenarios\n\nmonitoring.uss_qualifier.suites.definitions.TestSuiteDefinition, as defined in monitoring/uss_qualifier/suites/definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "actions": { + "description": "The actions to take when running the test suite. Components will be executed in order.", + "items": { + "$ref": "TestSuiteActionDeclaration.json" + }, + "type": "array" + }, + "local_resources": { + "additionalProperties": { + "$ref": "../../resources/definitions/ResourceDeclaration.json" + }, + "description": "Declarations of resources originating in this test suite.", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + } + }, + "type": [ + "object", + "null" + ] }, "name": { - "type": "string", - "description": "Name of the test suite" + "description": "Name of the test suite", + "type": "string" + }, + "participant_verifiable_capabilities": { + "description": "Definitions of capabilities verified by this test suite for individual participants.", + "items": { + "$ref": "../../reports/capability_definitions/ParticipantCapabilityDefinition.json" + }, + "type": [ + "array", + "null" + ] }, "report_evaluation_scenario": { + "description": "The scenario executed after all the actions that evaluates the test suite report. Must be a ReportEvaluationScenario.", "oneOf": [ { "type": "null" @@ -18,45 +53,26 @@ { "$ref": "../../scenarios/definitions/TestScenarioDeclaration.json" } - ], - "description": "The scenario executed after all the actions that evaluates the test suite report. Must be a ReportEvaluationScenario." - }, - "participant_verifiable_capabilities": { - "type": [ - "array", - "null" - ], - "items": { - "$ref": "../../reports/capability_definitions/ParticipantCapabilityDefinition.json" - }, - "description": "Definitions of capabilities verified by this test suite for individual participants." + ] }, "resources": { - "type": "object", - "properties": { - "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" - } - }, "additionalProperties": { "type": "string" }, - "description": "Enumeration of the resources used by this test suite" - }, - "actions": { - "type": "array", - "items": { - "$ref": "TestSuiteActionDeclaration.json" + "description": "Enumeration of the resources used by this test suite", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + } }, - "description": "The actions to take when running the test suite. Components will be executed in order." + "type": "object" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/suites/definitions/TestSuiteDefinition.json", - "description": "Schema for the definition of a test suite, analogous to the Python TestScenario subclass for scenarios\n\nmonitoring.uss_qualifier.suites.definitions.TestSuiteDefinition, as defined in monitoring/uss_qualifier/suites/definitions.py", "required": [ "actions", "name", "resources" - ] + ], + "type": "object" } \ No newline at end of file From efd11c1e4dc45a1832283bf925cae4b080b7c64f Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 23 Oct 2023 09:23:03 -0700 Subject: [PATCH 03/11] [tooling] Update schema generation (#280) * Update schema generation code * Run schema generation --- .../flight_check_table.py | 2 +- .../geospatial_map/feature_check_table.py | 2 +- schemas/manage_type_schemas.py | 93 ++++++++++++++----- .../monitorlib/fetch/RequestDescription.json | 38 ++++---- .../monitorlib/fetch/ResponseDescription.json | 34 +++---- .../monitorlib/geo/LatLngBoundingBox.json | 34 ------- .../monitoring/monitorlib/geo/Volume3D.json | 52 ----------- .../monitorlib/geotemporal/NextDay.json | 43 +++++++++ .../geotemporal/NextSunPosition.json | 29 ++++++ .../monitorlib/geotemporal/OffsetTime.json | 25 +++++ .../monitorlib/geotemporal/TestTime.json | 67 +++++++++++++ .../geotemporal/Volume4DTemplate.json | 86 +++++++++++++++++ .../for_each_dss/ForEachDSSSpecification.json | 29 ++++++ .../for_each_dss/ForEachDSSSpecification.json | 29 ++++++ ...lightPlannerCombinationsSpecification.json | 39 ++++++++ .../WithLocalitySpecification.json | 29 ++++++ .../repeat/RepeatSpecification.json | 24 +++++ .../configuration/GraphConfiguration.json | 16 ++-- .../USSQualifierConfiguration.json | 14 +-- .../capability_definitions/AllConditions.json | 16 ++-- .../capability_definitions/AnyCondition.json | 16 ++-- .../CapabilityVerificationCondition.json | 26 +++--- .../CapabilityVerifiedCondition.json | 30 +++--- .../NoFailedChecksCondition.json | 10 +- .../ParticipantCapabilityDefinition.json | 32 +++---- .../RequirementsCheckedCondition.json | 12 +-- .../SpecificCondition.json | 12 --- .../report/AllConditionsEvaluationReport.json | 20 ++-- .../report/AnyConditionEvaluationReport.json | 20 ++-- ...lityVerifiedConditionEvaluationReport.json | 32 +++---- .../reports/report/CheckedCapability.json | 34 +++---- .../reports/report/CheckedRequirement.json | 28 +++--- .../reports/report/ErrorReport.json | 32 +++---- .../reports/report/FailedCheck.json | 86 ++++++++--------- ...FailedChecksConditionEvaluationReport.json | 16 ++-- .../uss_qualifier/reports/report/Note.json | 16 ++-- ...ntCapabilityConditionEvaluationReport.json | 60 ++++++------ ...ParticipantCapabilityEvaluationReport.json | 30 +++--- ...mentsCheckedConditionEvaluationReport.json | 28 +++--- .../reports/report/SpuriousReportMatch.json | 24 ++--- .../reports/report/TestStepReport.json | 70 +++++++------- .../reports/report/TestSuiteActionReport.json | 30 +++--- .../definitions/RequirementCollection.json | 58 ++++++------ .../f3411/dss/DSSInstanceSpecification.json | 47 ++++++++++ .../f3411/dss/DSSInstancesSpecification.json} | 10 +- .../v21/dss/DSSInstanceSpecification.json | 31 +++++++ .../v21/dss/DSSInstancesSpecification.json | 21 +++++ .../AuthAdapterSpecification.json | 26 ++++++ .../definitions/ResourceCollection.json | 26 +++--- .../definitions/ResourceDeclaration.json | 34 +++---- .../resources/dev/noop/NoOpSpecification.json | 19 ++++ .../SourceDocumentSpecification.json | 19 ++++ .../resources/files/ExternalFile.json | 26 ++++++ .../FlightIntentsSpecification.json | 25 +++++ .../FlightPlannerConfiguration.json | 31 +++++++ ...annerCombinationSelectorSpecification.json | 38 ++++++++ .../FlightPlannerSpecification.json | 18 ++++ .../FlightPlannersSpecification.json | 21 +++++ .../definitions/FlightCheck.json | 63 +++++++++++++ .../definitions/FlightCheckTable.json | 21 +++++ .../FlightCheckTableSpecification.json | 18 ++++ .../definitions/FeatureCheck.json | 64 +++++++++++++ .../definitions/FeatureCheckTable.json | 21 +++++ .../FeatureCheckTableSpecification.json | 18 ++++ .../IDGeneratorSpecification.json | 24 +++++ .../mock_uss/client/MockUSSSpecification.json | 24 +++++ .../client/MockUSSsSpecification.json | 21 +++++ .../locality/LocalitySpecification.json} | 8 +- .../evaluation/EvaluationConfiguration.json | 30 ++++++ ...CircularFlightsSimulatorConfiguration.json | 51 ++++++++++ .../FlightDataKMLFileConfiguration.json | 31 +++++++ .../flight_data/FlightDataSpecification.json | 49 ++++++++++ .../FlightDataStorageSpecification.json | 26 ++++++ .../NetRIDObserversSpecification.json | 21 +++++ .../observers/ObserverConfiguration.json | 31 +++++++ .../ServiceAreaSpecification.json | 53 +++++++++++ .../NetRIDServiceProvidersSpecification.json | 21 +++++ .../ServiceProviderConfiguration.json | 31 +++++++ .../client/InterUSSVersionProvider.json | 19 ++++ .../client/VersionProviderSpecification.json | 30 ++++++ .../client/VersionProvidersSpecification.json | 21 +++++ .../SystemIdentitySpecification.json | 19 ++++ .../astm/f3411/v22a/api/LatLngPoint.json | 22 +++++ .../astm/f3411/v22a/api/Polygon.json | 21 +++++ 84 files changed, 1997 insertions(+), 576 deletions(-) delete mode 100644 schemas/monitoring/monitorlib/geo/LatLngBoundingBox.json delete mode 100644 schemas/monitoring/monitorlib/geo/Volume3D.json create mode 100644 schemas/monitoring/monitorlib/geotemporal/NextDay.json create mode 100644 schemas/monitoring/monitorlib/geotemporal/NextSunPosition.json create mode 100644 schemas/monitoring/monitorlib/geotemporal/OffsetTime.json create mode 100644 schemas/monitoring/monitorlib/geotemporal/TestTime.json create mode 100644 schemas/monitoring/monitorlib/geotemporal/Volume4DTemplate.json create mode 100644 schemas/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss/ForEachDSSSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss/ForEachDSSSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations/FlightPlannerCombinationsSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality/WithLocalitySpecification.json create mode 100644 schemas/monitoring/uss_qualifier/action_generators/repetition/repeat/RepeatSpecification.json delete mode 100644 schemas/monitoring/uss_qualifier/reports/capability_definitions/SpecificCondition.json create mode 100644 schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstanceSpecification.json rename schemas/monitoring/{monitorlib/geo/Polygon.json => uss_qualifier/resources/astm/f3411/dss/DSSInstancesSpecification.json} (50%) create mode 100644 schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstancesSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/communications/auth_adapter/AuthAdapterSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/dev/noop/NoOpSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/eurocae/ed269/source_document/SourceDocumentSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/files/ExternalFile.json create mode 100644 schemas/monitoring/uss_qualifier/resources/flight_planning/flight_intent/FlightIntentsSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json create mode 100644 schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerCombinationSelectorSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannersSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheck.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheckTable.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table/FlightCheckTableSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheck.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheckTable.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table/FeatureCheckTableSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/id_generator/IDGeneratorSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSsSpecification.json rename schemas/monitoring/{monitorlib/dicts/RemovedElement.json => uss_qualifier/resources/interuss/mock_uss/locality/LocalitySpecification.json} (50%) create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/evaluation/EvaluationConfiguration.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataKMLFileConfiguration.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/flight_data_resources/FlightDataStorageSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/observers/NetRIDObserversSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/observers/ObserverConfiguration.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/service_area/ServiceAreaSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/service_providers/NetRIDServiceProvidersSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/netrid/service_providers/ServiceProviderConfiguration.json create mode 100644 schemas/monitoring/uss_qualifier/resources/versioning/client/InterUSSVersionProvider.json create mode 100644 schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProviderSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProvidersSpecification.json create mode 100644 schemas/monitoring/uss_qualifier/resources/versioning/system_identity/SystemIdentitySpecification.json create mode 100644 schemas/uas_standards/astm/f3411/v22a/api/LatLngPoint.json create mode 100644 schemas/uas_standards/astm/f3411/v22a/api/Polygon.json diff --git a/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py b/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py index 5943f3bafd..668fab9c4d 100644 --- a/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py +++ b/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py @@ -10,7 +10,7 @@ class FlightCheckTableSpecification(ImplicitDict): table: FlightCheckTable -class FlightCheckTableResource(Resource): +class FlightCheckTableResource(Resource[FlightCheckTableSpecification]): table: FlightCheckTable def __init__(self, specification: FlightCheckTableSpecification): diff --git a/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py b/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py index daec022b07..a621fb3ec6 100644 --- a/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py +++ b/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py @@ -10,7 +10,7 @@ class FeatureCheckTableSpecification(ImplicitDict): table: FeatureCheckTable -class FeatureCheckTableResource(Resource): +class FeatureCheckTableResource(Resource[FeatureCheckTableSpecification]): table: FeatureCheckTable def __init__(self, specification: FeatureCheckTableSpecification): diff --git a/schemas/manage_type_schemas.py b/schemas/manage_type_schemas.py index db3c23ab86..710ff2156c 100644 --- a/schemas/manage_type_schemas.py +++ b/schemas/manage_type_schemas.py @@ -4,14 +4,24 @@ import json import os import sys -from typing import Optional, Set, Dict, Type +from typing import Optional, Set, Dict, Type, get_type_hints, get_args, get_origin +import implicitdict from implicitdict import ImplicitDict import implicitdict.jsonschema from implicitdict.jsonschema import SchemaVars, SchemaVarsResolver from loguru import logger import monitoring +import monitoring.uss_qualifier.action_generators +import monitoring.uss_qualifier.resources +from monitoring.monitorlib.inspection import fullname, import_submodules +from monitoring.uss_qualifier.action_generators.action_generator import ActionGenerator +from monitoring.uss_qualifier.configurations.configuration import ( + USSQualifierConfiguration, +) +from monitoring.uss_qualifier.reports.report import TestRunReport +from monitoring.uss_qualifier.resources.resource import Resource class Action(str, enum.Enum): @@ -41,18 +51,49 @@ def parse_args() -> argparse.Namespace: return parser.parse_args() -def _find_type_schemas( - module, +def _make_type_schemas( + parent: Type[ImplicitDict], reference_resolver: SchemaVarsResolver, - repo: Optional[Dict[str, dict]] = None, + repo: Dict[str, dict], already_checked: Optional[Set[str]] = None, -) -> Dict[str, dict]: +) -> None: + implicitdict.jsonschema.make_json_schema(parent, reference_resolver, repo) if already_checked is None: already_checked = set() - already_checked.add(module.__name__) + already_checked.add(fullname(parent)) + + # TODO: Expose get_fields formally in implicitdict + all_fields, _ = implicitdict._get_fields(parent) + hints = get_type_hints(parent) + for field in all_fields: + if field not in hints: + continue + field_type = hints[field] + + pending_types = [field_type] + while pending_types: + pending_type = pending_types.pop(0) + generic_type = get_origin(pending_type) + if generic_type: + pending_types.extend(get_args(pending_type)) + else: + if ( + issubclass(pending_type, ImplicitDict) + and fullname(pending_type) not in already_checked + ): + _make_type_schemas( + pending_type, reference_resolver, repo, already_checked + ) + - if repo is None: - repo = {} +def _find_specifications( + module, + repo: Dict[str, Type[ImplicitDict]], + already_checked: Optional[Set[str]] = None, +) -> None: + if already_checked is None: + already_checked = set() + already_checked.add(module.__name__) for name, member in inspect.getmembers(module): if ( @@ -60,15 +101,14 @@ def _find_type_schemas( and member.__name__ not in already_checked and member.__name__.startswith("monitoring") ): - _find_type_schemas(member, reference_resolver, repo, already_checked) - elif ( - inspect.isclass(member) - and issubclass(member, ImplicitDict) - and member != ImplicitDict - ): - implicitdict.jsonschema.make_json_schema(member, reference_resolver, repo) - - return repo + _find_specifications(member, repo, already_checked) + elif inspect.isclass(member): + if issubclass(member, Resource) and member != Resource: + spec_type = get_args(member.__orig_bases__[0])[0] + repo[fullname(spec_type)] = spec_type + elif issubclass(member, ActionGenerator) and member != ActionGenerator: + spec_type = get_args(member.__orig_bases__[0])[0] + repo[fullname(spec_type)] = spec_type def main() -> int: @@ -114,12 +154,19 @@ def path_to(t_dest: Type, t_src: Type) -> str: description=f"{full_name(schema_type)}, as defined in {path_of_py_file(schema_type)}", ) - from monitoring.uss_qualifier.reports.report import TestRunReport - from monitoring.uss_qualifier.configurations.configuration import ( - USSQualifierConfiguration, - ) - - schemas = _find_type_schemas(monitoring, schema_vars_resolver) + schemas = {} + _make_type_schemas(TestRunReport, schema_vars_resolver, schemas) + _make_type_schemas(USSQualifierConfiguration, schema_vars_resolver, schemas) + + repo = {} + import_submodules(monitoring.uss_qualifier.resources) + _find_specifications(monitoring.uss_qualifier.resources, repo) + import_submodules(monitoring.uss_qualifier.action_generators) + _find_specifications(monitoring.uss_qualifier.action_generators, repo) + for spec_type in repo.values(): + implicitdict.jsonschema.make_json_schema( + spec_type, schema_vars_resolver, schemas + ) repo_root = os.path.abspath( os.path.join(os.path.dirname(monitoring.__file__), "..") diff --git a/schemas/monitoring/monitorlib/fetch/RequestDescription.json b/schemas/monitoring/monitorlib/fetch/RequestDescription.json index 6b96254576..167ef825a4 100644 --- a/schemas/monitoring/monitorlib/fetch/RequestDescription.json +++ b/schemas/monitoring/monitorlib/fetch/RequestDescription.json @@ -1,28 +1,28 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/fetch/RequestDescription.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.monitorlib.fetch.RequestDescription, as defined in monitoring/monitorlib/fetch/__init__.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, - "initiated_at": { + "body": { "type": [ "string", "null" - ], - "format": "date-time" + ] }, - "received_at": { + "headers": { "type": [ - "string", + "object", "null" - ], - "format": "date-time" + ] }, - "headers": { + "initiated_at": { + "format": "date-time", "type": [ - "object", + "string", "null" ] }, @@ -35,20 +35,20 @@ "method": { "type": "string" }, - "url": { - "type": "string" - }, - "body": { + "received_at": { + "format": "date-time", "type": [ "string", "null" ] + }, + "url": { + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/fetch/RequestDescription.json", - "description": "monitoring.monitorlib.fetch.RequestDescription, as defined in monitoring/monitorlib/fetch/__init__.py", "required": [ "method", "url" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/fetch/ResponseDescription.json b/schemas/monitoring/monitorlib/fetch/ResponseDescription.json index b141547224..34afcab1f0 100644 --- a/schemas/monitoring/monitorlib/fetch/ResponseDescription.json +++ b/schemas/monitoring/monitorlib/fetch/ResponseDescription.json @@ -1,10 +1,17 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/fetch/ResponseDescription.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.monitorlib.fetch.ResponseDescription, as defined in monitoring/monitorlib/fetch/__init__.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "body": { + "type": [ + "string", + "null" + ] }, "code": { "type": [ @@ -12,6 +19,9 @@ "null" ] }, + "elapsed_s": { + "type": "number" + }, "failure": { "type": [ "string", @@ -30,24 +40,14 @@ "null" ] }, - "elapsed_s": { - "type": "number" - }, - "body": { - "type": [ - "string", - "null" - ] - }, "reported": { - "type": "string", - "format": "date-time" + "format": "date-time", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/fetch/ResponseDescription.json", - "description": "monitoring.monitorlib.fetch.ResponseDescription, as defined in monitoring/monitorlib/fetch/__init__.py", "required": [ "elapsed_s", "reported" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/geo/LatLngBoundingBox.json b/schemas/monitoring/monitorlib/geo/LatLngBoundingBox.json deleted file mode 100644 index da84055d29..0000000000 --- a/schemas/monitoring/monitorlib/geo/LatLngBoundingBox.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/geo/LatLngBoundingBox.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "Bounding box in latitude and longitude\n\nmonitoring.monitorlib.geo.LatLngBoundingBox, as defined in monitoring/monitorlib/geo.py", - "properties": { - "$ref": { - "description": "Path to content that replaces the $ref", - "type": "string" - }, - "lat_max": { - "description": "Upper latitude bound (degrees)", - "type": "number" - }, - "lat_min": { - "description": "Lower latitude bound (degrees)", - "type": "number" - }, - "lng_max": { - "description": "Upper longitude bound (degrees)", - "type": "number" - }, - "lng_min": { - "description": "Lower longitude bound (degrees)", - "type": "number" - } - }, - "required": [ - "lat_max", - "lat_min", - "lng_max", - "lng_min" - ], - "type": "object" -} \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/geo/Volume3D.json b/schemas/monitoring/monitorlib/geo/Volume3D.json deleted file mode 100644 index abbbc30120..0000000000 --- a/schemas/monitoring/monitorlib/geo/Volume3D.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/geo/Volume3D.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "monitoring.monitorlib.geo.Volume3D, as defined in monitoring/monitorlib/geo.py", - "properties": { - "$ref": { - "description": "Path to content that replaces the $ref", - "type": "string" - }, - "altitude_lower": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "Altitude.json" - } - ] - }, - "altitude_upper": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "Altitude.json" - } - ] - }, - "outline_circle": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "Circle.json" - } - ] - }, - "outline_polygon": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "Polygon.json" - } - ] - } - }, - "type": "object" -} \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/geotemporal/NextDay.json b/schemas/monitoring/monitorlib/geotemporal/NextDay.json new file mode 100644 index 0000000000..c7dcf08b96 --- /dev/null +++ b/schemas/monitoring/monitorlib/geotemporal/NextDay.json @@ -0,0 +1,43 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/geotemporal/NextDay.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.monitorlib.geotemporal.NextDay, as defined in monitoring/monitorlib/geotemporal.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "days_of_the_week": { + "description": "Acceptable days of the week. Omit to indicate that any day of the week is acceptable.", + "items": { + "enum": [ + "Mo", + "Tu", + "We", + "Th", + "Fr", + "Sa", + "Su" + ], + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "starting_from": { + "$ref": "TestTime.json", + "description": "The time after which the first instance of one of the days should be found." + }, + "time_zone": { + "description": "Time zone in which \"day\" is understood. Examples:\n * \"local\" (local time of machine running this code)\n * \"Z\" (Zulu time)\n * \"-08:00\" (ISO time zone)\n * \"US/Pacific\" (IANA time zone)", + "type": "string" + } + }, + "required": [ + "starting_from", + "time_zone" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/geotemporal/NextSunPosition.json b/schemas/monitoring/monitorlib/geotemporal/NextSunPosition.json new file mode 100644 index 0000000000..23ec5b041b --- /dev/null +++ b/schemas/monitoring/monitorlib/geotemporal/NextSunPosition.json @@ -0,0 +1,29 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/geotemporal/NextSunPosition.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.monitorlib.geotemporal.NextSunPosition, as defined in monitoring/monitorlib/geotemporal.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "elevation_deg": { + "description": "Elevation of the center of the sun above horizontal, in degrees.", + "type": "number" + }, + "observed_from": { + "$ref": "../geo/LatLngPoint.json", + "description": "The location on earth observing the sun." + }, + "starting_from": { + "$ref": "TestTime.json", + "description": "The time after which the first time the sun is at the specified position should be found." + } + }, + "required": [ + "elevation_deg", + "observed_from", + "starting_from" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/geotemporal/OffsetTime.json b/schemas/monitoring/monitorlib/geotemporal/OffsetTime.json new file mode 100644 index 0000000000..8ffef9f9b9 --- /dev/null +++ b/schemas/monitoring/monitorlib/geotemporal/OffsetTime.json @@ -0,0 +1,25 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/geotemporal/OffsetTime.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.monitorlib.geotemporal.OffsetTime, as defined in monitoring/monitorlib/geotemporal.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "offset": { + "description": "Offset from starting time.", + "format": "duration", + "type": "string" + }, + "starting_from": { + "$ref": "TestTime.json", + "description": "The time from which the offset should be applied." + } + }, + "required": [ + "offset", + "starting_from" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/geotemporal/TestTime.json b/schemas/monitoring/monitorlib/geotemporal/TestTime.json new file mode 100644 index 0000000000..a88291be01 --- /dev/null +++ b/schemas/monitoring/monitorlib/geotemporal/TestTime.json @@ -0,0 +1,67 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/geotemporal/TestTime.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Exactly one of the time option fields of this object must be specified.\n\nmonitoring.monitorlib.geotemporal.TestTime, as defined in monitoring/monitorlib/geotemporal.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "absolute_time": { + "description": "Time option field to use a precise timestamp which does not change with test conditions.\n\nThe value of absolute_time is limited given that the specific time a test will be started is unknown, and the jurisdictions usually impose a limit on how far in the future an operation can be planned.", + "format": "date-time", + "type": [ + "string", + "null" + ] + }, + "next_day": { + "description": "Time option field to use a timestamp equal to midnight beginning the next occurrence of any matching day following the specified reference timestamp.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "NextDay.json" + } + ] + }, + "next_sun_position": { + "description": "Time option field to use a timestamp equal to the next time after the specified reference timestamp at which the sun will be at the specified angle above the horizon.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "NextSunPosition.json" + } + ] + }, + "offset_from": { + "description": "Time option field to use a timestamp that is offset by the specified amount from the specified time.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "OffsetTime.json" + } + ] + }, + "start_of_test": { + "description": "Time option field to, if specified, use the timestamp at which the current test run started.", + "type": [ + "object", + "null" + ] + }, + "use_timezone": { + "description": "If specified, report the timestamp in the specified time zone. Examples:\n * \"local\" (local time of machine running this code)\n * \"Z\" (Zulu time)\n * \"-08:00\" (ISO time zone)\n * \"US/Pacific\" (IANA time zone)", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/geotemporal/Volume4DTemplate.json b/schemas/monitoring/monitorlib/geotemporal/Volume4DTemplate.json new file mode 100644 index 0000000000..bbf22ce12c --- /dev/null +++ b/schemas/monitoring/monitorlib/geotemporal/Volume4DTemplate.json @@ -0,0 +1,86 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/geotemporal/Volume4DTemplate.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.monitorlib.geotemporal.Volume4DTemplate, as defined in monitoring/monitorlib/geotemporal.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "altitude_lower": { + "description": "The minimum altitude at which the virtual user will fly while using this volume for their flight.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "../geo/Altitude.json" + } + ] + }, + "altitude_upper": { + "description": "The maximum altitude at which the virtual user will fly while using this volume for their flight.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "../geo/Altitude.json" + } + ] + }, + "duration": { + "description": "If only one of start_time and end_time is specified, then the other time should be separated from the specified time by this amount. May not be defined in both start_time and end_time are defined.", + "format": "duration", + "type": [ + "string", + "null" + ] + }, + "end_time": { + "description": "The time at which the virtual user will be finished using the specified geospatial area for their flight. May not be defined if duration and start_time are defined.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "TestTime.json" + } + ] + }, + "outline_circle": { + "description": "Circular outline/footprint of the specified area. May not be defined if outline_polygon is defined.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "../geo/Circle.json" + } + ] + }, + "outline_polygon": { + "description": "Polygonal 2D outline/footprint of the specified area. May not be defined if outline_circle is defined.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "../../../uas_standards/astm/f3411/v22a/api/Polygon.json" + } + ] + }, + "start_time": { + "description": "The time at which the virtual user may start using the specified geospatial area for their flight. May not be defined if duration and end_time are defined.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "TestTime.json" + } + ] + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss/ForEachDSSSpecification.json b/schemas/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss/ForEachDSSSpecification.json new file mode 100644 index 0000000000..026f9e185d --- /dev/null +++ b/schemas/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss/ForEachDSSSpecification.json @@ -0,0 +1,29 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss/ForEachDSSSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.action_generators.astm.f3411.for_each_dss.ForEachDSSSpecification, as defined in monitoring/uss_qualifier/action_generators/astm/f3411/for_each_dss.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "action_to_repeat": { + "$ref": "../../../../suites/definitions/TestSuiteActionDeclaration.json", + "description": "Test suite action to run for each DSS instance" + }, + "dss_instance_id": { + "description": "Resource IDs of DSS input to the action_to_repeat", + "type": "string" + }, + "dss_instances_source": { + "description": "ID of the resource providing the single DSS instance", + "type": "string" + } + }, + "required": [ + "action_to_repeat", + "dss_instance_id", + "dss_instances_source" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss/ForEachDSSSpecification.json b/schemas/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss/ForEachDSSSpecification.json new file mode 100644 index 0000000000..3cb25f3b8e --- /dev/null +++ b/schemas/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss/ForEachDSSSpecification.json @@ -0,0 +1,29 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss/ForEachDSSSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.action_generators.astm.f3548.for_each_dss.ForEachDSSSpecification, as defined in monitoring/uss_qualifier/action_generators/astm/f3548/for_each_dss.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "action_to_repeat": { + "$ref": "../../../../suites/definitions/TestSuiteActionDeclaration.json", + "description": "Test suite action to run for each DSS instance" + }, + "dss_instance_id": { + "description": "Resource IDs of DSS input to the action_to_repeat", + "type": "string" + }, + "dss_instances_source": { + "description": "ID of the resource providing the single DSS instance", + "type": "string" + } + }, + "required": [ + "action_to_repeat", + "dss_instance_id", + "dss_instances_source" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations/FlightPlannerCombinationsSpecification.json b/schemas/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations/FlightPlannerCombinationsSpecification.json new file mode 100644 index 0000000000..233f95616f --- /dev/null +++ b/schemas/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations/FlightPlannerCombinationsSpecification.json @@ -0,0 +1,39 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations/FlightPlannerCombinationsSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.action_generators.flight_planning.planner_combinations.FlightPlannerCombinationsSpecification, as defined in monitoring/uss_qualifier/action_generators/flight_planning/planner_combinations.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "action_to_repeat": { + "$ref": "../../../suites/definitions/TestSuiteActionDeclaration.json", + "description": "Test suite action to run for each combination of flight planners" + }, + "combination_selector_source": { + "description": "If specified and contained in the provided resources, the resource containing a FlightPlannerCombinationSelectorResource to select only a subset of combinations", + "type": [ + "string", + "null" + ] + }, + "flight_planners_source": { + "description": "ID of the resource providing all available flight planners", + "type": "string" + }, + "roles": { + "description": "Resource IDs of FlightPlannerResource inputs to the action_to_repeat", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "action_to_repeat", + "flight_planners_source", + "roles" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality/WithLocalitySpecification.json b/schemas/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality/WithLocalitySpecification.json new file mode 100644 index 0000000000..9e384537c0 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality/WithLocalitySpecification.json @@ -0,0 +1,29 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality/WithLocalitySpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.action_generators.interuss.mock_uss.with_locality.WithLocalitySpecification, as defined in monitoring/uss_qualifier/action_generators/interuss/mock_uss/with_locality.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "action_to_wrap": { + "$ref": "../../../../suites/definitions/TestSuiteActionDeclaration.json", + "description": "Test suite action to perform after setting mock_uss localities" + }, + "locality_source": { + "description": "ID of the resource providing the locality to use temporarily for the provided mock_uss instances", + "type": "string" + }, + "mock_uss_instances_source": { + "description": "ID of the resource providing all mock_uss instances to change the locality of", + "type": "string" + } + }, + "required": [ + "action_to_wrap", + "locality_source", + "mock_uss_instances_source" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/action_generators/repetition/repeat/RepeatSpecification.json b/schemas/monitoring/uss_qualifier/action_generators/repetition/repeat/RepeatSpecification.json new file mode 100644 index 0000000000..7e7896330b --- /dev/null +++ b/schemas/monitoring/uss_qualifier/action_generators/repetition/repeat/RepeatSpecification.json @@ -0,0 +1,24 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/action_generators/repetition/repeat/RepeatSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.action_generators.repetition.repeat.RepeatSpecification, as defined in monitoring/uss_qualifier/action_generators/repetition/repeat.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "action_to_repeat": { + "$ref": "../../../suites/definitions/TestSuiteActionDeclaration.json", + "description": "Test suite action to repeat" + }, + "times_to_repeat": { + "description": "Number of times to repeat the test suite action declared above", + "type": "integer" + } + }, + "required": [ + "action_to_repeat", + "times_to_repeat" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json index 0235cb737a..715d83454a 100644 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json +++ b/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json @@ -1,19 +1,19 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.configurations.configuration.GraphConfiguration, as defined in monitoring/uss_qualifier/configurations/configuration.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "gv_path": { - "type": "string", - "description": "Path of GraphViz (.gv) text file to contain a visualization of the test run" + "description": "Path of GraphViz (.gv) text file to contain a visualization of the test run", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json", - "description": "monitoring.uss_qualifier.configurations.configuration.GraphConfiguration, as defined in monitoring/uss_qualifier/configurations/configuration.py", "required": [ "gv_path" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/configurations/configuration/USSQualifierConfiguration.json b/schemas/monitoring/uss_qualifier/configurations/configuration/USSQualifierConfiguration.json index 31a9b4a767..295ed9c4ee 100644 --- a/schemas/monitoring/uss_qualifier/configurations/configuration/USSQualifierConfiguration.json +++ b/schemas/monitoring/uss_qualifier/configurations/configuration/USSQualifierConfiguration.json @@ -1,12 +1,14 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/configurations/configuration/USSQualifierConfiguration.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.configurations.configuration.USSQualifierConfiguration, as defined in monitoring/uss_qualifier/configurations/configuration.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "v1": { + "description": "Configuration in version 1 format", "oneOf": [ { "type": "null" @@ -14,10 +16,8 @@ { "$ref": "USSQualifierConfigurationV1.json" } - ], - "description": "Configuration in version 1 format" + ] } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/configurations/configuration/USSQualifierConfiguration.json", - "description": "monitoring.uss_qualifier.configurations.configuration.USSQualifierConfiguration, as defined in monitoring/uss_qualifier/configurations/configuration.py" + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/capability_definitions/AllConditions.json b/schemas/monitoring/uss_qualifier/reports/capability_definitions/AllConditions.json index 80545cbcdf..2ef50616d5 100644 --- a/schemas/monitoring/uss_qualifier/reports/capability_definitions/AllConditions.json +++ b/schemas/monitoring/uss_qualifier/reports/capability_definitions/AllConditions.json @@ -1,21 +1,21 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/AllConditions.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Condition will only be satisfied when all specified conditions are satisfied.\n\nNote that an empty list of conditions will result in a successful evaluation.\n\nmonitoring.uss_qualifier.reports.capability_definitions.AllConditions, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "conditions": { - "type": "array", "items": { "$ref": "CapabilityVerificationCondition.json" - } + }, + "type": "array" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/AllConditions.json", - "description": "Condition will only be satisfied when all specified conditions are satisfied.\n\nNote that an empty list of conditions will result in a successful evaluation.\n\nmonitoring.uss_qualifier.reports.capability_definitions.AllConditions, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "required": [ "conditions" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/capability_definitions/AnyCondition.json b/schemas/monitoring/uss_qualifier/reports/capability_definitions/AnyCondition.json index c2c00bdd9b..9a6a00eea3 100644 --- a/schemas/monitoring/uss_qualifier/reports/capability_definitions/AnyCondition.json +++ b/schemas/monitoring/uss_qualifier/reports/capability_definitions/AnyCondition.json @@ -1,21 +1,21 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/AnyCondition.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Condition will be satisfied when any of the specified conditions are satisfied.\n\nNote that an empty list of conditions will result in an unsuccessful evaluation.\n\nmonitoring.uss_qualifier.reports.capability_definitions.AnyCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "conditions": { - "type": "array", "items": { "$ref": "CapabilityVerificationCondition.json" - } + }, + "type": "array" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/AnyCondition.json", - "description": "Condition will be satisfied when any of the specified conditions are satisfied.\n\nNote that an empty list of conditions will result in an unsuccessful evaluation.\n\nmonitoring.uss_qualifier.reports.capability_definitions.AnyCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "required": [ "conditions" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerificationCondition.json b/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerificationCondition.json index 1aee4dc145..8f8fd2200b 100644 --- a/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerificationCondition.json +++ b/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerificationCondition.json @@ -1,18 +1,19 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerificationCondition.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Specification of a single condition used to determine whether a capability should be verified.\n\nExactly one field must be specified.\n\nmonitoring.uss_qualifier.reports.capability_definitions.CapabilityVerificationCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, - "no_failed_checks": { + "all_conditions": { "oneOf": [ { "type": "null" }, { - "$ref": "NoFailedChecksCondition.json" + "$ref": "AllConditions.json" } ] }, @@ -26,37 +27,36 @@ } ] }, - "requirements_checked": { + "capability_verified": { "oneOf": [ { "type": "null" }, { - "$ref": "RequirementsCheckedCondition.json" + "$ref": "CapabilityVerifiedCondition.json" } ] }, - "all_conditions": { + "no_failed_checks": { "oneOf": [ { "type": "null" }, { - "$ref": "AllConditions.json" + "$ref": "NoFailedChecksCondition.json" } ] }, - "capability_verified": { + "requirements_checked": { "oneOf": [ { "type": "null" }, { - "$ref": "CapabilityVerifiedCondition.json" + "$ref": "RequirementsCheckedCondition.json" } ] } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerificationCondition.json", - "description": "Specification of a single condition used to determine whether a capability should be verified.\n\nExactly one field must be specified.\n\nmonitoring.uss_qualifier.reports.capability_definitions.CapabilityVerificationCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py" + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerifiedCondition.json b/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerifiedCondition.json index 363f67ac1e..dbba84ca25 100644 --- a/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerifiedCondition.json +++ b/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerifiedCondition.json @@ -1,29 +1,29 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerifiedCondition.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Condition will be satisfied when the specified capability is verified.\n\nNote that a capability which do not declare any requirement will result in an unsuccessful evaluation.\n\nmonitoring.uss_qualifier.reports.capability_definitions.CapabilityVerifiedCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" - }, - "capability_location": { - "type": [ - "string", - "null" - ], - "description": "Location of report to inspect for the verification of the specified capability, relative to the report in which\nthe capability is defined. Implicit default value is \"$\" (look for verified capability in the report in which the\ndependant capability is defined).\n\nIf this location resolves to multiple TestSuiteReports, then the capability must be verified in all resolved reports\nin order for this condition to be satisfied. When this location resolves to artifacts that are not\nTestSuiteReports, those artifacts will be ignored.\n\nNote that capabilities are verified in the order they are defined. So, if capability B defined in a particular\nlocation depends on whether capability A in that same location is granted, capability A must be defined before\ncapability B is defined. Also note that capability verifications are computed as test components are completed.\nSince a parent test component (e.g., test suite) is not complete until all of its child components are complete, a\ndescendant test component's capability condition cannot depend on whether an ancestor's (e.g., parent's) capability\nis verified." + "description": "Path to content that replaces the $ref", + "type": "string" }, "capability_ids": { - "type": "array", + "description": "List of identifier of capability that must be verified for this condition to be satisfied.", "items": { "type": "string" }, - "description": "List of identifier of capability that must be verified for this condition to be satisfied." + "type": "array" + }, + "capability_location": { + "description": "Location of report to inspect for the verification of the specified capability, relative to the report in which\nthe capability is defined. Implicit default value is \"$\" (look for verified capability in the report in which the\ndependant capability is defined).\n\nIf this location resolves to multiple TestSuiteReports, then the capability must be verified in all resolved reports\nin order for this condition to be satisfied. When this location resolves to artifacts that are not\nTestSuiteReports, those artifacts will be ignored.\n\nNote that capabilities are verified in the order they are defined. So, if capability B defined in a particular\nlocation depends on whether capability A in that same location is granted, capability A must be defined before\ncapability B is defined. Also note that capability verifications are computed as test components are completed.\nSince a parent test component (e.g., test suite) is not complete until all of its child components are complete, a\ndescendant test component's capability condition cannot depend on whether an ancestor's (e.g., parent's) capability\nis verified.", + "type": [ + "string", + "null" + ] } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/CapabilityVerifiedCondition.json", - "description": "Condition will be satisfied when the specified capability is verified.\n\nNote that a capability which do not declare any requirement will result in an unsuccessful evaluation.\n\nmonitoring.uss_qualifier.reports.capability_definitions.CapabilityVerifiedCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "required": [ "capability_ids" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/capability_definitions/NoFailedChecksCondition.json b/schemas/monitoring/uss_qualifier/reports/capability_definitions/NoFailedChecksCondition.json index ffce3f407f..ad16251e7a 100644 --- a/schemas/monitoring/uss_qualifier/reports/capability_definitions/NoFailedChecksCondition.json +++ b/schemas/monitoring/uss_qualifier/reports/capability_definitions/NoFailedChecksCondition.json @@ -1,12 +1,12 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/NoFailedChecksCondition.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Condition will only be satisfied if there are no applicable failed checks.\n\nFor a capability to be verified for a participant, only checks including the participant's ID will be considered.\n\nmonitoring.uss_qualifier.reports.capability_definitions.NoFailedChecksCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/NoFailedChecksCondition.json", - "description": "Condition will only be satisfied if there are no applicable failed checks.\n\nFor a capability to be verified for a participant, only checks including the participant's ID will be considered.\n\nmonitoring.uss_qualifier.reports.capability_definitions.NoFailedChecksCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py" + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/capability_definitions/ParticipantCapabilityDefinition.json b/schemas/monitoring/uss_qualifier/reports/capability_definitions/ParticipantCapabilityDefinition.json index 87d54d42ee..f8e1d0643c 100644 --- a/schemas/monitoring/uss_qualifier/reports/capability_definitions/ParticipantCapabilityDefinition.json +++ b/schemas/monitoring/uss_qualifier/reports/capability_definitions/ParticipantCapabilityDefinition.json @@ -1,34 +1,34 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/ParticipantCapabilityDefinition.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.reports.capability_definitions.ParticipantCapabilityDefinition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "description": { - "type": "string", - "description": "Human-readable description of the capability." + "description": "Human-readable description of the capability.", + "type": "string" }, - "verification_condition": { - "$ref": "CapabilityVerificationCondition.json", - "description": "Condition required in order to verify the capability." + "id": { + "description": "Identifier of this capability, unique at the level in which this capability is defined.", + "type": "string" }, "name": { - "type": "string", - "description": "Human-readable name of the capability." + "description": "Human-readable name of the capability.", + "type": "string" }, - "id": { - "type": "string", - "description": "Identifier of this capability, unique at the level in which this capability is defined." + "verification_condition": { + "$ref": "CapabilityVerificationCondition.json", + "description": "Condition required in order to verify the capability." } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/ParticipantCapabilityDefinition.json", - "description": "monitoring.uss_qualifier.reports.capability_definitions.ParticipantCapabilityDefinition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "required": [ "description", "id", "name", "verification_condition" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/capability_definitions/RequirementsCheckedCondition.json b/schemas/monitoring/uss_qualifier/reports/capability_definitions/RequirementsCheckedCondition.json index e1b6cbb2ce..9f08ba19fd 100644 --- a/schemas/monitoring/uss_qualifier/reports/capability_definitions/RequirementsCheckedCondition.json +++ b/schemas/monitoring/uss_qualifier/reports/capability_definitions/RequirementsCheckedCondition.json @@ -1,19 +1,19 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/RequirementsCheckedCondition.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Condition will only be satisfied if at least one successful check exists for all specified requirements.\n\nNote that an empty collection of requirements will result in an unsuccessful evaluation.\n\nmonitoring.uss_qualifier.reports.capability_definitions.RequirementsCheckedCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "checked": { "$ref": "../../requirements/definitions/RequirementCollection.json", "description": "Each requirement contained within this collection must be covered by at least one successful check." } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/RequirementsCheckedCondition.json", - "description": "Condition will only be satisfied if at least one successful check exists for all specified requirements.\n\nNote that an empty collection of requirements will result in an unsuccessful evaluation.\n\nmonitoring.uss_qualifier.reports.capability_definitions.RequirementsCheckedCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py", "required": [ "checked" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/capability_definitions/SpecificCondition.json b/schemas/monitoring/uss_qualifier/reports/capability_definitions/SpecificCondition.json deleted file mode 100644 index cbdd5e8c69..0000000000 --- a/schemas/monitoring/uss_qualifier/reports/capability_definitions/SpecificCondition.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" - } - }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/capability_definitions/SpecificCondition.json", - "description": "monitoring.uss_qualifier.reports.capability_definitions.SpecificCondition, as defined in monitoring/uss_qualifier/reports/capability_definitions.py" -} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/AllConditionsEvaluationReport.json b/schemas/monitoring/uss_qualifier/reports/report/AllConditionsEvaluationReport.json index 8add1360f2..f5532226bb 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/AllConditionsEvaluationReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/AllConditionsEvaluationReport.json @@ -1,30 +1,30 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/AllConditionsEvaluationReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Result of an evaluation of AllConditions determined by whether all the subconditions are satisfied.\n\nmonitoring.uss_qualifier.reports.report.AllConditionsEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "satisfied_conditions": { - "type": "array", + "description": "All of the conditions that were satisfied (there must be at least one).", "items": { "$ref": "ParticipantCapabilityConditionEvaluationReport.json" }, - "description": "All of the conditions that were satisfied (there must be at least one)." + "type": "array" }, "unsatisfied_conditions": { - "type": "array", + "description": "All of the conditions that were unsatisfied (if any, then this condition will not be satisfied).", "items": { "$ref": "ParticipantCapabilityConditionEvaluationReport.json" }, - "description": "All of the conditions that were unsatisfied (if any, then this condition will not be satisfied)." + "type": "array" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/AllConditionsEvaluationReport.json", - "description": "Result of an evaluation of AllConditions determined by whether all the subconditions are satisfied.\n\nmonitoring.uss_qualifier.reports.report.AllConditionsEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "satisfied_conditions", "unsatisfied_conditions" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/AnyConditionEvaluationReport.json b/schemas/monitoring/uss_qualifier/reports/report/AnyConditionEvaluationReport.json index 839158a0d9..b665ee7a0c 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/AnyConditionEvaluationReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/AnyConditionEvaluationReport.json @@ -1,30 +1,30 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/AnyConditionEvaluationReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Result of an evaluation of AnyCondition determined by whether any of the subconditions are satisfied.\n\nmonitoring.uss_qualifier.reports.report.AnyConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "satisfied_options": { - "type": "array", + "description": "Which of the specified options were satisfied (if any were satisfied, then this condition should be satisfied).", "items": { "$ref": "ParticipantCapabilityConditionEvaluationReport.json" }, - "description": "Which of the specified options were satisfied (if any were satisfied, then this condition should be satisfied)." + "type": "array" }, "unsatisfied_options": { - "type": "array", + "description": "Which of the specified options were not satisfied (these are informational only and do not affect the evaluation).", "items": { "$ref": "ParticipantCapabilityConditionEvaluationReport.json" }, - "description": "Which of the specified options were not satisfied (these are informational only and do not affect the evaluation)." + "type": "array" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/AnyConditionEvaluationReport.json", - "description": "Result of an evaluation of AnyCondition determined by whether any of the subconditions are satisfied.\n\nmonitoring.uss_qualifier.reports.report.AnyConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "satisfied_options", "unsatisfied_options" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/CapabilityVerifiedConditionEvaluationReport.json b/schemas/monitoring/uss_qualifier/reports/report/CapabilityVerifiedConditionEvaluationReport.json index c8e8f0aacb..4281914709 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/CapabilityVerifiedConditionEvaluationReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/CapabilityVerifiedConditionEvaluationReport.json @@ -1,38 +1,38 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/CapabilityVerifiedConditionEvaluationReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Result of an evaluation of a CapabilityVerifiedCondition dependent on whether other capabilities were verified.\n\nmonitoring.uss_qualifier.reports.report.CapabilityVerifiedConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "checked_capabilities": { - "type": "array", + "description": "All capability evaluations checked for this condition.", "items": { "$ref": "CheckedCapability.json" }, - "description": "All capability evaluations checked for this condition." + "type": "array" }, - "spurious_matches": { - "type": "array", + "missing_capabilities": { + "description": "Capabilities specified for this condition but not found in the report.", "items": { - "$ref": "SpuriousReportMatch.json" + "type": "string" }, - "description": "Report elements matching the condition's `capability_location`, but not of the type TestSuiteReport." + "type": "array" }, - "missing_capabilities": { - "type": "array", + "spurious_matches": { + "description": "Report elements matching the condition's `capability_location`, but not of the type TestSuiteReport.", "items": { - "type": "string" + "$ref": "SpuriousReportMatch.json" }, - "description": "Capabilities specified for this condition but not found in the report." + "type": "array" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/CapabilityVerifiedConditionEvaluationReport.json", - "description": "Result of an evaluation of a CapabilityVerifiedCondition dependent on whether other capabilities were verified.\n\nmonitoring.uss_qualifier.reports.report.CapabilityVerifiedConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "checked_capabilities", "missing_capabilities", "spurious_matches" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/CheckedCapability.json b/schemas/monitoring/uss_qualifier/reports/report/CheckedCapability.json index fe606c3165..45786a5471 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/CheckedCapability.json +++ b/schemas/monitoring/uss_qualifier/reports/report/CheckedCapability.json @@ -1,34 +1,34 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/CheckedCapability.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Existing/previous participant-verifiable capability upon which a CapabilityVerifiedCondition depends.\n\nmonitoring.uss_qualifier.reports.report.CheckedCapability, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, - "capability_verified": { - "type": "boolean", - "description": "Whether this capability was successfully verified" + "capability_id": { + "description": "ID of the existing/previous participant-verifiable capability.", + "type": "string" }, "capability_location": { - "type": "string", - "description": "The location of the ParticipantCapabilityConditionEvaluationReport for the capability, relative to the TestSuiteReport in which this checked requirement is located." + "description": "The location of the ParticipantCapabilityConditionEvaluationReport for the capability, relative to the TestSuiteReport in which this checked requirement is located.", + "type": "string" }, - "report_location": { - "type": "string", - "description": "Location of the ParticipantCapabilityEvaluationReport for the existing/previous capability, relative to the TestSuiteReport in which the CapabilityVerifiedConditionEvaluationReport containing this CheckedCapability is located." + "capability_verified": { + "description": "Whether this capability was successfully verified", + "type": "boolean" }, - "capability_id": { - "type": "string", - "description": "ID of the existing/previous participant-verifiable capability." + "report_location": { + "description": "Location of the ParticipantCapabilityEvaluationReport for the existing/previous capability, relative to the TestSuiteReport in which the CapabilityVerifiedConditionEvaluationReport containing this CheckedCapability is located.", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/CheckedCapability.json", - "description": "Existing/previous participant-verifiable capability upon which a CapabilityVerifiedCondition depends.\n\nmonitoring.uss_qualifier.reports.report.CheckedCapability, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "capability_id", "capability_location", "capability_verified", "report_location" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/CheckedRequirement.json b/schemas/monitoring/uss_qualifier/reports/report/CheckedRequirement.json index f9d77994cd..b3bfdc0374 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/CheckedRequirement.json +++ b/schemas/monitoring/uss_qualifier/reports/report/CheckedRequirement.json @@ -1,35 +1,35 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/CheckedRequirement.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "A single requirement being checked for participant-verifiable capability verification.\n\nmonitoring.uss_qualifier.reports.report.CheckedRequirement, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, - "passed_checks": { - "type": "array", + "failed_checks": { + "description": "The location of each PassedCheck involving the requirement of interest, relative to the TestSuiteReport in which the RequirementsCheckedConditionEvaluationReport containing this checked requirement is located.", "items": { "type": "string" }, - "description": "The location of each PassedCheck involving the requirement of interest, relative to the TestSuiteReport in which the RequirementsCheckedConditionEvaluationReport containing this checked requirement is located." + "type": "array" }, - "failed_checks": { - "type": "array", + "passed_checks": { + "description": "The location of each PassedCheck involving the requirement of interest, relative to the TestSuiteReport in which the RequirementsCheckedConditionEvaluationReport containing this checked requirement is located.", "items": { "type": "string" }, - "description": "The location of each PassedCheck involving the requirement of interest, relative to the TestSuiteReport in which the RequirementsCheckedConditionEvaluationReport containing this checked requirement is located." + "type": "array" }, "requirement_id": { - "type": "string", - "description": "The requirement being checked." + "description": "The requirement being checked.", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/CheckedRequirement.json", - "description": "A single requirement being checked for participant-verifiable capability verification.\n\nmonitoring.uss_qualifier.reports.report.CheckedRequirement, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "failed_checks", "passed_checks", "requirement_id" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/ErrorReport.json b/schemas/monitoring/uss_qualifier/reports/report/ErrorReport.json index 45a2bdb43e..1f118c6be4 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/ErrorReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/ErrorReport.json @@ -1,35 +1,35 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/ErrorReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.reports.report.ErrorReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "message": { - "type": "string", - "description": "Error message" + "description": "Error message", + "type": "string" }, "stacktrace": { - "type": "string", - "description": "Full stack trace of error" - }, - "type": { - "type": "string", - "description": "Type of error" + "description": "Full stack trace of error", + "type": "string" }, "timestamp": { - "type": "string", + "description": "Time at which the error was logged", "format": "date-time", - "description": "Time at which the error was logged" + "type": "string" + }, + "type": { + "description": "Type of error", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/ErrorReport.json", - "description": "monitoring.uss_qualifier.reports.report.ErrorReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "message", "stacktrace", "timestamp", "type" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/FailedCheck.json b/schemas/monitoring/uss_qualifier/reports/report/FailedCheck.json index 61c8749194..5e8de07a6b 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/FailedCheck.json +++ b/schemas/monitoring/uss_qualifier/reports/report/FailedCheck.json @@ -1,76 +1,75 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/FailedCheck.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.reports.report.FailedCheck, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, - "name": { - "type": "string", - "description": "Name of the check that failed" + "additional_data": { + "description": "Additional data, structured according to the checks' needs, that may be relevant for understanding this failed check", + "type": [ + "object", + "null" + ] + }, + "details": { + "description": "Human-readable description of the issue", + "type": "string" }, "documentation_url": { - "type": "string", - "description": "URL at which the check which failed is described" + "description": "URL at which the check which failed is described", + "type": "string" }, - "summary": { - "type": "string", - "description": "Human-readable summary of the issue" + "name": { + "description": "Name of the check that failed", + "type": "string" }, - "details": { - "type": "string", - "description": "Human-readable description of the issue" + "participants": { + "description": "Participants that may not meet the relevant requirements due to this failed check", + "items": { + "type": "string" + }, + "type": "array" }, "query_report_timestamps": { + "description": "List of the `report` timestamp field for queries relevant to this failed check", + "items": { + "type": "string" + }, "type": [ "array", "null" - ], + ] + }, + "requirements": { + "description": "Requirements that are not met due to this failed check", "items": { "type": "string" }, - "description": "List of the `report` timestamp field for queries relevant to this failed check" + "type": "array" }, "severity": { - "type": "string", + "description": "How severe the issue is", "enum": [ "Critical", "High", "Medium", "Low" ], - "description": "How severe the issue is" - }, - "participants": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Participants that may not meet the relevant requirements due to this failed check" + "type": "string" }, - "requirements": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Requirements that are not met due to this failed check" + "summary": { + "description": "Human-readable summary of the issue", + "type": "string" }, "timestamp": { - "type": "string", + "description": "Time the issue was discovered", "format": "date-time", - "description": "Time the issue was discovered" - }, - "additional_data": { - "type": [ - "object", - "null" - ], - "description": "Additional data, structured according to the checks' needs, that may be relevant for understanding this failed check" + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/FailedCheck.json", - "description": "monitoring.uss_qualifier.reports.report.FailedCheck, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "details", "documentation_url", @@ -80,5 +79,6 @@ "severity", "summary", "timestamp" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/NoFailedChecksConditionEvaluationReport.json b/schemas/monitoring/uss_qualifier/reports/report/NoFailedChecksConditionEvaluationReport.json index b62155425d..7bb9b8a051 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/NoFailedChecksConditionEvaluationReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/NoFailedChecksConditionEvaluationReport.json @@ -1,22 +1,22 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/NoFailedChecksConditionEvaluationReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Result of an evaluation of NoFailedChecksCondition dependent on whether any checks failed within the scope of the test suite in which this condition is located.\n\nmonitoring.uss_qualifier.reports.report.NoFailedChecksConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "failed_checks": { - "type": "array", + "description": "The location of each FailedCheck, relative to the TestSuiteReport in which this report is located.", "items": { "type": "string" }, - "description": "The location of each FailedCheck, relative to the TestSuiteReport in which this report is located." + "type": "array" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/NoFailedChecksConditionEvaluationReport.json", - "description": "Result of an evaluation of NoFailedChecksCondition dependent on whether any checks failed within the scope of the test suite in which this condition is located.\n\nmonitoring.uss_qualifier.reports.report.NoFailedChecksConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "failed_checks" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/Note.json b/schemas/monitoring/uss_qualifier/reports/report/Note.json index 6ff6ddebe2..17d8d576e5 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/Note.json +++ b/schemas/monitoring/uss_qualifier/reports/report/Note.json @@ -1,23 +1,23 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/Note.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.reports.report.Note, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "message": { "type": "string" }, "timestamp": { - "type": "string", - "format": "date-time" + "format": "date-time", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/Note.json", - "description": "monitoring.uss_qualifier.reports.report.Note, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "message", "timestamp" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityConditionEvaluationReport.json b/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityConditionEvaluationReport.json index 1962dc4a51..fec24e6bca 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityConditionEvaluationReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityConditionEvaluationReport.json @@ -1,74 +1,74 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityConditionEvaluationReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Result of an evaluation of a condition related to whether a participant capability should be verified.\n\nExactly one field other than `condition_satisfied` must be specified.\n\nmonitoring.uss_qualifier.reports.report.ParticipantCapabilityConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, - "capability_verified": { + "all_conditions": { + "description": "When specified, the condition evaluated was AllConditions.", "oneOf": [ { "type": "null" }, { - "$ref": "CapabilityVerifiedConditionEvaluationReport.json" + "$ref": "AllConditionsEvaluationReport.json" } - ], - "description": "When specified, the condition evaluated was CapabilityVerifiedCondition." + ] }, - "no_failed_checks": { + "any_conditions": { + "description": "When specified, the condition evaluated was AnyCondition.", "oneOf": [ { "type": "null" }, { - "$ref": "NoFailedChecksConditionEvaluationReport.json" + "$ref": "AnyConditionEvaluationReport.json" } - ], - "description": "When specified, the condition evaluated was NoFailedChecksCondition." + ] }, - "condition_satisfied": { - "type": "boolean", - "description": "Whether the condition was satisfied for the relevant participant." - }, - "any_conditions": { + "capability_verified": { + "description": "When specified, the condition evaluated was CapabilityVerifiedCondition.", "oneOf": [ { "type": "null" }, { - "$ref": "AnyConditionEvaluationReport.json" + "$ref": "CapabilityVerifiedConditionEvaluationReport.json" } - ], - "description": "When specified, the condition evaluated was AnyCondition." + ] }, - "requirements_checked": { + "condition_satisfied": { + "description": "Whether the condition was satisfied for the relevant participant.", + "type": "boolean" + }, + "no_failed_checks": { + "description": "When specified, the condition evaluated was NoFailedChecksCondition.", "oneOf": [ { "type": "null" }, { - "$ref": "RequirementsCheckedConditionEvaluationReport.json" + "$ref": "NoFailedChecksConditionEvaluationReport.json" } - ], - "description": "When specified, the condition evaluated was RequirementsCheckedCondition." + ] }, - "all_conditions": { + "requirements_checked": { + "description": "When specified, the condition evaluated was RequirementsCheckedCondition.", "oneOf": [ { "type": "null" }, { - "$ref": "AllConditionsEvaluationReport.json" + "$ref": "RequirementsCheckedConditionEvaluationReport.json" } - ], - "description": "When specified, the condition evaluated was AllConditions." + ] } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityConditionEvaluationReport.json", - "description": "Result of an evaluation of a condition related to whether a participant capability should be verified.\n\nExactly one field other than `condition_satisfied` must be specified.\n\nmonitoring.uss_qualifier.reports.report.ParticipantCapabilityConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "condition_satisfied" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityEvaluationReport.json b/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityEvaluationReport.json index 26551f1915..a60c0050a9 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityEvaluationReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityEvaluationReport.json @@ -1,34 +1,34 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityEvaluationReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.reports.report.ParticipantCapabilityEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "capability_id": { + "description": "ID of capability being evaluated.", + "type": "string" }, "condition_evaluation": { "$ref": "ParticipantCapabilityConditionEvaluationReport.json", "description": "Report produced by evaluating the condition for verifying this capability." }, - "verified": { - "type": "boolean", - "description": "Whether the capability was successfully verified." - }, "participant_id": { - "type": "string", - "description": "ID of participant for which capability is being evaluated." + "description": "ID of participant for which capability is being evaluated.", + "type": "string" }, - "capability_id": { - "type": "string", - "description": "ID of capability being evaluated." + "verified": { + "description": "Whether the capability was successfully verified.", + "type": "boolean" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/ParticipantCapabilityEvaluationReport.json", - "description": "monitoring.uss_qualifier.reports.report.ParticipantCapabilityEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "capability_id", "condition_evaluation", "participant_id", "verified" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/RequirementsCheckedConditionEvaluationReport.json b/schemas/monitoring/uss_qualifier/reports/report/RequirementsCheckedConditionEvaluationReport.json index df9c8bc528..eb89d59a1a 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/RequirementsCheckedConditionEvaluationReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/RequirementsCheckedConditionEvaluationReport.json @@ -1,38 +1,38 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/RequirementsCheckedConditionEvaluationReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Result of an evaluation of RequirementsCheckedCondition dependent on whether a set of requirements were successfully checked.\n\nmonitoring.uss_qualifier.reports.report.RequirementsCheckedConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, - "passed_requirements": { - "type": "array", + "failed_requirements": { + "description": "Requirements with FailedChecks.", "items": { "$ref": "CheckedRequirement.json" }, - "description": "Requirements with only PassedChecks." + "type": "array" }, - "failed_requirements": { - "type": "array", + "passed_requirements": { + "description": "Requirements with only PassedChecks.", "items": { "$ref": "CheckedRequirement.json" }, - "description": "Requirements with FailedChecks." + "type": "array" }, "untested_requirements": { - "type": "array", + "description": "Requirements that didn't have any PassedChecks or FailedChecks within the scope of the test suite in which this condition is located.", "items": { "type": "string" }, - "description": "Requirements that didn't have any PassedChecks or FailedChecks within the scope of the test suite in which this condition is located." + "type": "array" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/RequirementsCheckedConditionEvaluationReport.json", - "description": "Result of an evaluation of RequirementsCheckedCondition dependent on whether a set of requirements were successfully checked.\n\nmonitoring.uss_qualifier.reports.report.RequirementsCheckedConditionEvaluationReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "failed_requirements", "passed_requirements", "untested_requirements" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/SpuriousReportMatch.json b/schemas/monitoring/uss_qualifier/reports/report/SpuriousReportMatch.json index 3b3d9740d4..2347f18862 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/SpuriousReportMatch.json +++ b/schemas/monitoring/uss_qualifier/reports/report/SpuriousReportMatch.json @@ -1,24 +1,24 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/SpuriousReportMatch.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "Participant-verifiable capability evaluations are only present in TestSuiteReports. If a CapabilityVerifiedCondition points to a report element that is not a TestSuiteReport, an instance of this class will be generated.\n\nmonitoring.uss_qualifier.reports.report.SpuriousReportMatch, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" - }, - "type": { - "type": "string", - "description": "Data type of the report element (not TestSuiteReport)." + "description": "Path to content that replaces the $ref", + "type": "string" }, "location": { - "type": "string", - "description": "Location of the non-TestSuiteReport report element matching the CapabilityVerifiedCondition's `capability_location`, relative to the TestSuiteReport in which this condition is located." + "description": "Location of the non-TestSuiteReport report element matching the CapabilityVerifiedCondition's `capability_location`, relative to the TestSuiteReport in which this condition is located.", + "type": "string" + }, + "type": { + "description": "Data type of the report element (not TestSuiteReport).", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/SpuriousReportMatch.json", - "description": "Participant-verifiable capability evaluations are only present in TestSuiteReports. If a CapabilityVerifiedCondition points to a report element that is not a TestSuiteReport, an instance of this class will be generated.\n\nmonitoring.uss_qualifier.reports.report.SpuriousReportMatch, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "location", "type" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/TestStepReport.json b/schemas/monitoring/uss_qualifier/reports/report/TestStepReport.json index 658b1297c0..7f48438c72 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/TestStepReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/TestStepReport.json @@ -1,64 +1,64 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/TestStepReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.reports.report.TestStepReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" - }, - "failed_checks": { - "type": "array", - "items": { - "$ref": "FailedCheck.json" - }, - "description": "The checks which failed in this test step" - }, - "name": { - "type": "string", - "description": "Name of this test step" + "description": "Path to content that replaces the $ref", + "type": "string" }, "documentation_url": { - "type": "string", - "description": "URL at which this test step is described" + "description": "URL at which this test step is described", + "type": "string" }, "end_time": { + "description": "Time at which the test step completed or encountered an error", + "format": "date-time", "type": [ "string", "null" - ], - "format": "date-time", - "description": "Time at which the test step completed or encountered an error" + ] }, - "queries": { - "type": [ - "array", - "null" - ], + "failed_checks": { + "description": "The checks which failed in this test step", "items": { - "$ref": "../../../monitorlib/fetch/Query.json" + "$ref": "FailedCheck.json" }, - "description": "Description of HTTP requests relevant to this issue" + "type": "array" }, - "start_time": { - "type": "string", - "format": "date-time", - "description": "Time at which the test step started" + "name": { + "description": "Name of this test step", + "type": "string" }, "passed_checks": { - "type": "array", + "description": "The checks which successfully passed in this test step", "items": { "$ref": "PassedCheck.json" }, - "description": "The checks which successfully passed in this test step" + "type": "array" + }, + "queries": { + "description": "Description of HTTP requests relevant to this issue", + "items": { + "$ref": "../../../monitorlib/fetch/Query.json" + }, + "type": [ + "array", + "null" + ] + }, + "start_time": { + "description": "Time at which the test step started", + "format": "date-time", + "type": "string" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/TestStepReport.json", - "description": "monitoring.uss_qualifier.reports.report.TestStepReport, as defined in monitoring/uss_qualifier/reports/report.py", "required": [ "documentation_url", "failed_checks", "name", "passed_checks", "start_time" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/reports/report/TestSuiteActionReport.json b/schemas/monitoring/uss_qualifier/reports/report/TestSuiteActionReport.json index 612cf3e094..41becebf3c 100644 --- a/schemas/monitoring/uss_qualifier/reports/report/TestSuiteActionReport.json +++ b/schemas/monitoring/uss_qualifier/reports/report/TestSuiteActionReport.json @@ -1,34 +1,36 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/TestSuiteActionReport.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.reports.report.TestSuiteActionReport, as defined in monitoring/uss_qualifier/reports/report.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, - "test_scenario": { + "action_generator": { + "description": "If this action was an action generator, this field will hold its report", "oneOf": [ { "type": "null" }, { - "$ref": "TestScenarioReport.json" + "$ref": "ActionGeneratorReport.json" } - ], - "description": "If this action was a test scenario, this field will hold its report" + ] }, - "action_generator": { + "test_scenario": { + "description": "If this action was a test scenario, this field will hold its report", "oneOf": [ { "type": "null" }, { - "$ref": "ActionGeneratorReport.json" + "$ref": "TestScenarioReport.json" } - ], - "description": "If this action was an action generator, this field will hold its report" + ] }, "test_suite": { + "description": "If this action was a test suite, this field will hold its report", "oneOf": [ { "type": "null" @@ -36,10 +38,8 @@ { "$ref": "TestSuiteReport.json" } - ], - "description": "If this action was a test suite, this field will hold its report" + ] } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/reports/report/TestSuiteActionReport.json", - "description": "monitoring.uss_qualifier.reports.report.TestSuiteActionReport, as defined in monitoring/uss_qualifier/reports/report.py" + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/requirements/definitions/RequirementCollection.json b/schemas/monitoring/uss_qualifier/requirements/definitions/RequirementCollection.json index 406db7992c..66d5cf2a0b 100644 --- a/schemas/monitoring/uss_qualifier/requirements/definitions/RequirementCollection.json +++ b/schemas/monitoring/uss_qualifier/requirements/definitions/RequirementCollection.json @@ -1,53 +1,53 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/requirements/definitions/RequirementCollection.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.requirements.definitions.RequirementCollection, as defined in monitoring/uss_qualifier/requirements/definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "exclude": { + "description": "This collection does not include any of these requirements, despite all previous fields.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "RequirementCollection.json" + } + ] }, "requirement_collections": { - "type": [ - "array", - "null" - ], + "description": "This collection includes all of the requirements in all of these requirement collections.", "items": { "$ref": "RequirementCollection.json" }, - "description": "This collection includes all of the requirements in all of these requirement collections." - }, - "requirements": { "type": [ "array", "null" - ], + ] + }, + "requirement_sets": { + "description": "This collection includes all requirements in all of these requirement sets.", "items": { "type": "string" }, - "description": "This collection includes all of these requirements." - }, - "requirement_sets": { "type": [ "array", "null" - ], + ] + }, + "requirements": { + "description": "This collection includes all of these requirements.", "items": { "type": "string" }, - "description": "This collection includes all requirements in all of these requirement sets." - }, - "exclude": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "RequirementCollection.json" - } - ], - "description": "This collection does not include any of these requirements, despite all previous fields." + "type": [ + "array", + "null" + ] } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/requirements/definitions/RequirementCollection.json", - "description": "monitoring.uss_qualifier.requirements.definitions.RequirementCollection, as defined in monitoring/uss_qualifier/requirements/definitions.py" + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstanceSpecification.json b/schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstanceSpecification.json new file mode 100644 index 0000000000..c49140ffa8 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstanceSpecification.json @@ -0,0 +1,47 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstanceSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.astm.f3411.dss.DSSInstanceSpecification, as defined in monitoring/uss_qualifier/resources/astm/f3411/dss.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "base_url": { + "description": "Base URL for the DSS instance according to the ASTM F3411 API appropriate to the specified rid_version", + "type": "string" + }, + "has_private_address": { + "description": "Whether this DSS instance is expected to have a private address that is not publicly addressable.", + "type": [ + "boolean", + "null" + ] + }, + "local_debug": { + "description": "Whether this DSS instance is running locally for debugging or development purposes. Mostly used for relaxing\nconstraints around encryption.\nAssumed to be true if left unspecified and has_private_address is true, otherwise defaults to false", + "type": [ + "boolean", + "null" + ] + }, + "participant_id": { + "description": "ID of the USS responsible for this DSS instance", + "type": "string" + }, + "rid_version": { + "description": "Version of ASTM F3411 implemented by this DSS instance", + "enum": [ + "F3411-19", + "F3411-22a" + ], + "type": "string" + } + }, + "required": [ + "base_url", + "participant_id", + "rid_version" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/geo/Polygon.json b/schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstancesSpecification.json similarity index 50% rename from schemas/monitoring/monitorlib/geo/Polygon.json rename to schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstancesSpecification.json index c7bb037d14..2644fe76b4 100644 --- a/schemas/monitoring/monitorlib/geo/Polygon.json +++ b/schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstancesSpecification.json @@ -1,21 +1,21 @@ { - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/geo/Polygon.json", + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/astm/f3411/dss/DSSInstancesSpecification.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "monitoring.monitorlib.geo.Polygon, as defined in monitoring/monitorlib/geo.py", + "description": "monitoring.uss_qualifier.resources.astm.f3411.dss.DSSInstancesSpecification, as defined in monitoring/uss_qualifier/resources/astm/f3411/dss.py", "properties": { "$ref": { "description": "Path to content that replaces the $ref", "type": "string" }, - "vertices": { + "dss_instances": { "items": { - "$ref": "LatLngPoint.json" + "$ref": "DSSInstanceSpecification.json" }, "type": "array" } }, "required": [ - "vertices" + "dss_instances" ], "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json b/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json new file mode 100644 index 0000000000..9e6338eade --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json @@ -0,0 +1,31 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstanceSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.astm.f3548.v21.dss.DSSInstanceSpecification, as defined in monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "base_url": { + "description": "Base URL for the DSS instance according to the ASTM F3548-21 API", + "type": "string" + }, + "has_private_address": { + "description": "Whether this DSS instance is expected to have a private address that is not publicly addressable.", + "type": [ + "boolean", + "null" + ] + }, + "participant_id": { + "description": "ID of the USS responsible for this DSS instance", + "type": "string" + } + }, + "required": [ + "base_url", + "participant_id" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstancesSpecification.json b/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstancesSpecification.json new file mode 100644 index 0000000000..854fe6df55 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstancesSpecification.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/astm/f3548/v21/dss/DSSInstancesSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.astm.f3548.v21.dss.DSSInstancesSpecification, as defined in monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "dss_instances": { + "items": { + "$ref": "DSSInstanceSpecification.json" + }, + "type": "array" + } + }, + "required": [ + "dss_instances" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/communications/auth_adapter/AuthAdapterSpecification.json b/schemas/monitoring/uss_qualifier/resources/communications/auth_adapter/AuthAdapterSpecification.json new file mode 100644 index 0000000000..aca02c7a45 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/communications/auth_adapter/AuthAdapterSpecification.json @@ -0,0 +1,26 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/communications/auth_adapter/AuthAdapterSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Specification for an AuthAdapter resource.\n\nExactly one of the fields defined must be populated.\n\nmonitoring.uss_qualifier.resources.communications.auth_adapter.AuthAdapterSpecification, as defined in monitoring/uss_qualifier/resources/communications/auth_adapter.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "auth_spec": { + "description": "Literal representation of auth spec. WARNING: Specifying this directly may cause sensitive information to be included in reports and unprotected configuration files.", + "type": [ + "string", + "null" + ] + }, + "environment_variable_containing_auth_spec": { + "description": "Name of environment variable containing the auth spec. This is the preferred method of providing the auth spec.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/definitions/ResourceCollection.json b/schemas/monitoring/uss_qualifier/resources/definitions/ResourceCollection.json index ddd017ba45..87fa54a430 100644 --- a/schemas/monitoring/uss_qualifier/resources/definitions/ResourceCollection.json +++ b/schemas/monitoring/uss_qualifier/resources/definitions/ResourceCollection.json @@ -1,28 +1,28 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/definitions/ResourceCollection.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.resources.definitions.ResourceCollection, as defined in monitoring/uss_qualifier/resources/definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "resource_declarations": { - "type": "object", + "additionalProperties": { + "$ref": "ResourceDeclaration.json" + }, + "description": "Mapping of globally (within resource collection) unique name identifying a resource to the declaration of that resource", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" } }, - "additionalProperties": { - "$ref": "ResourceDeclaration.json" - }, - "description": "Mapping of globally (within resource collection) unique name identifying a resource to the declaration of that resource" + "type": "object" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/definitions/ResourceCollection.json", - "description": "monitoring.uss_qualifier.resources.definitions.ResourceCollection, as defined in monitoring/uss_qualifier/resources/definitions.py", "required": [ "resource_declarations" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json b/schemas/monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json index 8399217fd9..1e6eb6bbda 100644 --- a/schemas/monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json +++ b/schemas/monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json @@ -1,36 +1,36 @@ { + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", + "description": "monitoring.uss_qualifier.resources.definitions.ResourceDeclaration, as defined in monitoring/uss_qualifier/resources/definitions.py", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" }, "dependencies": { - "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Mapping of dependency parameter (additional argument to concrete resource constructor) to `name` of resource to use", "properties": { "$ref": { - "type": "string", - "description": "Path to content that replaces the $ref" + "description": "Path to content that replaces the $ref", + "type": "string" } }, - "additionalProperties": { - "type": "string" - }, - "description": "Mapping of dependency parameter (additional argument to concrete resource constructor) to `name` of resource to use" + "type": "object" }, "resource_type": { - "type": "string", - "description": "Type of resource, expressed as a Python class name qualified relative to this `resources` module" + "description": "Type of resource, expressed as a Python class name qualified relative to this `resources` module", + "type": "string" }, "specification": { - "type": "object", - "description": "Specification of resource; format is the SpecificationType that corresponds to the `resource_type`" + "description": "Specification of resource; format is the SpecificationType that corresponds to the `resource_type`", + "type": "object" } }, - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json", - "description": "monitoring.uss_qualifier.resources.definitions.ResourceDeclaration, as defined in monitoring/uss_qualifier/resources/definitions.py", "required": [ "resource_type" - ] + ], + "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/dev/noop/NoOpSpecification.json b/schemas/monitoring/uss_qualifier/resources/dev/noop/NoOpSpecification.json new file mode 100644 index 0000000000..652585a2ca --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/dev/noop/NoOpSpecification.json @@ -0,0 +1,19 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/dev/noop/NoOpSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.dev.noop.NoOpSpecification, as defined in monitoring/uss_qualifier/resources/dev/noop.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "sleep_secs": { + "description": "Duration for which to sleep, expressed in seconds.", + "type": "integer" + } + }, + "required": [ + "sleep_secs" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/eurocae/ed269/source_document/SourceDocumentSpecification.json b/schemas/monitoring/uss_qualifier/resources/eurocae/ed269/source_document/SourceDocumentSpecification.json new file mode 100644 index 0000000000..319dcd6a31 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/eurocae/ed269/source_document/SourceDocumentSpecification.json @@ -0,0 +1,19 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/eurocae/ed269/source_document/SourceDocumentSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.eurocae.ed269.source_document.SourceDocumentSpecification, as defined in monitoring/uss_qualifier/resources/eurocae/ed269/source_document.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "url": { + "description": "Url of the ED-269 document to verify", + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/files/ExternalFile.json b/schemas/monitoring/uss_qualifier/resources/files/ExternalFile.json new file mode 100644 index 0000000000..bbc6cff8d8 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/files/ExternalFile.json @@ -0,0 +1,26 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/files/ExternalFile.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.files.ExternalFile, as defined in monitoring/uss_qualifier/resources/files.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "hash_sha512": { + "description": "SHA-512 hash of the external file.\n\nIf specified, the external file's content will be verified to have this hash or else produce an error.\n\nIf not specified, will be populated with the hash of the external file at the time of execution.", + "type": [ + "string", + "null" + ] + }, + "path": { + "description": "Location of the external file.", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_intent/FlightIntentsSpecification.json b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_intent/FlightIntentsSpecification.json new file mode 100644 index 0000000000..6c391acfbd --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_intent/FlightIntentsSpecification.json @@ -0,0 +1,25 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_intent/FlightIntentsSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.flight_planning.flight_intent.FlightIntentsSpecification, as defined in monitoring/uss_qualifier/resources/flight_planning/flight_intent.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "file": { + "$ref": "../../files/ExternalFile.json", + "description": "Location of file to load" + }, + "planning_time": { + "description": "Time delta between the time uss_qualifier initiates this FlightInjectionAttempt and when a timestamp within the test_injection equal to reference_time occurs", + "format": "duration", + "type": "string" + } + }, + "required": [ + "file", + "planning_time" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json new file mode 100644 index 0000000000..13e2532ed0 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json @@ -0,0 +1,31 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planner/FlightPlannerConfiguration.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.flight_planning.flight_planner.FlightPlannerConfiguration, as defined in monitoring/uss_qualifier/resources/flight_planning/flight_planner.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "injection_base_url": { + "description": "Base URL for the flight planner's implementation of the interfaces/automated-testing/scd/scd.yaml API", + "type": "string" + }, + "participant_id": { + "description": "ID of the flight planner into which test data can be injected", + "type": "string" + }, + "timeout_seconds": { + "description": "Number of seconds to allow for requests to this flight planner. If None, use default.", + "type": [ + "number", + "null" + ] + } + }, + "required": [ + "injection_base_url", + "participant_id" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerCombinationSelectorSpecification.json b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerCombinationSelectorSpecification.json new file mode 100644 index 0000000000..266c94ec48 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerCombinationSelectorSpecification.json @@ -0,0 +1,38 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerCombinationSelectorSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.flight_planning.flight_planners.FlightPlannerCombinationSelectorSpecification, as defined in monitoring/uss_qualifier/resources/flight_planning/flight_planners.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "maximum_roles": { + "additionalProperties": { + "type": "integer" + }, + "description": "Maximum number of roles a particular participant may fill in any given combination", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + } + }, + "type": [ + "object", + "null" + ] + }, + "must_include": { + "description": "The set of flight planners which must be included in every combination", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerSpecification.json b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerSpecification.json new file mode 100644 index 0000000000..991d50c7cf --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerSpecification.json @@ -0,0 +1,18 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannerSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.flight_planning.flight_planners.FlightPlannerSpecification, as defined in monitoring/uss_qualifier/resources/flight_planning/flight_planners.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "flight_planner": { + "$ref": "../flight_planner/FlightPlannerConfiguration.json" + } + }, + "required": [ + "flight_planner" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannersSpecification.json b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannersSpecification.json new file mode 100644 index 0000000000..00c2169527 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannersSpecification.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/flight_planning/flight_planners/FlightPlannersSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.flight_planning.flight_planners.FlightPlannersSpecification, as defined in monitoring/uss_qualifier/resources/flight_planning/flight_planners.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "flight_planners": { + "items": { + "$ref": "../flight_planner/FlightPlannerConfiguration.json" + }, + "type": "array" + } + }, + "required": [ + "flight_planners" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheck.json b/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheck.json new file mode 100644 index 0000000000..7ee3137a1d --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheck.json @@ -0,0 +1,63 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheck.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.interuss.flight_authorization.definitions.FlightCheck, as defined in monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "acceptance_expectation": { + "description": "Expected outcome when authorizing a flight as described.", + "enum": [ + "MustBeRejected", + "MustBeAccepted", + "Irrelevant" + ], + "type": "string" + }, + "additional_information": { + "description": "Any additional information that should be provided to a USS planning the flight.\n\nFormat is agreed upon between test designer and USSs.", + "type": "object" + }, + "conditions_expectation": { + "description": "Expected conditions/advisories produced when authorizing a flight as described.", + "enum": [ + "Irrelevant", + "MustBePresent", + "MustBeAbsent" + ], + "type": "string" + }, + "description": { + "description": "Human-readable test step description to aid in the debugging and traceability.", + "type": "string" + }, + "flight_check_id": { + "description": "Unique (within table) test step/row identifier.", + "type": "string" + }, + "requirement_ids": { + "description": "Jurisdictional identifiers of the requirements this test step is evaluating.", + "items": { + "type": "string" + }, + "type": "array" + }, + "volumes": { + "description": "Spatial and temporal definition of the areas the virtual user intends to fly in.\n\nA service provider is expected to authorizing a flight covering the entire area specified and for any of the entire time specified.", + "items": { + "$ref": "../../../../../monitorlib/geotemporal/Volume4DTemplate.json" + }, + "type": "array" + } + }, + "required": [ + "additional_information", + "description", + "flight_check_id", + "requirement_ids", + "volumes" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheckTable.json b/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheckTable.json new file mode 100644 index 0000000000..1a4955e1a2 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheckTable.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions/FlightCheckTable.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.interuss.flight_authorization.definitions.FlightCheckTable, as defined in monitoring/uss_qualifier/resources/interuss/flight_authorization/definitions.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "rows": { + "items": { + "$ref": "FlightCheck.json" + }, + "type": "array" + } + }, + "required": [ + "rows" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table/FlightCheckTableSpecification.json b/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table/FlightCheckTableSpecification.json new file mode 100644 index 0000000000..60edbbc6b3 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table/FlightCheckTableSpecification.json @@ -0,0 +1,18 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table/FlightCheckTableSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.interuss.flight_authorization.flight_check_table.FlightCheckTableSpecification, as defined in monitoring/uss_qualifier/resources/interuss/flight_authorization/flight_check_table.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "table": { + "$ref": "../definitions/FlightCheckTable.json" + } + }, + "required": [ + "table" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheck.json b/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheck.json new file mode 100644 index 0000000000..803a730faf --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheck.json @@ -0,0 +1,64 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheck.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.interuss.geospatial_map.definitions.FeatureCheck, as defined in monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "description": { + "description": "Human-readable test step description to aid in the debugging and traceability.", + "type": "string" + }, + "expected_result": { + "description": "Expected outcome when checking map for features as described.", + "enum": [ + "Block", + "Advise", + "Neither" + ], + "type": "string" + }, + "geospatial_check_id": { + "description": "Unique (within table) test step/row identifier.", + "type": "string" + }, + "operation_rule_set": { + "description": "The set of operating rules (or rule set) under which the operation described in the feature check should be performed.", + "type": [ + "string", + "null" + ] + }, + "requirement_ids": { + "description": "Jurisdictional identifiers of the requirements this test step is evaluating.", + "items": { + "type": "string" + }, + "type": "array" + }, + "restriction_source": { + "description": "Which source for geospatial features describing restrictions should be considered when looking for the expected outcome.", + "type": [ + "string", + "null" + ] + }, + "volumes": { + "description": "Spatial and temporal definition of the areas the virtual user intends to fly in.\n\nA service provider is expected to provide geospatial features relevant to any of the entire area specified and for any of the entire time specified.", + "items": { + "$ref": "../../../../../monitorlib/geotemporal/Volume4DTemplate.json" + }, + "type": "array" + } + }, + "required": [ + "description", + "expected_result", + "geospatial_check_id", + "requirement_ids", + "volumes" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheckTable.json b/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheckTable.json new file mode 100644 index 0000000000..ece3ed5710 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheckTable.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions/FeatureCheckTable.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.interuss.geospatial_map.definitions.FeatureCheckTable, as defined in monitoring/uss_qualifier/resources/interuss/geospatial_map/definitions.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "rows": { + "items": { + "$ref": "FeatureCheck.json" + }, + "type": "array" + } + }, + "required": [ + "rows" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table/FeatureCheckTableSpecification.json b/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table/FeatureCheckTableSpecification.json new file mode 100644 index 0000000000..051a9dd672 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table/FeatureCheckTableSpecification.json @@ -0,0 +1,18 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table/FeatureCheckTableSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.interuss.geospatial_map.feature_check_table.FeatureCheckTableSpecification, as defined in monitoring/uss_qualifier/resources/interuss/geospatial_map/feature_check_table.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "table": { + "$ref": "../definitions/FeatureCheckTable.json" + } + }, + "required": [ + "table" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/id_generator/IDGeneratorSpecification.json b/schemas/monitoring/uss_qualifier/resources/interuss/id_generator/IDGeneratorSpecification.json new file mode 100644 index 0000000000..28d40d41a2 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/id_generator/IDGeneratorSpecification.json @@ -0,0 +1,24 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/id_generator/IDGeneratorSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Generated IDs contain the client's identity so the appropriate client can clean up any dangling resources.\n\nNote that this may not be necessary/important with the resolution of https://github.com/interuss/dss/issues/939\n\nTo determine the client's identity, an access token is retrieved and the subscriber is read from the obtained token.\nTherefore, the client running uss_qualifier must have the ability to obtain an access token via an auth adapter.\n\nmonitoring.uss_qualifier.resources.interuss.id_generator.IDGeneratorSpecification, as defined in monitoring/uss_qualifier/resources/interuss/id_generator.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "whoami_audience": { + "description": "Audience to request for the access token used to determine subscriber identity.", + "type": "string" + }, + "whoami_scope": { + "description": "Scope to request for the access token used to determine subscribe identity. Must be a scope that the client is\nauthorized to obtain.", + "type": "string" + } + }, + "required": [ + "whoami_audience", + "whoami_scope" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSSpecification.json b/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSSpecification.json new file mode 100644 index 0000000000..30f7b4cc0c --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSSpecification.json @@ -0,0 +1,24 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.interuss.mock_uss.client.MockUSSSpecification, as defined in monitoring/uss_qualifier/resources/interuss/mock_uss/client.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "mock_uss_base_url": { + "description": "The base URL for the mock USS.\n\nIf the mock USS had scdsc enabled, for instance, then these URLs would be\nvalid:\n * /mock/scd/uss/v1/reports\n * /scdsc/v1/status", + "type": "string" + }, + "participant_id": { + "description": "Test participant responsible for this mock USS.", + "type": "string" + } + }, + "required": [ + "mock_uss_base_url", + "participant_id" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSsSpecification.json b/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSsSpecification.json new file mode 100644 index 0000000000..382fcff6c6 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSsSpecification.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/client/MockUSSsSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.interuss.mock_uss.client.MockUSSsSpecification, as defined in monitoring/uss_qualifier/resources/interuss/mock_uss/client.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "instances": { + "items": { + "$ref": "MockUSSSpecification.json" + }, + "type": "array" + } + }, + "required": [ + "instances" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/monitorlib/dicts/RemovedElement.json b/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/locality/LocalitySpecification.json similarity index 50% rename from schemas/monitoring/monitorlib/dicts/RemovedElement.json rename to schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/locality/LocalitySpecification.json index baacca58a5..157b71ba2c 100644 --- a/schemas/monitoring/monitorlib/dicts/RemovedElement.json +++ b/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/locality/LocalitySpecification.json @@ -1,18 +1,18 @@ { - "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/monitorlib/dicts/RemovedElement.json", + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/interuss/mock_uss/locality/LocalitySpecification.json", "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "monitoring.monitorlib.dicts.RemovedElement, as defined in monitoring/monitorlib/dicts.py", + "description": "monitoring.uss_qualifier.resources.interuss.mock_uss.locality.LocalitySpecification, as defined in monitoring/uss_qualifier/resources/interuss/mock_uss/locality.py", "properties": { "$ref": { "description": "Path to content that replaces the $ref", "type": "string" }, - "address": { + "locality_code": { "type": "string" } }, "required": [ - "address" + "locality_code" ], "type": "object" } \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/evaluation/EvaluationConfiguration.json b/schemas/monitoring/uss_qualifier/resources/netrid/evaluation/EvaluationConfiguration.json new file mode 100644 index 0000000000..561bd72df7 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/evaluation/EvaluationConfiguration.json @@ -0,0 +1,30 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/evaluation/EvaluationConfiguration.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.evaluation.EvaluationConfiguration, as defined in monitoring/uss_qualifier/resources/netrid/evaluation.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "max_propagation_latency": { + "description": "Allow up to this much time for data to propagate through the system.", + "format": "duration", + "type": "string" + }, + "min_polling_interval": { + "description": "Do not repeat system observations with intervals smaller than this.", + "format": "duration", + "type": "string" + }, + "min_query_diagonal": { + "description": "Do not make queries with diagonals smaller than this many meters.", + "type": "number" + }, + "repeat_query_rect_period": { + "description": "If set to a value above zero, reuse the most recent query rectangle/view every this many queries.", + "type": "integer" + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json new file mode 100644 index 0000000000..94ad03e5e7 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json @@ -0,0 +1,51 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/AdjacentCircularFlightsSimulatorConfiguration.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.flight_data.AdjacentCircularFlightsSimulatorConfiguration, as defined in monitoring/uss_qualifier/resources/netrid/flight_data.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "altitude_of_ground_level_wgs_84": { + "type": "integer" + }, + "flight_start_shift": { + "description": "Delay generated flight starts from the reference time to spread flights over time. Expressed in seconds. Use 0 to disable.", + "type": "integer" + }, + "maxx": { + "description": "Eastern edge of bounding box (degrees longitude)", + "type": "number" + }, + "maxy": { + "description": "Northern edge of bounding box (degrees latitude)", + "type": "number" + }, + "minx": { + "description": "Western edge of bounding box (degrees longitude)", + "type": "number" + }, + "miny": { + "description": "Southern edge of bounding box (degrees latitude)", + "type": "number" + }, + "random_seed": { + "description": "Pseudorandom seed that should be used, or specify None to use default Random.", + "type": [ + "integer", + "null" + ] + }, + "reference_time": { + "description": "The reference time relative to which flight data should be generated.\n\nThe time should be irrelevant in real-world use as times are adjusted to be\nrelative to a time close to the time of test.", + "format": "date-time", + "type": "string" + }, + "utm_zone": { + "description": "UTM Zone integer for the location, see https://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system to identify the zone for the location.", + "type": "integer" + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataKMLFileConfiguration.json b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataKMLFileConfiguration.json new file mode 100644 index 0000000000..4d47c36726 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataKMLFileConfiguration.json @@ -0,0 +1,31 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataKMLFileConfiguration.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.flight_data.FlightDataKMLFileConfiguration, as defined in monitoring/uss_qualifier/resources/netrid/flight_data.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "kml_file": { + "$ref": "../../files/ExternalFile.json", + "description": "Location of KML describing a FlightRecordCollection." + }, + "random_seed": { + "description": "Pseudorandom seed that should be used, or specify None to use default Random.", + "type": [ + "integer", + "null" + ] + }, + "reference_time": { + "description": "The reference time relative to which flight data should be generated.\n\nThe time should be irrelevant in real-world use as times are adjusted to be\nrelative to a time close to the time of test.", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "kml_file" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataSpecification.json b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataSpecification.json new file mode 100644 index 0000000000..de7aaf5c68 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataSpecification.json @@ -0,0 +1,49 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/flight_data/FlightDataSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.flight_data.FlightDataSpecification, as defined in monitoring/uss_qualifier/resources/netrid/flight_data.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "adjacent_circular_flights_simulation_source": { + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "AdjacentCircularFlightsSimulatorConfiguration.json" + } + ] + }, + "flight_start_delay": { + "description": "Amount of time between starting the test and commencement of flights", + "format": "duration", + "type": "string" + }, + "kml_source": { + "description": "When this field is populated, flight data will be generated from a KML file", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "FlightDataKMLFileConfiguration.json" + } + ] + }, + "record_source": { + "description": "When this field is populated, flight record data will be loaded directly from this file", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "../../files/ExternalFile.json" + } + ] + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/flight_data_resources/FlightDataStorageSpecification.json b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data_resources/FlightDataStorageSpecification.json new file mode 100644 index 0000000000..33a4356c66 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/flight_data_resources/FlightDataStorageSpecification.json @@ -0,0 +1,26 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/flight_data_resources/FlightDataStorageSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.flight_data_resources.FlightDataStorageSpecification, as defined in monitoring/uss_qualifier/resources/netrid/flight_data_resources.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "flight_record_collection_path": { + "description": "Path, usually ending with .json, at which to store the FlightRecordCollection", + "type": [ + "string", + "null" + ] + }, + "geojson_tracks_path": { + "description": "Path (folder) in which to store track_XX.geojson files, 1 for each flight", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/observers/NetRIDObserversSpecification.json b/schemas/monitoring/uss_qualifier/resources/netrid/observers/NetRIDObserversSpecification.json new file mode 100644 index 0000000000..9275c487e7 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/observers/NetRIDObserversSpecification.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/observers/NetRIDObserversSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.observers.NetRIDObserversSpecification, as defined in monitoring/uss_qualifier/resources/netrid/observers.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "observers": { + "items": { + "$ref": "ObserverConfiguration.json" + }, + "type": "array" + } + }, + "required": [ + "observers" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/observers/ObserverConfiguration.json b/schemas/monitoring/uss_qualifier/resources/netrid/observers/ObserverConfiguration.json new file mode 100644 index 0000000000..50cc918518 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/observers/ObserverConfiguration.json @@ -0,0 +1,31 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/observers/ObserverConfiguration.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.observers.ObserverConfiguration, as defined in monitoring/uss_qualifier/resources/netrid/observers.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "local_debug": { + "description": "Whether this Observer instance is running locally for debugging or development purposes. Mostly used for relaxing\nconstraints around encryption.\nAssumed to be true if left unspecified and has_private_address is true, otherwise defaults to false", + "type": [ + "boolean", + "null" + ] + }, + "observation_base_url": { + "description": "Base URL for the observer's implementation of the interfaces/automated-testing/rid/observation.yaml API", + "type": "string" + }, + "participant_id": { + "description": "Participant ID of the observer providing a view of RID data in the system", + "type": "string" + } + }, + "required": [ + "observation_base_url", + "participant_id" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/service_area/ServiceAreaSpecification.json b/schemas/monitoring/uss_qualifier/resources/netrid/service_area/ServiceAreaSpecification.json new file mode 100644 index 0000000000..3b83058ed6 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/service_area/ServiceAreaSpecification.json @@ -0,0 +1,53 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/service_area/ServiceAreaSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.service_area.ServiceAreaSpecification, as defined in monitoring/uss_qualifier/resources/netrid/service_area.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "altitude_max": { + "description": "Upper altitude bound of service area, meters above WGS84 ellipsoid", + "type": "number" + }, + "altitude_min": { + "description": "Lower altitude bound of service area, meters above WGS84 ellipsoid", + "type": "number" + }, + "base_url": { + "description": "Base URL to use for the Identification Service Area.\n\nNote that this is the API base URL, not the flights URL (as specified in F3411-19).\n\nThis URL will probably not identify a real resource in tests.", + "type": "string" + }, + "footprint": { + "description": "2D outline of service area", + "items": { + "$ref": "../../../../monitorlib/geo/LatLngPoint.json" + }, + "type": "array" + }, + "reference_time": { + "description": "Reference time used to adjust start and end times at runtime", + "format": "date-time", + "type": "string" + }, + "time_end": { + "description": "End time of service area (relative to reference_time)", + "format": "date-time", + "type": "string" + }, + "time_start": { + "description": "Start time of service area (relative to reference_time)", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "base_url", + "footprint", + "reference_time", + "time_end", + "time_start" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/service_providers/NetRIDServiceProvidersSpecification.json b/schemas/monitoring/uss_qualifier/resources/netrid/service_providers/NetRIDServiceProvidersSpecification.json new file mode 100644 index 0000000000..cc93bc352d --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/service_providers/NetRIDServiceProvidersSpecification.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/service_providers/NetRIDServiceProvidersSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.service_providers.NetRIDServiceProvidersSpecification, as defined in monitoring/uss_qualifier/resources/netrid/service_providers.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "service_providers": { + "items": { + "$ref": "ServiceProviderConfiguration.json" + }, + "type": "array" + } + }, + "required": [ + "service_providers" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/netrid/service_providers/ServiceProviderConfiguration.json b/schemas/monitoring/uss_qualifier/resources/netrid/service_providers/ServiceProviderConfiguration.json new file mode 100644 index 0000000000..e53ee6c7d1 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/netrid/service_providers/ServiceProviderConfiguration.json @@ -0,0 +1,31 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/netrid/service_providers/ServiceProviderConfiguration.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.netrid.service_providers.ServiceProviderConfiguration, as defined in monitoring/uss_qualifier/resources/netrid/service_providers.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "injection_base_url": { + "description": "Base URL for the Service Provider's implementation of the interfaces/automated-testing/rid/injection.yaml API", + "type": "string" + }, + "local_debug": { + "description": "Whether this Service Provider instance is running locally for debugging or development purposes. Mostly used for relaxing\nconstraints around encryption.", + "type": [ + "boolean", + "null" + ] + }, + "participant_id": { + "description": "ID of the NetRID Service Provider into which test data can be injected", + "type": "string" + } + }, + "required": [ + "injection_base_url", + "participant_id" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/versioning/client/InterUSSVersionProvider.json b/schemas/monitoring/uss_qualifier/resources/versioning/client/InterUSSVersionProvider.json new file mode 100644 index 0000000000..9ebc1bbb06 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/versioning/client/InterUSSVersionProvider.json @@ -0,0 +1,19 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/versioning/client/InterUSSVersionProvider.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.versioning.client.InterUSSVersionProvider, as defined in monitoring/uss_qualifier/resources/versioning/client.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "base_url": { + "description": "The base URL at which the participant is hosting its implementation of the InterUSS automated testing versioning API.", + "type": "string" + } + }, + "required": [ + "base_url" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProviderSpecification.json b/schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProviderSpecification.json new file mode 100644 index 0000000000..bc18d1cba1 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProviderSpecification.json @@ -0,0 +1,30 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProviderSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.versioning.client.VersionProviderSpecification, as defined in monitoring/uss_qualifier/resources/versioning/client.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "interuss": { + "description": "Populated when the version provider is using the InterUSS automated testing versioning API to provide versioning information.", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "InterUSSVersionProvider.json" + } + ] + }, + "participant_id": { + "description": "Test participant providing system versions.", + "type": "string" + } + }, + "required": [ + "participant_id" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProvidersSpecification.json b/schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProvidersSpecification.json new file mode 100644 index 0000000000..6ad1492c08 --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProvidersSpecification.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/versioning/client/VersionProvidersSpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.versioning.client.VersionProvidersSpecification, as defined in monitoring/uss_qualifier/resources/versioning/client.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "instances": { + "items": { + "$ref": "VersionProviderSpecification.json" + }, + "type": "array" + } + }, + "required": [ + "instances" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/monitoring/uss_qualifier/resources/versioning/system_identity/SystemIdentitySpecification.json b/schemas/monitoring/uss_qualifier/resources/versioning/system_identity/SystemIdentitySpecification.json new file mode 100644 index 0000000000..099ef3091e --- /dev/null +++ b/schemas/monitoring/uss_qualifier/resources/versioning/system_identity/SystemIdentitySpecification.json @@ -0,0 +1,19 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/monitoring/uss_qualifier/resources/versioning/system_identity/SystemIdentitySpecification.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "monitoring.uss_qualifier.resources.versioning.system_identity.SystemIdentitySpecification, as defined in monitoring/uss_qualifier/resources/versioning/system_identity.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "system_identity": { + "description": "Identity of a system, as understood identically by the test designer and test participants.", + "type": "string" + } + }, + "required": [ + "system_identity" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/uas_standards/astm/f3411/v22a/api/LatLngPoint.json b/schemas/uas_standards/astm/f3411/v22a/api/LatLngPoint.json new file mode 100644 index 0000000000..5adcac5df8 --- /dev/null +++ b/schemas/uas_standards/astm/f3411/v22a/api/LatLngPoint.json @@ -0,0 +1,22 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/uas_standards/astm/f3411/v22a/api/LatLngPoint.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Point on the earth's surface.\n\nuas_standards.astm.f3411.v22a.api.LatLngPoint, as defined in uas_standards/astm/f3411/v22a/api.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "lat": { + "type": "number" + }, + "lng": { + "type": "number" + } + }, + "required": [ + "lat", + "lng" + ], + "type": "object" +} \ No newline at end of file diff --git a/schemas/uas_standards/astm/f3411/v22a/api/Polygon.json b/schemas/uas_standards/astm/f3411/v22a/api/Polygon.json new file mode 100644 index 0000000000..5327ab8bf1 --- /dev/null +++ b/schemas/uas_standards/astm/f3411/v22a/api/Polygon.json @@ -0,0 +1,21 @@ +{ + "$id": "https://github.com/interuss/monitoring/blob/main/schemas/uas_standards/astm/f3411/v22a/api/Polygon.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "An enclosed area on the earth. The bounding edges of this polygon are defined to be the shortest paths between connected vertices. This means, for instance, that the edge between two points both defined at a particular latitude is not generally contained at that latitude. The winding order must be interpreted as the order which produces the smaller area. The path between two vertices is defined to be the shortest possible path between those vertices. Edges may not cross. Vertices may not be duplicated. In particular, the final polygon vertex must not be identical to the first vertex.\n\nuas_standards.astm.f3411.v22a.api.Polygon, as defined in uas_standards/astm/f3411/v22a/api.py", + "properties": { + "$ref": { + "description": "Path to content that replaces the $ref", + "type": "string" + }, + "vertices": { + "items": { + "$ref": "LatLngPoint.json" + }, + "type": "array" + } + }, + "required": [ + "vertices" + ], + "type": "object" +} \ No newline at end of file From 778d8af96c1db8a55ef3eb997c605c088d30cda6 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 23 Oct 2023 09:23:30 -0700 Subject: [PATCH 04/11] [uss_qualifier] Make execution error critical (#285) Make execution error critical --- monitoring/uss_qualifier/reports/report.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/monitoring/uss_qualifier/reports/report.py b/monitoring/uss_qualifier/reports/report.py index d22cc1d005..562f3f6d6f 100644 --- a/monitoring/uss_qualifier/reports/report.py +++ b/monitoring/uss_qualifier/reports/report.py @@ -245,9 +245,13 @@ class TestScenarioReport(ImplicitDict): """If there was an error while executing this test scenario, this field describes the error""" def has_critical_problem(self) -> bool: - return any(c.has_critical_problem() for c in self.cases) or ( - "cleanup" in self and self.cleanup and self.cleanup.has_critical_problem() - ) + if any(c.has_critical_problem() for c in self.cases): + return True + if "cleanup" in self and self.cleanup and self.cleanup.has_critical_problem(): + return True + if "execution_error" in self and self.execution_error: + return True + return False def all_participants(self) -> Set[ParticipantID]: participants = set() From 463a66335ab1dc20b0f06ad40052c8fcacff5a98 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 23 Oct 2023 09:45:18 -0700 Subject: [PATCH 05/11] [mock_uss] Accept single or multiple scopes in requires_scope decorator (#281) * Accept single or multiple scopes in requires_scope decorator * `make format` --- monitoring/mock_uss/dynamic_configuration/routes.py | 2 +- monitoring/mock_uss/geoawareness/routes.py | 2 +- .../mock_uss/geoawareness/routes_geoawareness.py | 8 ++++---- .../interaction_logging/routes_interactions_log.py | 4 ++-- monitoring/mock_uss/riddp/routes_observation.py | 4 ++-- monitoring/mock_uss/ridsp/routes_injection.py | 4 ++-- monitoring/mock_uss/ridsp/routes_ridsp_v19.py | 6 +++--- monitoring/mock_uss/ridsp/routes_ridsp_v22a.py | 6 +++--- monitoring/mock_uss/scdsc/routes_injection.py | 10 +++++----- monitoring/mock_uss/scdsc/routes_scdsc.py | 4 ++-- monitoring/mock_uss/versioning/routes.py | 2 +- monitoring/monitorlib/auth_validation.py | 3 +++ 12 files changed, 29 insertions(+), 26 deletions(-) diff --git a/monitoring/mock_uss/dynamic_configuration/routes.py b/monitoring/mock_uss/dynamic_configuration/routes.py index a6cfc66a31..9aaa5b4e2f 100644 --- a/monitoring/mock_uss/dynamic_configuration/routes.py +++ b/monitoring/mock_uss/dynamic_configuration/routes.py @@ -21,7 +21,7 @@ def locality_get() -> Tuple[str, int]: @webapp.route("/configuration/locality", methods=["PUT"]) -@requires_scope([MOCK_USS_CONFIG_SCOPE]) # TODO: use separate public key for this +@requires_scope(MOCK_USS_CONFIG_SCOPE) # TODO: use separate public key for this def locality_set() -> Tuple[str, int]: """Set the locality of the mock_uss.""" try: diff --git a/monitoring/mock_uss/geoawareness/routes.py b/monitoring/mock_uss/geoawareness/routes.py index e16231089a..9d2b0aab04 100644 --- a/monitoring/mock_uss/geoawareness/routes.py +++ b/monitoring/mock_uss/geoawareness/routes.py @@ -11,7 +11,7 @@ @webapp.route("/geoawareness/status") -@requires_scope([SCOPE_GEOAWARENESS_TEST]) +@requires_scope(SCOPE_GEOAWARENESS_TEST) def geoawareness_status(): return StatusResponse( status=StatusResponseStatus.Ready, version=versioning.get_code_version() diff --git a/monitoring/mock_uss/geoawareness/routes_geoawareness.py b/monitoring/mock_uss/geoawareness/routes_geoawareness.py index da9262cf6c..aba16451fe 100644 --- a/monitoring/mock_uss/geoawareness/routes_geoawareness.py +++ b/monitoring/mock_uss/geoawareness/routes_geoawareness.py @@ -24,7 +24,7 @@ "/geoawareness/geozone_sources/", methods=["GET"], ) -@requires_scope([SCOPE_GEOAWARENESS_TEST]) +@requires_scope(SCOPE_GEOAWARENESS_TEST) def geoawareness_get_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: return get_geozone_source(geozone_source_id) @@ -33,7 +33,7 @@ def geoawareness_get_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: "/geoawareness/geozone_sources/", methods=["PUT"], ) -@requires_scope([SCOPE_GEOAWARENESS_TEST]) +@requires_scope(SCOPE_GEOAWARENESS_TEST) def geoawareness_put_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: try: json = flask.request.json @@ -55,13 +55,13 @@ def geoawareness_put_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: "/geoawareness/geozone_sources/", methods=["DELETE"], ) -@requires_scope([SCOPE_GEOAWARENESS_TEST]) +@requires_scope(SCOPE_GEOAWARENESS_TEST) def geoawareness_delete_geozone_sources(geozone_source_id: str) -> Tuple[str, int]: return delete_geozone_source(geozone_source_id) @webapp.route("/geoawareness/check", methods=["POST"]) -@requires_scope([SCOPE_GEOAWARENESS_TEST]) +@requires_scope(SCOPE_GEOAWARENESS_TEST) def geoawareness_check(): try: json = flask.request.json diff --git a/monitoring/mock_uss/interaction_logging/routes_interactions_log.py b/monitoring/mock_uss/interaction_logging/routes_interactions_log.py index 4acdb573ed..9a0e6fc6ec 100644 --- a/monitoring/mock_uss/interaction_logging/routes_interactions_log.py +++ b/monitoring/mock_uss/interaction_logging/routes_interactions_log.py @@ -19,7 +19,7 @@ @webapp.route("/mock_uss/interuss_logging/logs", methods=["GET"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) +@requires_scope(SCOPE_SCD_QUALIFIER_INJECT) def interaction_logs() -> Tuple[str, int]: """ Returns all the interaction logs with requests that were @@ -64,7 +64,7 @@ def interaction_logs() -> Tuple[str, int]: @webapp.route("/mock_uss/interuss_logging/logs", methods=["DELETE"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) +@requires_scope(SCOPE_SCD_QUALIFIER_INJECT) def delete_interaction_logs() -> Tuple[str, int]: """Deletes all the files under the logging directory""" log_path = webapp.config[KEY_INTERACTIONS_LOG_DIR] diff --git a/monitoring/mock_uss/riddp/routes_observation.py b/monitoring/mock_uss/riddp/routes_observation.py index 540977dbc3..b806f48c6d 100644 --- a/monitoring/mock_uss/riddp/routes_observation.py +++ b/monitoring/mock_uss/riddp/routes_observation.py @@ -81,7 +81,7 @@ def _make_flight_observation( @webapp.route("/riddp/observation/display_data", methods=["GET"]) -@requires_scope([Scope.Read]) +@requires_scope(Scope.Read) def riddp_display_data() -> Tuple[str, int]: """Implements retrieval of current display data per automated testing API.""" @@ -176,7 +176,7 @@ def riddp_display_data() -> Tuple[str, int]: @webapp.route("/riddp/observation/display_data/", methods=["GET"]) -@requires_scope([Scope.Read]) +@requires_scope(Scope.Read) def riddp_flight_details(flight_id: str) -> Tuple[str, int]: """Implements get flight details endpoint per automated testing API.""" tx = db.value diff --git a/monitoring/mock_uss/ridsp/routes_injection.py b/monitoring/mock_uss/ridsp/routes_injection.py index 19ae90c1d1..4c57b92576 100644 --- a/monitoring/mock_uss/ridsp/routes_injection.py +++ b/monitoring/mock_uss/ridsp/routes_injection.py @@ -33,7 +33,7 @@ class ErrorResponse(ImplicitDict): @webapp.route("/ridsp/injection/tests/", methods=["PUT"]) -@requires_scope([injection_api.SCOPE_RID_QUALIFIER_INJECT]) +@requires_scope(injection_api.SCOPE_RID_QUALIFIER_INJECT) @idempotent_request() def ridsp_create_test(test_id: str) -> Tuple[str, int]: """Implements test creation in RID automated testing injection API.""" @@ -111,7 +111,7 @@ def ridsp_create_test(test_id: str) -> Tuple[str, int]: @webapp.route("/ridsp/injection/tests//", methods=["DELETE"]) -@requires_scope([injection_api.SCOPE_RID_QUALIFIER_INJECT]) +@requires_scope(injection_api.SCOPE_RID_QUALIFIER_INJECT) def ridsp_delete_test(test_id: str, version: str) -> Tuple[str, int]: """Implements test deletion in RID automated testing injection API.""" logger.info(f"Delete test {test_id}") diff --git a/monitoring/mock_uss/ridsp/routes_ridsp_v19.py b/monitoring/mock_uss/ridsp/routes_ridsp_v19.py index fa03135028..3e67645337 100644 --- a/monitoring/mock_uss/ridsp/routes_ridsp_v19.py +++ b/monitoring/mock_uss/ridsp/routes_ridsp_v19.py @@ -94,7 +94,7 @@ def rid_v19_operation(op_id: OperationID): @rid_v19_operation(OperationID.PostIdentificationServiceArea) -@requires_scope([Scope.Write]) +@requires_scope(Scope.Write) def ridsp_notify_isa_v19(id: str): return ( flask.jsonify( @@ -105,7 +105,7 @@ def ridsp_notify_isa_v19(id: str): @rid_v19_operation(OperationID.SearchFlights) -@requires_scope([Scope.Read]) +@requires_scope(Scope.Read) def ridsp_flights_v19(): if "view" not in flask.request.args: return ( @@ -151,7 +151,7 @@ def ridsp_flights_v19(): @rid_v19_operation(OperationID.GetFlightDetails) -@requires_scope([Scope.Read]) +@requires_scope(Scope.Read) def ridsp_flight_details_v19(id: str): now = arrow.utcnow().datetime tx = db.value diff --git a/monitoring/mock_uss/ridsp/routes_ridsp_v22a.py b/monitoring/mock_uss/ridsp/routes_ridsp_v22a.py index 31c4faec17..9ca6af82f4 100644 --- a/monitoring/mock_uss/ridsp/routes_ridsp_v22a.py +++ b/monitoring/mock_uss/ridsp/routes_ridsp_v22a.py @@ -152,7 +152,7 @@ def rid_v22a_operation(op_id: OperationID): @rid_v22a_operation(OperationID.PostIdentificationServiceArea) -@requires_scope([Scope.ServiceProvider]) +@requires_scope(Scope.ServiceProvider) def ridsp_notify_isa_v22a(id: str): return ( flask.jsonify( @@ -163,7 +163,7 @@ def ridsp_notify_isa_v22a(id: str): @rid_v22a_operation(OperationID.SearchFlights) -@requires_scope([Scope.DisplayProvider]) +@requires_scope(Scope.DisplayProvider) def ridsp_flights_v22a(): if "view" not in flask.request.args: return ( @@ -227,7 +227,7 @@ def ridsp_flights_v22a(): @rid_v22a_operation(OperationID.GetFlightDetails) -@requires_scope([Scope.DisplayProvider]) +@requires_scope(Scope.DisplayProvider) def ridsp_flight_details_v22a(id: str): now = arrow.utcnow().datetime tx = db.value diff --git a/monitoring/mock_uss/scdsc/routes_injection.py b/monitoring/mock_uss/scdsc/routes_injection.py index c857b372fa..9f9086df4a 100644 --- a/monitoring/mock_uss/scdsc/routes_injection.py +++ b/monitoring/mock_uss/scdsc/routes_injection.py @@ -112,7 +112,7 @@ def query_operational_intents( @webapp.route("/scdsc/v1/status", methods=["GET"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) +@requires_scope(SCOPE_SCD_QUALIFIER_INJECT) def scdsc_injection_status() -> Tuple[str, int]: """Implements USS status in SCD automated testing injection API.""" json, code = injection_status() @@ -127,7 +127,7 @@ def injection_status() -> Tuple[dict, int]: @webapp.route("/scdsc/v1/capabilities", methods=["GET"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) +@requires_scope(SCOPE_SCD_QUALIFIER_INJECT) def scdsc_scd_capabilities() -> Tuple[str, int]: """Implements USS capabilities in SCD automated testing injection API.""" json, code = scd_capabilities() @@ -148,7 +148,7 @@ def scd_capabilities() -> Tuple[dict, int]: @webapp.route("/scdsc/v1/flights/", methods=["PUT"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) +@requires_scope(SCOPE_SCD_QUALIFIER_INJECT) @idempotent_request() def scdsc_inject_flight(flight_id: str) -> Tuple[str, int]: """Implements flight injection in SCD automated testing injection API.""" @@ -378,7 +378,7 @@ def log(msg: str): @webapp.route("/scdsc/v1/flights/", methods=["DELETE"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) +@requires_scope(SCOPE_SCD_QUALIFIER_INJECT) def scdsc_delete_flight(flight_id: str) -> Tuple[str, int]: """Implements flight deletion in SCD automated testing injection API.""" json, code = delete_flight(flight_id) @@ -478,7 +478,7 @@ def delete_flight(flight_id) -> Tuple[dict, int]: @webapp.route("/scdsc/v1/clear_area_requests", methods=["POST"]) -@requires_scope([SCOPE_SCD_QUALIFIER_INJECT]) +@requires_scope(SCOPE_SCD_QUALIFIER_INJECT) @idempotent_request() def scdsc_clear_area() -> Tuple[str, int]: try: diff --git a/monitoring/mock_uss/scdsc/routes_scdsc.py b/monitoring/mock_uss/scdsc/routes_scdsc.py index a998b0dbba..cd955fd103 100644 --- a/monitoring/mock_uss/scdsc/routes_scdsc.py +++ b/monitoring/mock_uss/scdsc/routes_scdsc.py @@ -13,7 +13,7 @@ @webapp.route("/mock/scd/uss/v1/operational_intents/", methods=["GET"]) -@requires_scope([scd.SCOPE_SC]) +@requires_scope(scd.SCOPE_SC) def scdsc_get_operational_intent_details(entityid: str): """Implements getOperationalIntentDetails in ASTM SCD API.""" @@ -57,7 +57,7 @@ def op_intent_from_flightrecord(flight: FlightRecord) -> OperationalIntent: @webapp.route("/mock/scd/uss/v1/operational_intents", methods=["POST"]) -@requires_scope([scd.SCOPE_SC]) +@requires_scope(scd.SCOPE_SC) def scdsc_notify_operational_intent_details_changed(): """Implements notifyOperationalIntentDetailsChanged in ASTM SCD API.""" diff --git a/monitoring/mock_uss/versioning/routes.py b/monitoring/mock_uss/versioning/routes.py index 2c184f4a90..324ef48977 100644 --- a/monitoring/mock_uss/versioning/routes.py +++ b/monitoring/mock_uss/versioning/routes.py @@ -9,7 +9,7 @@ @webapp.route("/versioning/versions/", methods=["GET"]) -@requires_scope([constants.Scope.ReadSystemVersions]) +@requires_scope(constants.Scope.ReadSystemVersions) def versioning_get_version(system_identity: str) -> Tuple[str, int]: version = versioning.get_code_version() return flask.jsonify( diff --git a/monitoring/monitorlib/auth_validation.py b/monitoring/monitorlib/auth_validation.py index d065b98a04..d480756a39 100644 --- a/monitoring/monitorlib/auth_validation.py +++ b/monitoring/monitorlib/auth_validation.py @@ -40,6 +40,9 @@ def requires_scope_decorator(public_key: str, audience: str): audiences = audience.split(",") if audience else [] def decorator(permitted_scopes): + if isinstance(permitted_scopes, str): + permitted_scopes = [permitted_scopes] + def outer_wrapper(fn): @wraps(fn) def wrapper(*args, **kwargs): From 726592d62bf09ecaa9d458bade369f84446cca12 Mon Sep 17 00:00:00 2001 From: Benjamin Pelletier Date: Mon, 23 Oct 2023 10:26:20 -0700 Subject: [PATCH 06/11] [uss_qualifier] Clean up artifacts (#282) * Clean up artifacts * Fix dss_probing artifacts * Fix shell lint --- github_pages/make_site_content.sh | 4 +- github_pages/static/index.md | 32 +- .../configurations/configuration.py | 55 ++- .../configurations/dev/dss_probing.yaml | 25 +- .../dev/f3548_self_contained.yaml | 30 +- .../dev/general_flight_auth.yaml | 4 +- .../configurations/dev/geoawareness_cis.yaml | 6 +- .../dev/geospatial_comprehension.yaml | 4 +- .../configurations/dev/library/resources.yaml | 4 +- .../configurations/dev/message_signing.yaml | 4 +- .../configurations/dev/netrid_v19.yaml | 37 +- .../configurations/dev/netrid_v22a.yaml | 37 +- .../configurations/dev/noop.yaml | 8 +- .../configurations/dev/uspace.yaml | 63 ++-- monitoring/uss_qualifier/main.py | 94 ++++-- monitoring/uss_qualifier/reports/graphs.py | 317 ------------------ .../uss_qualifier/reports/sequence_view.py | 14 +- monitoring/uss_qualifier/reports/templates.py | 17 +- .../capability_evaluation_report.html | 101 ------ .../tested_roles/test_run_report.html | 12 - .../reports/tested_requirements.py | 9 +- .../uss_qualifier/reports/tested_roles.py | 217 ------------ monitoring/uss_qualifier/run_locally.sh | 14 - .../configuration/ArtifactsConfiguration.json | 59 ++-- .../configuration/GraphConfiguration.json | 19 -- .../configuration/RawReportConfiguration.json | 23 ++ .../configuration/ReportConfiguration.json | 19 -- .../ReportHTMLConfiguration.json | 9 +- .../SequenceViewConfiguration.json | 9 +- .../TemplatedReportConfiguration.json | 6 +- .../TestedRequirementsConfiguration.json | 10 +- .../TestedRolesConfiguration.json | 19 -- 32 files changed, 295 insertions(+), 986 deletions(-) delete mode 100644 monitoring/uss_qualifier/reports/graphs.py delete mode 100644 monitoring/uss_qualifier/reports/templates/tested_roles/capability_evaluation_report.html delete mode 100644 monitoring/uss_qualifier/reports/templates/tested_roles/test_run_report.html delete mode 100644 monitoring/uss_qualifier/reports/tested_roles.py delete mode 100644 schemas/monitoring/uss_qualifier/configurations/configuration/GraphConfiguration.json create mode 100644 schemas/monitoring/uss_qualifier/configurations/configuration/RawReportConfiguration.json delete mode 100644 schemas/monitoring/uss_qualifier/configurations/configuration/ReportConfiguration.json delete mode 100644 schemas/monitoring/uss_qualifier/configurations/configuration/TestedRolesConfiguration.json diff --git a/github_pages/make_site_content.sh b/github_pages/make_site_content.sh index bbb1f1f8c2..566a7462a9 100755 --- a/github_pages/make_site_content.sh +++ b/github_pages/make_site_content.sh @@ -11,6 +11,4 @@ mkdir ./public cp -r ./monitoring/github_pages/static/* ./public mkdir -p ./public/artifacts/uss_qualifier/reports -cp -r ./artifacts/uss_qualifier/output/sequence_uspace ./public/artifacts/uss_qualifier/reports/sequence_uspace -cp -r ./artifacts/uss_qualifier/output/tested_requirements_uspace ./public/artifacts/uss_qualifier/reports/tested_requirements_uspace -cp -r ./artifacts/uss_qualifier/output/capabilities_uspace.html ./public/artifacts/uss_qualifier/reports/capabilities_uspace.html +cp -r ./artifacts/uss_qualifier/output ./public/artifacts/uss_qualifier/reports diff --git a/github_pages/static/index.md b/github_pages/static/index.md index 036b0aa6c1..02cd5b3ce0 100644 --- a/github_pages/static/index.md +++ b/github_pages/static/index.md @@ -4,10 +4,32 @@ This site contains content automatically generated by actions in the [InterUSS]( ## uss_qualifier [reports](https://github.com/interuss/monitoring/tree/main/monitoring/uss_qualifier/reports) -These reports were generated during continuous integration for the most recent PR merged to the main branch. +These reports were generated during continuous integration for the most recent PR merged to the main branch. The test configurations producing this output are in [monitoring/uss_qualifier/configurations/dev](https://github.com/interuss/monitoring/tree/main/monitoring/uss_qualifier/configurations/dev). -### [U-space developer](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/uspace.yaml) [test configuration](https://github.com/interuss/monitoring/tree/main/monitoring/uss_qualifier/configurations) +### [U-space test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/uspace.yaml) -* [Sequence view](./artifacts/uss_qualifier/reports/sequence_uspace) -* [Tested requirements](./artifacts/uss_qualifier/reports/tested_requirements_uspace) -* [Demonstrated capabilities](./artifacts/uss_qualifier/reports/capabilities_uspace.html) +* [Sequence view](./artifacts/uss_qualifier/reports/uspace/sequence) +* [Tested requirements](./artifacts/uss_qualifier/reports/uspace/requirements) +* [Demonstrated capabilities](./artifacts/uss_qualifier/reports/uspace/capabilities.html) +* [Raw report](./artifacts/uss_qualifier/reports/uspace/report.json) (large) + +### [No-op test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/noop.yaml) + +* [Raw report](./artifacts/uss_qualifier/reports/noop/report.json) (indented to be human-readable) +* [Interactive report](./artifacts/uss_qualifier/reports/noop/report.html) +* [Sequence view](./artifacts/uss_qualifier/reports/noop/sequence) + +### [ASTM F3548-21 test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml) + +* [Sequence view](./artifacts/uss_qualifier/reports/f3548/sequence) +* [Tested requirements](./artifacts/uss_qualifier/reports/f3548/requirements) + +### [ASTM F3411-22a test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml) + +* [Sequence view](./artifacts/uss_qualifier/reports/netrid_v22a/sequence) +* [Tested requirements](./artifacts/uss_qualifier/reports/netrid_v22a/requirements) + +### [DSS integration test configuration](https://github.com/interuss/monitoring/blob/main/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml) + +* [Sequence view](./artifacts/uss_qualifier/reports/dss_probing/sequence) +* [Tested requirements](./artifacts/uss_qualifier/reports/dss_probing/requirements) diff --git a/monitoring/uss_qualifier/configurations/configuration.py b/monitoring/uss_qualifier/configurations/configuration.py index e3c679323e..cd6526cd2f 100644 --- a/monitoring/uss_qualifier/configurations/configuration.py +++ b/monitoring/uss_qualifier/configurations/configuration.py @@ -27,18 +27,13 @@ class TestConfiguration(ImplicitDict): """Declarations for resources used by the test suite""" -class TestedRolesConfiguration(ImplicitDict): - report_path: str - """Path of folder to write HTML files containing a fulfilled-requirements-based view of the test report""" - - TestedRequirementsCollectionIdentifier = str """Identifier for a requirements collection, local to a TestedRequirementsConfiguration artifact configuration.""" class TestedRequirementsConfiguration(ImplicitDict): - output_path: str - """Path of a folder into which report HTML files should be written""" + report_name: str + """Name of subfolder in output path to contain the rendered templated report""" requirement_collections: Optional[ Dict[TestedRequirementsCollectionIdentifier, RequirementCollection] @@ -52,13 +47,13 @@ class TestedRequirementsConfiguration(ImplicitDict): class SequenceViewConfiguration(ImplicitDict): - output_path: str - """Path of a folder into which report HTML files should be written""" + redact_access_tokens: bool = True + """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" class ReportHTMLConfiguration(ImplicitDict): - html_path: str - """Path of HTML file to contain an HTML rendering of the raw test report object""" + redact_access_tokens: bool = True + """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" class TemplatedReportInjectedConfiguration(ImplicitDict): @@ -69,44 +64,36 @@ class TemplatedReportConfiguration(ImplicitDict): template_url: str """Url of the template to download from""" - output_path: str - """Path of HTML file to contain the rendered templated report""" + report_name: str + """Name of HTML file (without extension) to contain the rendered templated report""" configuration: Optional[TemplatedReportInjectedConfiguration] = None """Configuration to be injected in the templated report""" -class GraphConfiguration(ImplicitDict): - gv_path: str - """Path of GraphViz (.gv) text file to contain a visualization of the test run""" - +class RawReportConfiguration(ImplicitDict): + redact_access_tokens: bool = True + """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" -class ReportConfiguration(ImplicitDict): - report_path: str - """File name of the report to write (if test_config provided) or read (if test_config not provided)""" + indent: Optional[int] = None + """To pretty-print JSON content, specify an indent level (generally 2), or omit or set to None to write compactly.""" class ArtifactsConfiguration(ImplicitDict): - redact_access_tokens: bool = True - """When True, look for instances of "Authorization" keys in the report with values starting "Bearer " and redact the signature from those access tokens""" + output_path: str + """Path to folder where artifacts should be written.""" - report: Optional[ReportConfiguration] = None - """Configuration for report generation""" + raw_report: Optional[RawReportConfiguration] = None + """Configuration for raw report generation""" report_html: Optional[ReportHTMLConfiguration] = None - """If specified, configuration describing how an HTML version of the report should be generated""" + """If specified, configuration describing how an HTML version of the raw report should be generated""" - templated_reports: List[TemplatedReportConfiguration] = [] + templated_reports: Optional[List[TemplatedReportConfiguration]] = None """List of report templates to be rendered""" - graph: Optional[GraphConfiguration] = None - """If specified, configuration describing a desired graph visualization summarizing the test run""" - - tested_roles: Optional[TestedRolesConfiguration] = None - """If specified, configuration describing a desired report summarizing tested requirements for each specified participant and role""" - - tested_requirements: Optional[TestedRequirementsConfiguration] = None - """If specified, configuration describing a desired report summarizing all tested requirements for each participant""" + tested_requirements: Optional[List[TestedRequirementsConfiguration]] = None + """If specified, list of configurations describing desired reports summarizing tested requirements for each participant""" sequence_view: Optional[SequenceViewConfiguration] = None """If specified, configuration describing a desired report describing the sequence of events that occurred during the test""" diff --git a/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml b/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml index 6b458f94b0..af807b8ab7 100644 --- a/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml +++ b/monitoring/uss_qualifier/configurations/dev/dss_probing.yaml @@ -26,19 +26,20 @@ v1: service_area: kentland_service_area problematically_big_area: kentland_problematically_big_area artifacts: - report: - report_path: output/report_dss_probing.json + output_path: output/dss_probing + raw_report: {} + sequence_view: {} tested_requirements: - output_path: output/tested_requirements_dss_probing - requirement_collections: - all_astm_dss_requirements: - requirement_collections: - - requirement_sets: - - astm.f3411.v22a.dss_provider - - astm.f3411.v19.dss_provider - participant_requirements: - uss1: all_astm_dss_requirements - uss2: all_astm_dss_requirements + - report_name: requirements + requirement_collections: + all_astm_dss_requirements: + requirement_collections: + - requirement_sets: + - astm.f3411.v22a.dss_provider + - astm.f3411.v19.dss_provider + participant_requirements: + uss1: all_astm_dss_requirements + uss2: all_astm_dss_requirements validation: criteria: - full_success: {} diff --git a/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml b/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml index 413fbf3cfc..cc74bc3a10 100644 --- a/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml +++ b/monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml @@ -93,23 +93,23 @@ v1: # relative to where uss_qualifier is executed from, and are located inside the # Docker container executing uss_qualifier. artifacts: + # Write artifacts to this folder (relative to uss_qualifier) + output_path: output/f3548 + # Write out full report content - report: - # Path to main report output - report_path: output/report_f3548_self_contained.json + raw_report: {} - # Write out a human-readable report of the requirements tested + # Write out a human-readable report of the F3548-21 requirements tested tested_requirements: - output_path: output/tested_requirements_f3548_self_contained - requirement_collections: - scd: - requirement_collections: - - requirement_sets: - - astm.f3548.v21.scd - participant_requirements: - uss1: scd - uss2: scd + - report_name: requirements + requirement_collections: + scd: + requirement_collections: + - requirement_sets: + - astm.f3548.v21.scd + participant_requirements: + uss1: scd + uss2: scd # Write out a human-readable report showing the sequence of events of the test - sequence_view: - output_path: output/sequence_f3548_self_contained + sequence_view: {} diff --git a/monitoring/uss_qualifier/configurations/dev/general_flight_auth.yaml b/monitoring/uss_qualifier/configurations/dev/general_flight_auth.yaml index f9126e75d4..eda18dbd56 100644 --- a/monitoring/uss_qualifier/configurations/dev/general_flight_auth.yaml +++ b/monitoring/uss_qualifier/configurations/dev/general_flight_auth.yaml @@ -10,7 +10,7 @@ v1: resources: table: example_flight_check_table artifacts: - report: - report_path: output/report_general_flight_auth.json + output_path: output/general_flight_auth + raw_report: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/geoawareness_cis.yaml b/monitoring/uss_qualifier/configurations/dev/geoawareness_cis.yaml index e44ba54145..373b631fea 100644 --- a/monitoring/uss_qualifier/configurations/dev/geoawareness_cis.yaml +++ b/monitoring/uss_qualifier/configurations/dev/geoawareness_cis.yaml @@ -13,9 +13,7 @@ v1: resources: source_document: source_document artifacts: - report: - report_path: output/report_geoawareness_cis.json - graph: - gv_path: output/report_geoawareness_cis.gv + output_path: output/geoawareness_cis + raw_report: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/geospatial_comprehension.yaml b/monitoring/uss_qualifier/configurations/dev/geospatial_comprehension.yaml index d96d005596..d48799efb5 100644 --- a/monitoring/uss_qualifier/configurations/dev/geospatial_comprehension.yaml +++ b/monitoring/uss_qualifier/configurations/dev/geospatial_comprehension.yaml @@ -10,7 +10,7 @@ v1: resources: table: example_feature_check_table artifacts: - report: - report_path: output/report_geospatial_comprehension.json + output_path: output/geospatial_comprehension + raw_report: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/library/resources.yaml b/monitoring/uss_qualifier/configurations/dev/library/resources.yaml index 5837c2da8f..bdc07830bd 100644 --- a/monitoring/uss_qualifier/configurations/dev/library/resources.yaml +++ b/monitoring/uss_qualifier/configurations/dev/library/resources.yaml @@ -101,13 +101,13 @@ adjacent_circular_storage_config: $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json resource_type: resources.netrid.FlightDataStorageResource specification: - flight_record_collection_path: "./output/test_data.che.netrid.circular_flights.json" + flight_record_collection_path: "./output/generate_rid_test_data/flight_data/test_data.che.netrid.circular_flights.json" kml_storage_config: $content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json resource_type: resources.netrid.FlightDataStorageResource specification: - flight_record_collection_path: "./output/test_data.usa.netrid.dcdemo_flights.json" + flight_record_collection_path: "./output/generate_rid_test_data/flight_data/test_data.usa.netrid.dcdemo_flights.json" # ===== Flight planning intents ===== diff --git a/monitoring/uss_qualifier/configurations/dev/message_signing.yaml b/monitoring/uss_qualifier/configurations/dev/message_signing.yaml index ea33cc7af7..dfb2ec8491 100644 --- a/monitoring/uss_qualifier/configurations/dev/message_signing.yaml +++ b/monitoring/uss_qualifier/configurations/dev/message_signing.yaml @@ -50,5 +50,5 @@ v1: dss: scd_dss artifacts: - report: - report_path: output/report_message_signing.json + output_path: output/message_signing + raw_report: {} diff --git a/monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml b/monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml index 65e3b32062..3a63bd3cb3 100644 --- a/monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml +++ b/monitoring/uss_qualifier/configurations/dev/netrid_v19.yaml @@ -31,26 +31,23 @@ v1: service_area: kentland_service_area problematically_big_area: au_problematically_big_area artifacts: - report: - report_path: output/report_netrid_v19.json - tested_roles: - report_path: output/tested_roles_netrid_v19 + output_path: output/netrid_v19 + raw_report: {} tested_requirements: - output_path: output/tested_requirements_f3411v19 - requirement_collections: - sp_dp_dss: - requirement_sets: - - astm.f3411.v19.service_provider#Tested by automated tests - - astm.f3411.v19.display_provider#Automated verification - - astm.f3411.v19.dss_provider - sp_dss: - requirement_sets: - - astm.f3411.v19.service_provider#Tested by automated tests - - astm.f3411.v19.dss_provider - participant_requirements: - uss1: sp_dp_dss - uss2: sp_dss - sequence_view: - output_path: output/sequence_netrid_v19 + - report_name: requirements + requirement_collections: + sp_dp_dss: + requirement_sets: + - astm.f3411.v19.service_provider#Tested by automated tests + - astm.f3411.v19.display_provider#Automated verification + - astm.f3411.v19.dss_provider + sp_dss: + requirement_sets: + - astm.f3411.v19.service_provider#Tested by automated tests + - astm.f3411.v19.dss_provider + participant_requirements: + uss1: sp_dp_dss + uss2: sp_dss + sequence_view: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml b/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml index 7d919e2ebd..5441555c5a 100644 --- a/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml +++ b/monitoring/uss_qualifier/configurations/dev/netrid_v22a.yaml @@ -31,26 +31,23 @@ v1: service_area: kentland_service_area problematically_big_area: au_problematically_big_area artifacts: - report: - report_path: output/report_netrid_v22a.json - tested_roles: - report_path: output/tested_roles_netrid_v22a + output_path: output/netrid_v22a + raw_report: {} tested_requirements: - output_path: output/tested_requirements_f3411v22a - requirement_collections: - sp_dp_dss: - requirement_sets: - - astm.f3411.v22a.service_provider#Mandatory requirements - - astm.f3411.v22a.display_provider#Mandatory requirements - - astm.f3411.v22a.dss_provider - sp_dss: - requirement_sets: - - astm.f3411.v22a.service_provider#Mandatory requirements - - astm.f3411.v22a.dss_provider - participant_requirements: - uss1: sp_dp_dss - uss2: sp_dss - sequence_view: - output_path: output/sequence_netrid_v22a + - report_name: requirements + requirement_collections: + sp_dp_dss: + requirement_sets: + - astm.f3411.v22a.service_provider#Mandatory requirements + - astm.f3411.v22a.display_provider#Mandatory requirements + - astm.f3411.v22a.dss_provider + sp_dss: + requirement_sets: + - astm.f3411.v22a.service_provider#Mandatory requirements + - astm.f3411.v22a.dss_provider + participant_requirements: + uss1: sp_dp_dss + uss2: sp_dss + sequence_view: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/noop.yaml b/monitoring/uss_qualifier/configurations/dev/noop.yaml index 95af477c6d..be427560a8 100644 --- a/monitoring/uss_qualifier/configurations/dev/noop.yaml +++ b/monitoring/uss_qualifier/configurations/dev/noop.yaml @@ -24,8 +24,10 @@ v1: noop_config: noop_config artifacts: - report: - # Path to main report output - report_path: output/report_noop.json + output_path: output/noop + raw_report: + indent: 2 + sequence_view: {} + report_html: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/configurations/dev/uspace.yaml b/monitoring/uss_qualifier/configurations/dev/uspace.yaml index aa8c389757..8aa10486c9 100644 --- a/monitoring/uss_qualifier/configurations/dev/uspace.yaml +++ b/monitoring/uss_qualifier/configurations/dev/uspace.yaml @@ -83,41 +83,38 @@ v1: service_area: service_area problematically_big_area: problematically_big_area artifacts: - tested_roles: - report_path: output/tested_roles_uspace - report: - report_path: output/report_uspace.json + output_path: output/uspace + raw_report: {} templated_reports: - template_url: https://github.com/Orbitalize/reports/releases/download/v0.0.18/app-v0.0.18.zip - output_path: output/capabilities_uspace.html + report_name: capabilities tested_requirements: - output_path: output/tested_requirements_uspace - requirement_collections: - uspace: - requirement_collections: - - requirement_sets: - - astm.f3411.v22a.service_provider#Mandatory requirements - - astm.f3411.v22a.service_provider#Operator ID provider - - astm.f3411.v22a.service_provider#UAS ID Serial Number provider - - astm.f3411.v22a.service_provider#Height provider - - astm.f3411.v22a.service_provider#Operator Position provider - - astm.f3411.v22a.service_provider#Operational Status provider - - astm.f3411.v22a.display_provider#Mandatory requirements - - astm.f3411.v22a.display_provider#UAS ID Serial Number transmitter - - astm.f3411.v22a.display_provider#Timestamp transmitter - - astm.f3411.v22a.display_provider#Operational Status transmitter - - astm.f3411.v22a.display_provider#Operator ID transmitter - - astm.f3411.v22a.display_provider#Current Position transmitter - - astm.f3411.v22a.display_provider#Height transmitter - - astm.f3411.v22a.display_provider#Track Direction transmitter - - astm.f3411.v22a.display_provider#Speed transmitter - - astm.f3411.v22a.display_provider#Operator Position transmitter - - astm.f3411.v22a.dss_provider - - astm.f3548.v21.scd#Automated verification - participant_requirements: - uss1: uspace - uss2: uspace - sequence_view: - output_path: output/sequence_uspace + - report_name: requirements + requirement_collections: + uspace: + requirement_collections: + - requirement_sets: + - astm.f3411.v22a.service_provider#Mandatory requirements + - astm.f3411.v22a.service_provider#Operator ID provider + - astm.f3411.v22a.service_provider#UAS ID Serial Number provider + - astm.f3411.v22a.service_provider#Height provider + - astm.f3411.v22a.service_provider#Operator Position provider + - astm.f3411.v22a.service_provider#Operational Status provider + - astm.f3411.v22a.display_provider#Mandatory requirements + - astm.f3411.v22a.display_provider#UAS ID Serial Number transmitter + - astm.f3411.v22a.display_provider#Timestamp transmitter + - astm.f3411.v22a.display_provider#Operational Status transmitter + - astm.f3411.v22a.display_provider#Operator ID transmitter + - astm.f3411.v22a.display_provider#Current Position transmitter + - astm.f3411.v22a.display_provider#Height transmitter + - astm.f3411.v22a.display_provider#Track Direction transmitter + - astm.f3411.v22a.display_provider#Speed transmitter + - astm.f3411.v22a.display_provider#Operator Position transmitter + - astm.f3411.v22a.dss_provider + - astm.f3548.v21.scd#Automated verification + participant_requirements: + uss1: uspace + uss2: uspace + sequence_view: {} validation: $ref: ./library/validation.yaml#/normal_test diff --git a/monitoring/uss_qualifier/main.py b/monitoring/uss_qualifier/main.py index 900ca905f6..8351de8b2c 100644 --- a/monitoring/uss_qualifier/main.py +++ b/monitoring/uss_qualifier/main.py @@ -14,7 +14,7 @@ from monitoring.uss_qualifier.configurations.configuration import ( USSQualifierConfiguration, ArtifactsConfiguration, - ReportConfiguration, + RawReportConfiguration, USSQualifierConfigurationV1, ) from monitoring.uss_qualifier.fileio import load_dict_with_references @@ -23,8 +23,6 @@ from monitoring.uss_qualifier.reports.tested_requirements import ( generate_tested_requirements, ) -from monitoring.uss_qualifier.reports.tested_roles import generate_tested_roles -from monitoring.uss_qualifier.reports.graphs import make_graph from monitoring.uss_qualifier.reports.report import TestRunReport, redact_access_tokens from monitoring.uss_qualifier.reports.templates import render_templates from monitoring.uss_qualifier.reports.validation.report_validation import ( @@ -159,10 +157,10 @@ def run_config( if report_path: if not config.artifacts: config.artifacts = ArtifactsConfiguration( - ReportConfiguration(report_path=report_path) + RawReportConfiguration(report_path=report_path) ) elif not config.artifacts.report: - config.artifacts.report = ReportConfiguration(report_path=report_path) + config.artifacts.report = RawReportConfiguration(report_path=report_path) else: config.artifacts.report.report_path = report_path @@ -180,46 +178,70 @@ def run_config( ) if config.artifacts: - if config.artifacts.report and not do_not_save_report: - if config.artifacts.redact_access_tokens: - logger.info("Redacting access tokens in report") - redact_access_tokens(report) - logger.info("Writing report to {}", config.artifacts.report.report_path) - with open(config.artifacts.report.report_path, "w") as f: - json.dump(report, f, indent=2) + os.makedirs(config.artifacts.output_path, exist_ok=True) - if config.artifacts.report_html: - logger.info( - "Writing HTML report to {}", config.artifacts.report_html.html_path - ) - with open(config.artifacts.report_html.html_path, "w") as f: - f.write(make_report_html(report)) - - if len(config.artifacts.templated_reports) > 0: + def _should_redact(cfg) -> bool: + return "redact_access_tokens" in cfg and cfg.redact_access_tokens - render_templates(config.artifacts, report) + logger.info(f"Redacting access tokens from report") + redacted_report = ImplicitDict.parse( + json.loads(json.dumps(report)), TestRunReport + ) + redact_access_tokens(redacted_report) + + if config.artifacts.raw_report and not do_not_save_report: + # Raw report + path = os.path.join(config.artifacts.output_path, "report.json") + logger.info(f"Writing raw report to {path}") + raw_report = config.artifacts.raw_report + report_to_write = redacted_report if _should_redact(raw_report) else report + with open(path, "w") as f: + if "indent" in raw_report and raw_report.indent is not None: + json.dump(report_to_write, f, indent=raw_report.indent) + else: + json.dump(report_to_write, f) - if config.artifacts.graph: - logger.info( - "Writing GraphViz dot source to {}", config.artifacts.graph.gv_path + if config.artifacts.report_html: + # HTML rendering of raw report + path = os.path.join(config.artifacts.output_path, "report.html") + logger.info(f"Writing HTML report to {path}") + report_to_write = ( + redacted_report + if _should_redact(config.artifacts.report_html) + else report + ) + with open(path, "w") as f: + f.write(make_report_html(report_to_write)) + + if config.artifacts.templated_reports: + # Templated reports + render_templates( + config.artifacts.output_path, + config.artifacts.templated_reports, + redacted_report, ) - with open(config.artifacts.graph.gv_path, "w") as f: - f.write(make_graph(report).source) - - if config.artifacts.tested_roles: - path = config.artifacts.tested_roles.report_path - logger.info("Writing tested roles view to {}", path) - generate_tested_roles(report, path) if config.artifacts.tested_requirements: - path = config.artifacts.tested_requirements.output_path - logger.info(f"Writing tested requirements view to {path}") - generate_tested_requirements(report, config.artifacts.tested_requirements) + # Tested requirements view + for tested_reqs_config in config.artifacts.tested_requirements: + path = os.path.join( + config.artifacts.output_path, tested_reqs_config.report_name + ) + logger.info(f"Writing tested requirements view to {path}") + generate_tested_requirements(redacted_report, tested_reqs_config, path) if config.artifacts.sequence_view: - path = config.artifacts.sequence_view.output_path + # Sequence view + path = os.path.join(config.artifacts.output_path, "sequence") logger.info(f"Writing sequence view to {path}") - generate_sequence_view(report, config.artifacts.sequence_view) + report_to_write = ( + redacted_report + if _should_redact(config.artifacts.sequence_view) + else report + ) + generate_sequence_view( + report_to_write, config.artifacts.sequence_view, path + ) if "validation" in config and config.validation: logger.info(f"Validating test run report for configuration '{config_name}'") diff --git a/monitoring/uss_qualifier/reports/graphs.py b/monitoring/uss_qualifier/reports/graphs.py deleted file mode 100644 index a9264faee8..0000000000 --- a/monitoring/uss_qualifier/reports/graphs.py +++ /dev/null @@ -1,317 +0,0 @@ -from typing import Optional, List, Set, Tuple, Dict - -import graphviz -from loguru import logger - -from implicitdict import ImplicitDict - -from monitoring.uss_qualifier.reports.report import ( - ActionGeneratorReport, - TestRunReport, - TestSuiteReport, - TestScenarioReport, -) -from monitoring.uss_qualifier.resources.definitions import ( - ResourceID, - ResourceCollection, -) -from monitoring.uss_qualifier.scenarios.definitions import TestScenarioDeclaration -from monitoring.uss_qualifier.suites.definitions import ( - ActionType, - TestSuiteDeclaration, - TestSuiteDefinition, - ActionGeneratorDefinition, -) - -NodeName = str - - -class Node(ImplicitDict): - """Represents a node to be used in a GraphViz graph.""" - - name: NodeName - label: Optional[str] = None - children: List[NodeName] - attributes: Dict[str, str] - - -class NodeNamer(object): - """Creates and tracks unique, valid names within a GraphViz graph.""" - - names: Set[NodeName] = set() - - def make_name(self, desired_name: NodeName) -> NodeName: - acceptable_name = desired_name.replace(" ", "").replace(".", "_") - actual_name = acceptable_name - i = 2 - while actual_name in self.names: - actual_name = f"{acceptable_name}_{i}" - i += 1 - self.names.add(actual_name) - return actual_name - - def use_name(self, name: NodeName) -> None: - self.names.add(name) - - -def _make_test_scenario_nodes( - declaration: Optional[TestScenarioDeclaration], - report: TestScenarioReport, - nodes_by_id: Dict[ResourceID, Node], - namer: NodeNamer, - include_notes: bool = False, -) -> List[Node]: - nodes: List[Node] = [] - - # Make the scenario node - scenario_type = report.scenario_type - if scenario_type.startswith("scenarios."): - scenario_type = scenario_type[len("scenarios.") :] - label_elements = [report.name, scenario_type] - if include_notes and "notes" in report: - label_elements += [f"{k}={v.message}" for k, v in report.notes.items()] - scenario_node = Node( - name=namer.make_name(report.scenario_type), - label="\n".join(label_elements), - children=[], - attributes={ - "shape": "component", - "fillcolor": "lightgreen" if report.successful else "lightpink", - "style": "filled", - }, - ) - - # Mark the scenario node a child of the appropriate resources - if declaration is not None: - for local_id, node in nodes_by_id.items(): - node.children.append(scenario_node.name) - - # Add failed checks and error below - parent_node = scenario_node - for _, failed_check in report.query_failed_checks(): - check_node = Node( - name=namer.make_name(report.scenario_type + "FailedCheck"), - label=failed_check.summary, - children=[], - attributes={ - "shape": "octagon", - "fillcolor": "lightpink", - "style": "filled", - }, - ) - nodes.append(check_node) - parent_node.children.append(check_node.name) - parent_node = check_node - if "execution_error" in report: - error_node = Node( - name=namer.make_name(report.scenario_type + "ExecutionError"), - label=report.execution_error.message, - children=[], - attributes={ - "shape": "octagon", - "fillcolor": "lightpink", - "style": "filled", - "color": "red", - }, - ) - nodes.append(error_node) - parent_node.children.append(error_node.name) - - # Add the scenario node last - nodes.append(scenario_node) - - return nodes - - -def _make_test_suite_nodes( - declaration: TestSuiteDeclaration, - report: TestSuiteReport, - nodes_by_id: Dict[ResourceID, Node], - namer: NodeNamer, -) -> List[Node]: - # Make child nodes for each action in the suite - new_nodes = None - nodes: List[Node] = [] - children: List[NodeName] = [] - definition = TestSuiteDefinition.load_from_declaration(declaration) - for action_report, action in zip(report.actions, definition.actions): - action_nodes = _translate_ids(nodes_by_id, action.get_resource_links()) - if "test_suite" in action_report: - new_nodes = _make_test_suite_nodes( - action.test_suite, - action_report.test_suite, - action_nodes, - namer, - ) - children.append(new_nodes[-1].name) - elif "test_scenario" in action_report: - new_nodes = _make_test_scenario_nodes( - action.test_scenario, action_report.test_scenario, action_nodes, namer - ) - nodes.extend(new_nodes) - children.append(new_nodes[-1].name) - elif "action_generator" in action_report: - new_nodes = _make_action_generator_nodes( - action.action_generator, - action_report.action_generator, - action_nodes, - namer, - ) - children.append(new_nodes[-1].name) - else: - ActionType.raise_invalid_action_declaration() - nodes.extend(new_nodes) - - # Make the suite node itself - suite_type = report.suite_type - if suite_type.startswith("suites."): - suite_type = suite_type[len("suites.") :] - suite_node = Node( - name=namer.make_name(report.suite_type), - label=report.name + "\n" + suite_type, - children=children, - attributes={"shape": "folder"}, - ) - nodes.append(suite_node) - return nodes - - -def _make_action_generator_nodes( - definition: ActionGeneratorDefinition, - report: ActionGeneratorReport, - nodes_by_id: Dict[ResourceID, Node], - namer: NodeNamer, -) -> List[Node]: - # Make the action generator node - nodes: List[Node] = [] - children: List[NodeName] = [] - for action in report.actions: - if "test_suite" in action: - # TODO: Support visualization of test suite actions with an action generator - logger.warning( - "test_suite action is being omitted from the action generator because graph representation of a test suite from an action generator is not yet supported" - ) - new_nodes = [] - elif "test_scenario" in action: - new_nodes = _make_test_scenario_nodes( - None, action.test_scenario, nodes_by_id, namer, True - ) - nodes.extend(new_nodes) - children.append(new_nodes[-1].name) - elif "action_generator" in action: - raise NotImplementedError() - else: - raise NotImplementedError() - nodes.extend(new_nodes) - generator_type = report.generator_type - if generator_type.startswith("action_generators."): - generator_type = generator_type[len("action_generators.") :] - generator_node = Node( - name=namer.make_name(report.generator_type), - label="Action generator\n" + generator_type, - children=children, - attributes={"shape": "box3d"}, - ) - nodes.append(generator_node) - - # Point all resources used by children of the action generator to the action generator - for local_id, node in _translate_ids(nodes_by_id, definition.resources).items(): - node.children.append(generator_node.name) - - return nodes - - -def _make_resource_nodes( - resources: ResourceCollection, namer: NodeNamer -) -> Tuple[List[Node], Dict[ResourceID, Node]]: - nodes: List[Node] = [] - nodes_by_id: Dict[ResourceID, Node] = {} - added = 1 - while added > 0: - added = 0 - for resource_id, declaration in resources.resource_declarations.items(): - # Skip resources that already have nodes - if resource_id in namer.names: - continue - - # Identify prerequisite resources not yet created - prereqs = [] - for child_param, parent_id in declaration.dependencies.items(): - if parent_id not in nodes_by_id: - prereqs.append(parent_id) - - # Create this resource node if there are no uncreated prerequisites - if not prereqs: - resource_type = declaration.resource_type - if resource_type.startswith("resources."): - resource_type = resource_type[len("resources.") :] - resource_node = Node( - name=namer.make_name(resource_id), - label=f"{resource_id}\n{resource_type}", - children=[], - attributes={ - "shape": "note", - "fillcolor": "lightskyblue1", - "style": "filled", - }, - ) - namer.use_name(resource_id) - nodes.append(resource_node) - nodes_by_id[resource_id] = resource_node - for child_param, parent_id in declaration.dependencies.items(): - nodes_by_id[parent_id].children.append(resource_node.name) - added += 1 - return nodes, nodes_by_id - - -def _translate_ids( - nodes_by_id: Dict[ResourceID, Node], local_resources: Dict[ResourceID, ResourceID] -) -> Dict[ResourceID, Node]: - local_id_by_parent_id = {v: k for k, v in local_resources.items()} - return { - local_id_by_parent_id[parent_id]: node - for parent_id, node in nodes_by_id.items() - if parent_id in local_id_by_parent_id - } - - -def make_graph(report: TestRunReport) -> graphviz.Digraph: - namer = NodeNamer() - - # Make nodes for resources - nodes, nodes_by_id = _make_resource_nodes(report.configuration.resources, namer) - - action_type = report.configuration.action.get_action_type() - if action_type == ActionType.TestSuite: - test_suite = report.configuration.action.test_suite - test_suite_report = report.report.test_suite - - # Translate resource names into the action frame - suite_nodes_by_id = _translate_ids(nodes_by_id, test_suite.resources) - - # Make nodes for the suite - nodes.extend( - _make_test_suite_nodes( - test_suite, test_suite_report, suite_nodes_by_id, namer - ) - ) - elif action_type == ActionType.TestScenario: - test_scenario = report.configuration.action.test_scenario - test_scenario_report = report.report.test_scenario - scenario_nodes_by_id = _translate_ids(nodes_by_id, test_scenario.resources) - nodes.extend( - _make_test_scenario_nodes( - test_scenario, test_scenario_report, scenario_nodes_by_id, namer - ) - ) - else: - raise NotImplementedError() - - # Translate nodes into GraphViz - dot = graphviz.Digraph(node_attr={"shape": "box"}) - for node in nodes: - dot.node(name=node.name, label=node.label, **node.attributes) - for child in node.children: - dot.edge(node.name, child) - - return dot diff --git a/monitoring/uss_qualifier/reports/sequence_view.py b/monitoring/uss_qualifier/reports/sequence_view.py index cba2d75efa..c87b3f52a4 100644 --- a/monitoring/uss_qualifier/reports/sequence_view.py +++ b/monitoring/uss_qualifier/reports/sequence_view.py @@ -597,7 +597,7 @@ def _enumerate_all_participants(node: ActionNode) -> List[ParticipantID]: def _generate_scenario_pages( - node: ActionNode, config: SequenceViewConfiguration + node: ActionNode, config: SequenceViewConfiguration, output_path: str ) -> None: if node.node_type == ActionNodeType.Scenario: all_participants = list(node.scenario.participants) @@ -606,7 +606,7 @@ def _generate_scenario_pages( all_participants.remove(UNATTRIBUTED_PARTICIPANT) all_participants.append(UNATTRIBUTED_PARTICIPANT) scenario_file = os.path.join( - config.output_path, f"s{node.scenario.scenario_index}.html" + output_path, f"s{node.scenario.scenario_index}.html" ) template = jinja_env.get_template("sequence_view/scenario.html") with open(scenario_file, "w") as f: @@ -623,16 +623,16 @@ def _generate_scenario_pages( ) else: for child in node.children: - _generate_scenario_pages(child, config) + _generate_scenario_pages(child, config, output_path) def generate_sequence_view( - report: TestRunReport, config: SequenceViewConfiguration + report: TestRunReport, config: SequenceViewConfiguration, output_path: str ) -> None: node = _compute_action_node(report.report, Indexer()) - os.makedirs(config.output_path, exist_ok=True) - _generate_scenario_pages(node, config) + os.makedirs(output_path, exist_ok=True) + _generate_scenario_pages(node, config, output_path) overview_rows = list(_compute_overview_rows(node)) _align_overview_rows(overview_rows) @@ -642,7 +642,7 @@ def generate_sequence_view( if UNATTRIBUTED_PARTICIPANT in all_participants: all_participants.remove(UNATTRIBUTED_PARTICIPANT) all_participants.append(UNATTRIBUTED_PARTICIPANT) - overview_file = os.path.join(config.output_path, "index.html") + overview_file = os.path.join(output_path, "index.html") template = jinja_env.get_template("sequence_view/overview.html") with open(overview_file, "w") as f: f.write( diff --git a/monitoring/uss_qualifier/reports/templates.py b/monitoring/uss_qualifier/reports/templates.py index 5a1b36ae66..5ac2a05472 100644 --- a/monitoring/uss_qualifier/reports/templates.py +++ b/monitoring/uss_qualifier/reports/templates.py @@ -1,6 +1,7 @@ import json import shutil import os +from typing import List from loguru import logger @@ -57,10 +58,12 @@ def _download_template(self) -> pathlib.Path: logger.debug(f"{url} extracted to {path}") return path - def render(self): + def render(self, base_path: str): # Copy template src = pathlib.Path(self._download_template(), "index.html") - dst = pathlib.Path(self._template.output_path) + dst = pathlib.Path( + os.path.join(base_path, self._template.report_name + ".html") + ) # Configure application rendered_configuration = json.dumps( @@ -82,7 +85,11 @@ def render(self): logger.info(f"Templated report rendered to {dst}") -def render_templates(config: ArtifactsConfiguration, report: TestRunReport): +def render_templates( + base_path: str, + templated_reports: List[TemplatedReportConfiguration], + report: TestRunReport, +): pathlib.Path(CACHE_TEMPLATE_PATH).mkdir(parents=True, exist_ok=True) - for template in config.templated_reports: - TemplateRenderer(template, report).render() + for template in templated_reports: + TemplateRenderer(template, report).render(base_path) diff --git a/monitoring/uss_qualifier/reports/templates/tested_roles/capability_evaluation_report.html b/monitoring/uss_qualifier/reports/templates/tested_roles/capability_evaluation_report.html deleted file mode 100644 index b057e70fa8..0000000000 --- a/monitoring/uss_qualifier/reports/templates/tested_roles/capability_evaluation_report.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - -