diff --git a/.gitignore b/.gitignore index 41e951a7e5813ee04764fd8a6115110c4c68a796..fc272ee5dae073b37bd06ea33b26c1513f88fbf0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ __pycache__/ *.pyc +tests/test_swagger.json \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 513b309e1c27ba646b89ee6e8a32f09136e55371..d4e696ec13a9ef7993b6925147268ce4dfaa0b4c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ include: - project: communications-backbone-system/backbone-infrastructure-config ref: master file: gitlab/all.yml + - local: /gitlab/test-js.yml variables: DOCKER_IMAGE_NAME: backbone-message-format diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6df79eb949d13943233a31b8e234f969212cfa..7254ef17d0f10d2b0fce92f29a45066301bd8b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [v1.0.0] - 2024-08-28 + +### Changed + +- Use discover to find all unit tests +- Run tests against current format definitions +- Test that formats match saved schema +- Run python and javascript tests in CI +- Refactor schema script to remove invalid definitions object automatically +- Refactor generate_schema_config script to output file on -f flag + ## [v0.2.0] - 2024-02-06 ### Added @@ -35,6 +46,7 @@ JSON schema definitions for the SoAR project Example messages matching the schema for each partner -[unreleased]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v0.2.0...dev +[unreleased]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v1.0.0...dev +[v1.0.0]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v0.2.0...v1.0.0 [v0.2.0]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v0.1.0...v0.2.0 [v0.1.0]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/9e6ce245...v0.1.0 diff --git a/README.md b/README.md index 4d3b970fe534bec0d4f8bcdbc00436ec30bea09f..081c9d8aeb0967aab2e27f1066d80355e34c2f2c 100644 --- a/README.md +++ b/README.md @@ -72,22 +72,28 @@ eg ## Run Docs & Save Schema When Updating Message Formats 1. Run the command below and go to `http://127.0.0.1:5000` ``` -python3 generate_schema_config.py +python3 generate_schema_config.py -f ``` -2. Copy the schema generated `http://127.0.0.1:5000/soar_protocol.json` into the `backbone-message-format/project/<project_name>/swagger.json` - -3. In the json copied, remove the key `"definitions"` from the schema and save this. +2. Commit any changes to the `backbone-message-format/project/<project_name>/swagger.json` ## Run Tests + +We have found slightly different behaviour between the OpenAPI validators in python and javascript. + +By running tests in both languages we should protect against messages that pass validation in +one client but fail in another. + Run both tests below: -1. Test 1 + +1. Test 1 - Use python validators ``` -python3 -m unittest tests/test_schemas.py +python3 -m unittest discover ``` -2. Test 2 +2. Test 2 - Use javascript validators ``` -cd tests-js/docker; docker compose up --build +# Compile schema and run javascript validation tests in docker +python3 test-js.py ``` ## Quick Links diff --git a/docker/docker-compose-test.yaml b/docker/docker-compose-test.yaml index 2461af3b7c397f7214875be0dfdd23fc8e4589ac..6caad2f364d3cb8f071d92f670bcb0abaf537005 100644 --- a/docker/docker-compose-test.yaml +++ b/docker/docker-compose-test.yaml @@ -12,4 +12,4 @@ services: - PYTHONDONTWRITEBYTECODE=1 volumes: - ../:/app - command: "python3 -m unittest tests/test_schemas.py" \ No newline at end of file + command: "python3 -m unittest discover" diff --git a/generate_schema_config.py b/generate_schema_config.py index 123de907cd461abfb03702e5c6f61f7ff25c5795..fa58bb19ddaa8d0077de2ff4a4856e30c9c0bd6e 100644 --- a/generate_schema_config.py +++ b/generate_schema_config.py @@ -14,11 +14,19 @@ from formats.alert import alert_schema from flasgger import Swagger from flask import Flask +import argparse +import json import os -app = Flask(__name__) -url_prefix = os.getenv("URL_PREFIX", "") +# Enable running on domain sub-path +URL_PREFIX = os.getenv("URL_PREFIX", "") +# Allow env override of default host +FLASK_HOST = os.getenv("FLASK_HOST", "localhost") +# Allow env override of default port +FLASK_PORT = os.getenv("FLASK_PORT", 5000) +# Switch on debug mode if env var is truthy +FLASK_DEBUG = os.getenv("FLASK_DEBUG", "False").lower() in ("true", "1", "t") swagger_config = { "openapi": "3.0.2", @@ -35,7 +43,7 @@ swagger_config = { "route": "/soar_protocol.json", } ], - "url_prefix": url_prefix, + "url_prefix": URL_PREFIX, "paths": {}, "components": { "schemas": { @@ -103,11 +111,125 @@ swagger_config = { }, } -swag = Swagger(app, config=swagger_config, merge=True) -flask_host = os.getenv( - "FLASK_HOST", "localhost" -) # Sets to whatever FLASK_HOST is, or defaults to localhost +def configure_flask(swagger_config): + """ + Setup a flask app, load flasgger + + and then patch to remove invalid + definitions:{} object + """ + app = Flask(__name__) + Swagger(app, config=swagger_config, merge=True) + + # Replace schema route to remove invalid + # definitions: {} + # Should be fixed if Flassger 0.9.7 is released + # + # The last release of flasgger was Aug 2020 + # This bug was fixed in Nov 2021 + # There is a pre-release from May 2023 + # Until the fix gets released we have to + # remove the invalid definitions object + # from the spec + @app.after_request + def after_request_decorator(response): + """ + I didn't want to mess with flasgger so + this blunt workaround that runs on every + route and then checks whether it's required + """ + is_response = type(response).__name__ == "Response" + is_json = is_response and response.content_type == "application/json" + if is_json: + parsed = response.json + if "definitions" in parsed: + del parsed["definitions"] + response.data = json.dumps(parsed) + + return response + + return app + + +def serve(swagger_config): + """ + Run as local flask app on FLASK_PORT|5000 + """ + app = configure_flask(swagger_config) + app.run(debug=FLASK_DEBUG, host=FLASK_HOST, port=FLASK_PORT) + + +def compile_schema(swagger_config): + """Extract the output schema from flasgger + + The only way I have found to do this is to + use a test client to make the GET request + for the page + + The function that returns the definition + can't be called outside the flask app context + """ + app = configure_flask(swagger_config) + route = swagger_config["specs"][0]["route"] + client = app.test_client() + response = client.get(route) + spec = response.json + return spec + + +def write_schema(swagger_config, file_path): + """ + Dump schema to specified file + """ + spec = compile_schema(swagger_config) + json_schema = json.dumps(spec, indent=2) + + with open(file_path, "w") as f: + f.write(json_schema) + + +def get_options(): + """ + Parse script arguments + """ + parser = argparse.ArgumentParser( + description="Generate the schema", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "-s", + "--serve", + dest="run_flask", + action="store_true", + help="Run flask app", + default=False, + ) + parser.add_argument( + "-f", + "--file", + dest="output_file", + action="store_true", + help="Save output to schema file", + default=False, + ) + parser.add_argument("filename", nargs="?", default="project/soar/swagger.json") + args = parser.parse_args() + config = vars(args) + # If no flag is specified default to running the flask server + if all(v is False for v in config.values()): + config["run_flask"] = True + return config + if __name__ == "__main__": - app.run(debug=False, host=flask_host) + # Parse script args + config = get_options() + + # Output compiled schema + if config.get("output_file"): + write_schema(swagger_config, config.get("filename")) + + # Run flask app + if config.get("run_flask"): + serve(swagger_config) diff --git a/gitlab/test-js.yml b/gitlab/test-js.yml new file mode 100644 index 0000000000000000000000000000000000000000..d773334724d33df555ae766c53467942e1eaad1e --- /dev/null +++ b/gitlab/test-js.yml @@ -0,0 +1,13 @@ +test-js: + stage: test + script: + - cd tests-js/docker + - docker compose up --build + tags: + - shell + rules: + - if: '$CI_SKIP_TESTS == "1"' + when: never + - if: '$CI_IGNORE_TESTS == "1"' + allow_failure: true + - when: on_success diff --git a/project/soar/swagger.json b/project/soar/swagger.json index 008b966e586c38fd30c86114d10215e654add9a4..12a468e881147d6476e33a28545bfe4644552cbf 100644 --- a/project/soar/swagger.json +++ b/project/soar/swagger.json @@ -1,1160 +1,1160 @@ { - "components": { - "schemas": { - "MESSAGE": { - "description": "Full message definition with message-metadata in `header` and different message type schemas under `payload`", - "properties": { - "header": { - "$ref": "#/components/schemas/header" - }, - "payload": { - "$ref": "#/components/schemas/payload" - } - }, - "required": [ - "header", - "payload" + "components": { + "schemas": { + "MESSAGE": { + "description": "Full message definition with message-metadata in `header` and different message type schemas under `payload`", + "properties": { + "header": { + "$ref": "#/components/schemas/header" + }, + "payload": { + "$ref": "#/components/schemas/payload" + } + }, + "required": [ + "header", + "payload" + ], + "type": "object" + }, + "acknowledgement": { + "properties": { + "approved": { + "description": "Human-in-the-loop approval.1 - Plan approved; 0 - Plan Rejected", + "type": "boolean" + }, + "autonomy_engine_plan_ID": { + "description": "Mission plan ID (according to Autonomy Engine's mission plan number sent) executed by platform", + "example": 1, + "type": "integer" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "acknowledgement" ], - "type": "object" - }, - "acknowledgement": { - "properties": { - "approved": { - "description": "Human-in-the-loop approval.1 - Plan approved; 0 - Plan Rejected", - "type": "boolean" - }, - "autonomy_engine_plan_ID": { - "description": "Mission plan ID (according to Autonomy Engine's mission plan number sent) executed by platform", - "example": 1, - "type": "integer" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "acknowledgement" - ], - "example": "acknowledgement", - "type": "string" - }, - "platform_ID": { - "description": "Unique identifier for this platform", - "example": "reav-x-1", - "type": "string" - } - }, - "required": [ - "message_type", - "autonomy_engine_plan_ID", - "platform_ID", - "approved" + "example": "acknowledgement", + "type": "string" + }, + "platform_ID": { + "description": "Unique identifier for this platform", + "example": "reav-x-1", + "type": "string" + } + }, + "required": [ + "message_type", + "autonomy_engine_plan_ID", + "platform_ID", + "approved" + ], + "type": "object" + }, + "alert": { + "properties": { + "code": { + "description": "Alert code", + "example": 345, + "type": "integer" + }, + "details": { + "description": "Detailed reason for the alert ", + "example": "Discrepancy between rudder and actuator positions : Rudder Pos=10.31\u00b0, Steering Actuator Pos=28.65\u00b0, Discrepancy=18.33\u00b0", + "type": "string" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "alert" ], - "type": "object" - }, - "alert": { - "properties": { - "code": { - "description": "Alert code", - "example": 345, - "type": "integer" - }, - "details": { - "description": "Detailed reason for the alert ", - "example": "Discrepancy between rudder and actuator positions : Rudder Pos=10.31\u00b0, Steering Actuator Pos=28.65\u00b0, Discrepancy=18.33\u00b0", - "type": "string" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "alert" - ], - "example": "alert", - "type": "string" - }, - "platform_ID": { - "description": "Unique identifier for this platform", - "example": "usvdecibel", - "type": "string" - }, - "platform_timestamp": { - "description": "Timestamp for onboard platform status message", - "example": "2022-12-21T00:00:00Z", - "format": "date-time", - "type": "string" - }, - "severity": { - "description": "Severity level of alert", - "enum": [ - "Emergency", - "Alarm", - "Warning", - "Caution" - ], - "example": "Alarm", - "type": "string" - }, - "subsystem": { - "description": "System that generated the alert", - "example": "Onboard Fault Monitor", - "type": "string" - }, - "summary": { - "description": "High level description of the alert", - "example": "Steering Damage - Port Side", - "type": "string" - } - }, - "required": [ - "message_type", - "platform_ID", - "code", - "severity" + "example": "alert", + "type": "string" + }, + "platform_ID": { + "description": "Unique identifier for this platform", + "example": "usvdecibel", + "type": "string" + }, + "platform_timestamp": { + "description": "Timestamp for onboard platform status message", + "example": "2022-12-21T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "severity": { + "description": "Severity level of alert", + "enum": [ + "Emergency", + "Alarm", + "Warning", + "Caution" ], - "type": "object" - }, - "header": { - "discriminator": { - "propertyName": "message_type" - }, - "properties": { - "delivery_type": { - "default": "publish", - "description": "To publish or broadcast this message.", - "enum": [ - "broadcast", - "publish" - ], - "example": "publish", - "type": "string" - }, - "destination": { - "description": "Publisher topic; What is the destination of this message", - "example": "ah1", - "type": "string" - }, - "encoded": { - "description": "Indicate that message raw (encoded) or decoded. Options: encoded=true, decoded=false", - "example": false, - "type": "boolean" - }, - "message_ID": { - "description": "An identifier for the type of message received.", - "example": "b427003c-0000-11aa-a1eb-bvcdfghjgfdd", - "type": "string" - }, - "source": { - "description": "The sender; Where is this message from", - "example": "autonomy_engine", - "type": "string" - }, - "timestamp": { - "description": "Timestamp of message", - "example": "2022-11-16T00:00:00Z", - "format": "date-time", - "type": "string" - }, - "version": { - "description": "Version of comms backbone message format protocol", - "example": 2.0, - "format": "float", - "type": "number" - } - }, - "type": "object" - }, - "mission_plan": { - "properties": { - "autonomy_engine_plan_ID": { - "description": "Unique identifier for this plangenerated by the Autonomy Engine", - "example": 3, - "type": "integer" - }, - "emergency": { - "default": false, - "description": "To indicate if this is an emergency. true = emergency and false = no emergency", - "example": false, - "type": "boolean" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "mission_plan" - ], - "example": "mission_plan", - "type": "string" - }, - "plan": { - "items": { - "properties": { - "action": { - "description": "Autonomy Engine's action from `move`, `payload`, `dive`, `send_hits`, `scanline`, `scanpoint`.", - "enum": [ - "move", - "payload", - "dive", - "send_hits", - "scanline", - "scanpoint", - "go_home", - "surface_now", - "stop_mission", - "abort_now" - ], - "example": "move", - "type": "string" - }, - "activate_payload": { - "description": "To activate/deactivate sensor for Autosub Hover-1 --> `MBES` sensor and for EcoSUB --> `Sidescan`", - "example": true, - "type": "boolean" - }, - "altitude": { - "description": "Altitude of next action", - "example": 15.0, - "format": "float", - "type": "number" - }, - "depth": { - "description": "Depth of next action", - "example": 15.0, - "format": "float", - "type": "number" - }, - "start_point_latitude": { - "description": "Start point, y-coordinate", - "example": 50.37072283932642, - "format": "float", - "type": "number" - }, - "start_point_longitude": { - "description": "Start point, x-coordinate", - "example": -4.187143188645706, - "format": "float", - "type": "number" - }, - "target_waypoint_latitude": { - "description": "Target waypoint, y-coordinate", - "example": 50.37072283932642, - "format": "float", - "type": "number" - }, - "target_waypoint_longitude": { - "description": "Target waypoint, x-coordinate", - "example": -4.187143188645706, - "format": "float", - "type": "number" - }, - "timeout": { - "description": "Timeout set to perform action", - "example": 1800.0, - "format": "float", - "type": "number" - } - }, - "required": [ - "target_waypoint_latitude", - "target_waypoint_longitude" - ], - "type": "object" - }, - "type": "array" - }, - "platform_ID": { - "description": "Unique identifier for this platform", - "example": "reav-x-1", - "type": "string" - } - }, - "required": [ - "message_type", - "autonomy_engine_plan_ID", - "platform_ID", - "plan" + "example": "Alarm", + "type": "string" + }, + "subsystem": { + "description": "System that generated the alert", + "example": "Onboard Fault Monitor", + "type": "string" + }, + "summary": { + "description": "High level description of the alert", + "example": "Steering Damage - Port Side", + "type": "string" + } + }, + "required": [ + "message_type", + "platform_ID", + "code", + "severity" + ], + "type": "object" + }, + "header": { + "discriminator": { + "propertyName": "message_type" + }, + "properties": { + "delivery_type": { + "default": "publish", + "description": "To publish or broadcast this message.", + "enum": [ + "broadcast", + "publish" ], - "type": "object" - }, - "mission_plan_encoded": { - "properties": { - "data": { - "description": "encoded string. E.g. Base64 encoded", - "example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", - "type": "string" - }, - "file_name": { - "description": "Name of file", - "example": "ah1-0238126349247372.bin", - "type": "string" - }, - "is_binary": { - "description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", - "example": true, - "type": "boolean" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "mission_plan_encoded" - ], - "example": "mission_plan_encoded", - "type": "string" - }, - "mime_type": { - "description": "MIME type", - "example": "application/gzip", - "type": "string" - } - }, - "required": [ - "data", - "is_binary" + "example": "publish", + "type": "string" + }, + "destination": { + "description": "Publisher topic; What is the destination of this message", + "example": "ah1", + "type": "string" + }, + "encoded": { + "description": "Indicate that message raw (encoded) or decoded. Options: encoded=true, decoded=false", + "example": false, + "type": "boolean" + }, + "message_ID": { + "description": "An identifier for the type of message received.", + "example": "b427003c-0000-11aa-a1eb-bvcdfghjgfdd", + "type": "string" + }, + "source": { + "description": "The sender; Where is this message from", + "example": "autonomy_engine", + "type": "string" + }, + "timestamp": { + "description": "Timestamp of message", + "example": "2022-11-16T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "version": { + "description": "Version of comms backbone message format protocol", + "example": 2.0, + "format": "float", + "type": "number" + } + }, + "type": "object" + }, + "mission_plan": { + "properties": { + "autonomy_engine_plan_ID": { + "description": "Unique identifier for this plangenerated by the Autonomy Engine", + "example": 3, + "type": "integer" + }, + "emergency": { + "default": false, + "description": "To indicate if this is an emergency. true = emergency and false = no emergency", + "example": false, + "type": "boolean" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "mission_plan" ], - "type": "object" - }, - "observation": { - "properties": { - "additional_data": { - "description": "Placeholder field for any additional data", - "example": { - "sensor_payload": false - } - }, - "message_type": { - "description": "Type of message", + "example": "mission_plan", + "type": "string" + }, + "plan": { + "items": { + "properties": { + "action": { + "description": "Autonomy Engine's action from `move`, `payload`, `dive`, `send_hits`, `scanline`, `scanpoint`.", "enum": [ - "observation" + "move", + "payload", + "dive", + "send_hits", + "scanline", + "scanpoint", + "go_home", + "surface_now", + "stop_mission", + "abort_now" ], - "example": "observation", - "type": "string" - }, - "platform_ID": { - "description": "Unique identifier for this platform", - "example": "reav-x-1", + "example": "move", "type": "string" - }, - "points_of_interest": { - "description": "Points from features of interest identified by platform if any found.", - "items": { - "properties": { - "latitude": { - "description": "Identified y-coordinate of point of interest", - "example": 178.2, - "format": "float", - "type": "number" - }, - "longitude": { - "description": "Identified x-coordinate of point of interest", - "example": -10.122, - "format": "float", - "type": "number" - }, - "quality_of_point": { - "description": "Quality/strength of points from features of interest identified by platform.", - "example": 0.98, - "format": "float", - "type": "number" - } - }, - "required": [ - "latitude", - "longitude" - ], - "type": "object" - }, - "type": "array" - }, - "region_surveyed": { - "description": "Region surveyed by given platform. GEOJSON", - "example": "", - "nullable": true - } - }, - "required": [ - "message_type", - "platform_ID" - ], - "type": "object" - }, - "observation_encoded": { - "properties": { - "data": { - "description": "encoded string. E.g. Base64 encoded", - "example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", - "type": "string" - }, - "file_name": { - "description": "Name of file", - "example": "ah1-0238126349247372.bin", - "type": "string" - }, - "is_binary": { - "description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", + }, + "activate_payload": { + "description": "To activate/deactivate sensor for Autosub Hover-1 --> `MBES` sensor and for EcoSUB --> `Sidescan`", "example": true, "type": "boolean" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "observation_encoded" - ], - "example": "observation_encoded", - "type": "string" - }, - "mime_type": { - "description": "MIME type", - "example": "application/gzip", - "type": "string" - } - }, - "required": [ - "data", - "is_binary" - ], - "type": "object" - }, - "payload": { - "discriminator": { - "mapping": { - "acknowledgement": "#/components/schemas/acknowledgement", - "alert": "#/components/schemas/alert", - "mission_plan": "#/components/schemas/mission_plan", - "mission_plan_encoded": "#/components/schemas/mission_plan_encoded", - "observation": "#/components/schemas/observation", - "observation_encoded": "#/components/schemas/observation_encoded", - "planning_configuration": "#/components/schemas/planning_configuration", - "platform_status": "#/components/schemas/platform_status", - "platform_status_encoded": "#/components/schemas/platform_status_encoded", - "survey": "#/components/schemas/survey", - "survey_encoded": "#/components/schemas/survey_encoded" - }, - "propertyName": "message_type" - }, - "oneOf": [ - { - "$ref": "#/components/schemas/alert" - }, - { - "$ref": "#/components/schemas/acknowledgement" - }, - { - "$ref": "#/components/schemas/mission_plan" - }, - { - "$ref": "#/components/schemas/mission_plan_encoded" - }, - { - "$ref": "#/components/schemas/observation" - }, - { - "$ref": "#/components/schemas/observation_encoded" - }, - { - "$ref": "#/components/schemas/planning_configuration" - }, - { - "$ref": "#/components/schemas/platform_status" - }, - { - "$ref": "#/components/schemas/platform_status_encoded" - }, - { - "$ref": "#/components/schemas/survey" - }, - { - "$ref": "#/components/schemas/survey_encoded" - } - ] - }, - "planning_configuration": { - "properties": { - "exclusion_zones": { - "description": "Exclusion zones for all platforms", - "items": { - "description": "Using GEOJSON, exact 4-point region (rectangle shaped - 5 points)", - "properties": { - "geometry_coordinates": { - "example": [ - [ - [ - -4.1777839187560915, - 50.34173405662855 - ], - [ - -4.1777839187560915, - 50.33820949229701 - ], - [ - -4.143667777943875, - 50.33820949229701 - ], - [ - -4.143667777943875, - 50.34173405662855 - ], - [ - -4.1777839187560915, - 50.34173405662855 - ] - ] - ], - "type": "array" - } - }, - "type": "object" - }, - "type": "array" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "planning_configuration" - ], - "example": "planning_configuration", - "type": "string" - }, - "planning_config_ID": { - "description": "Unique identifier tagged to version of this configuration plan", - "example": 3, - "type": "integer" - }, - "region_of_interest": { - "description": "Region of interest for the entire operation", - "items": { - "description": "Using GEOJSON, exact 4-point region (rectangle shaped - 5 points)", - "properties": { - "geometry_coordinates": { - "example": [ - [ - [ - -4.1777839187560915, - 50.34173405662855 - ], - [ - -4.1777839187560915, - 50.33820949229701 - ], - [ - -4.143667777943875, - 50.33820949229701 - ], - [ - -4.143667777943875, - 50.34173405662855 - ], - [ - -4.1777839187560915, - 50.34173405662855 - ] - ] - ], - "type": "array" - } - }, - "type": "object" - }, - "type": "array" - }, - "squads": { - "items": { - "properties": { - "no_of_platforms": { - "description": "Number of platforms", - "example": 3, - "type": "integer" - }, - "platforms": { - "description": "Squad consists of these platforms", - "items": { - "properties": { - "active": { - "description": "If platform is active = True, and inactive = False", - "example": true, - "type": "boolean" - }, - "additional_data": { - "description": "Any addition fields/data to be added here", - "example": { - "new_sensor_a": "test_sensor", - "range": 10.0 - }, - "type": "object" - }, - "beacon_ID": { - "description": "Unique identifier (number) for the beacon associated to this platform", - "example": 2407, - "type": "number" - }, - "emergency": { - "properties": { - "safe_command": { - "description": "Command/Action that is native to respective partner's platform/C2", - "enum": [ - "go_home", - "abort_now", - "stop_now", - "surface_now" - ], - "example": "go_home", - "type": "string" - }, - "target_depth": { - "description": "Z-coordinate safe place for respective platform . If platform to NOT stay at depth, key in `0.0`", - "example": 10.0, - "format": "float", - "type": "number" - }, - "target_waypoint_latitude": { - "description": "Y-coordinate safe place for respective platform", - "example": 50.365, - "format": "float", - "type": "number" - }, - "target_waypoint_longitude": { - "description": "X-coordinate safe place for respective platform", - "example": -7.432, - "format": "float", - "type": "number" - } - }, - "required": [ - "target_waypoint_latitude", - "target_waypoint_longitude", - "target_depth" - ], - "type": "object" - }, - "endurance_relative_to_water_speed": { - "properties": { - "avg_battery_rating": { - "description": "Battery endurance rating during standard operational speed usage (m/s)", - "example": 1.9, - "format": "float", - "type": "number" - }, - "max_battery_rating": { - "description": "Battery endurance rating during maximum speed usage (m/s)", - "example": 1.23, - "format": "float", - "type": "number" - }, - "min_battery_rating": { - "description": "Battery endurance rating during maximum speed usage (m/s)", - "example": 3.32, - "format": "float", - "type": "number" - } - }, - "type": "object" - }, - "max_velocity": { - "description": "Maximum velocity set for platform", - "example": 0.9, - "format": "float", - "type": "number" - }, - "min_altitude": { - "description": "Minimum altitude set for platform", - "example": 15.2, - "format": "float", - "type": "number" - }, - "min_velocity": { - "description": "Minimum velocity set for platform", - "example": 0.1, - "format": "float", - "type": "number" - }, - "model": { - "example": "reav", - "type": "string" - }, - "operator": { - "description": "Operator of platform", - "example": "noc", - "type": "string" - }, - "platform_ID": { - "description": "Unique identifier for this platform", - "example": "reav-x-1", - "type": "string" - }, - "scan_sensor": { - "properties": { - "angle": { - "description": "Angle of range of swath width (in degrees)", - "example": 140.0, - "format": "float", - "type": "number" - }, - "frequency": { - "description": "Frequency of scanning sensor (in kHz)", - "example": 700.0, - "format": "float", - "type": "number" - }, - "sensor_type": { - "description": "Unique identifier for this platform", - "enum": [ - "SIDESCAN", - "MBES" - ], - "example": "MBES", - "type": "string" - }, - "swath_width": { - "description": "Function of `target_altitude` for the platform's swath width (in metres)", - "example": 38.0, - "format": "float", - "type": "number" - }, - "warmup_time": { - "description": "Warmup time (seconds) for sensor to start up.", - "example": 180.0, - "format": "float", - "type": "number" - } - }, - "type": "object" - }, - "target_altitude": { - "description": "Target altitude set for platform. This affects swath width", - "example": 15.0, - "format": "float", - "type": "number" - }, - "turning_radius": { - "description": "Turning radius of platform (in metres)", - "example": 1.0, - "format": "float", - "type": "number" - } - }, - "required": [ - "operator", - "platform_ID", - "active", - "model" - ], - "type": "object" - }, - "type": "array" - }, - "squad_ID": { - "description": "Identifier of given squad", - "example": 23, - "type": "integer" - }, - "squad_mission_type": { - "description": "Mission of given squad: `tracking`, `survey`, `inspection`", - "enum": [ - "tracking", - "survey", - "inspection" - ], - "example": "survey", - "type": "string" - } - }, - "required": [ - "squad_ID", - "no_of_platforms", - "platforms", - "squad_mission_type" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "message_type", - "planning_config_ID", - "squads", - "exclusion_zones", - "region_of_interest" - ], - "type": "object" - }, - "platform_status": { - "properties": { - "altitude": { - "description": "Target altitude in metres", - "example": 20.0, - "format": "float", - "type": "number" - }, - "autonomy_engine_plan_ID": { - "description": "Last mission plan ID (according to Autonomy Engine's mission plan number sent) executed by platform", - "example": 1, - "type": "integer" - }, - "battery_output": { - "description": "Battery output in kW", - "example": 80.2, + }, + "altitude": { + "description": "Altitude of next action", + "example": 15.0, "format": "float", "type": "number" - }, - "battery_remaining_capacity": { - "description": "Battery remaining % provided by respective C2", - "example": 80.2, + }, + "depth": { + "description": "Depth of next action", + "example": 15.0, "format": "float", "type": "number" - }, - "depth": { - "default": 0.0, - "description": "Target depth in metres", - "example": 50.0, + }, + "start_point_latitude": { + "description": "Start point, y-coordinate", + "example": 50.37072283932642, "format": "float", "type": "number" - }, - "endurance": { - "description": "Estimate of hours of operation remaining based on present output or performance", - "example": 7.4, + }, + "start_point_longitude": { + "description": "Start point, x-coordinate", + "example": -4.187143188645706, "format": "float", "type": "number" - }, - "fuel_remaining_capacity": { - "description": "Percentage remaining capacity", - "example": 80.2, + }, + "target_waypoint_latitude": { + "description": "Target waypoint, y-coordinate", + "example": 50.37072283932642, "format": "float", "type": "number" - }, - "fuel_volume": { - "description": "Litres of liquid fuel", - "example": 12.5, + }, + "target_waypoint_longitude": { + "description": "Target waypoint, x-coordinate", + "example": -4.187143188645706, "format": "float", "type": "number" - }, - "heading": { - "description": "Angular distance relative to north, usually 000\u00b0 at north, clockwise through 359\u00b0, in degrees", - "example": 124.3, + }, + "timeout": { + "description": "Timeout set to perform action", + "example": 1800.0, "format": "float", "type": "number" - }, - "health_status": { - "description": "Health status where 0 is OK, 1 is platform has an ERROR", - "example": false, - "type": "boolean" - }, - "latitude": { - "description": "Latitude (Y-coordinate) in decimal degrees.", + } + }, + "required": [ + "target_waypoint_latitude", + "target_waypoint_longitude" + ], + "type": "object" + }, + "type": "array" + }, + "platform_ID": { + "description": "Unique identifier for this platform", + "example": "reav-x-1", + "type": "string" + } + }, + "required": [ + "message_type", + "autonomy_engine_plan_ID", + "platform_ID", + "plan" + ], + "type": "object" + }, + "mission_plan_encoded": { + "properties": { + "data": { + "description": "encoded string. E.g. Base64 encoded", + "example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", + "type": "string" + }, + "file_name": { + "description": "Name of file", + "example": "ah1-0238126349247372.bin", + "type": "string" + }, + "is_binary": { + "description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", + "example": true, + "type": "boolean" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "mission_plan_encoded" + ], + "example": "mission_plan_encoded", + "type": "string" + }, + "mime_type": { + "description": "MIME type", + "example": "application/gzip", + "type": "string" + } + }, + "required": [ + "data", + "is_binary" + ], + "type": "object" + }, + "observation": { + "properties": { + "additional_data": { + "description": "Placeholder field for any additional data", + "example": { + "sensor_payload": false + } + }, + "message_type": { + "description": "Type of message", + "enum": [ + "observation" + ], + "example": "observation", + "type": "string" + }, + "platform_ID": { + "description": "Unique identifier for this platform", + "example": "reav-x-1", + "type": "string" + }, + "points_of_interest": { + "description": "Points from features of interest identified by platform if any found.", + "items": { + "properties": { + "latitude": { + "description": "Identified y-coordinate of point of interest", "example": 178.2, "format": "float", "type": "number" - }, - "localisation_east_error": { - "description": "Difference in EAST between deadreckoningand USBL update.", - "example": 0.000129, - "format": "float", - "type": "number" - }, - "localisation_north_error": { - "description": "Difference in NORTH between deadreckoning and USBL update.", - "example": 0.000129, + }, + "longitude": { + "description": "Identified x-coordinate of point of interest", + "example": -10.122, "format": "float", "type": "number" - }, - "longitude": { - "description": "Longitude (X-coordinate) in decimal degrees.", - "example": -10.122, + }, + "quality_of_point": { + "description": "Quality/strength of points from features of interest identified by platform.", + "example": 0.98, "format": "float", "type": "number" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "platform_status" + } + }, + "required": [ + "latitude", + "longitude" + ], + "type": "object" + }, + "type": "array" + }, + "region_surveyed": { + "description": "Region surveyed by given platform. GEOJSON", + "example": "", + "nullable": true + } + }, + "required": [ + "message_type", + "platform_ID" + ], + "type": "object" + }, + "observation_encoded": { + "properties": { + "data": { + "description": "encoded string. E.g. Base64 encoded", + "example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", + "type": "string" + }, + "file_name": { + "description": "Name of file", + "example": "ah1-0238126349247372.bin", + "type": "string" + }, + "is_binary": { + "description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", + "example": true, + "type": "boolean" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "observation_encoded" + ], + "example": "observation_encoded", + "type": "string" + }, + "mime_type": { + "description": "MIME type", + "example": "application/gzip", + "type": "string" + } + }, + "required": [ + "data", + "is_binary" + ], + "type": "object" + }, + "payload": { + "discriminator": { + "mapping": { + "acknowledgement": "#/components/schemas/acknowledgement", + "alert": "#/components/schemas/alert", + "mission_plan": "#/components/schemas/mission_plan", + "mission_plan_encoded": "#/components/schemas/mission_plan_encoded", + "observation": "#/components/schemas/observation", + "observation_encoded": "#/components/schemas/observation_encoded", + "planning_configuration": "#/components/schemas/planning_configuration", + "platform_status": "#/components/schemas/platform_status", + "platform_status_encoded": "#/components/schemas/platform_status_encoded", + "survey": "#/components/schemas/survey", + "survey_encoded": "#/components/schemas/survey_encoded" + }, + "propertyName": "message_type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/alert" + }, + { + "$ref": "#/components/schemas/acknowledgement" + }, + { + "$ref": "#/components/schemas/mission_plan" + }, + { + "$ref": "#/components/schemas/mission_plan_encoded" + }, + { + "$ref": "#/components/schemas/observation" + }, + { + "$ref": "#/components/schemas/observation_encoded" + }, + { + "$ref": "#/components/schemas/planning_configuration" + }, + { + "$ref": "#/components/schemas/platform_status" + }, + { + "$ref": "#/components/schemas/platform_status_encoded" + }, + { + "$ref": "#/components/schemas/survey" + }, + { + "$ref": "#/components/schemas/survey_encoded" + } + ] + }, + "planning_configuration": { + "properties": { + "exclusion_zones": { + "description": "Exclusion zones for all platforms", + "items": { + "description": "Using GEOJSON, exact 4-point region (rectangle shaped - 5 points)", + "properties": { + "geometry_coordinates": { + "example": [ + [ + [ + -4.1777839187560915, + 50.34173405662855 + ], + [ + -4.1777839187560915, + 50.33820949229701 + ], + [ + -4.143667777943875, + 50.33820949229701 + ], + [ + -4.143667777943875, + 50.34173405662855 + ], + [ + -4.1777839187560915, + 50.34173405662855 + ] + ] ], - "example": "platform_status", - "type": "string" - }, - "mission_plan_ID": { - "description": "Mission plan ID according to platform-C2 system", - "example": 1, - "type": "integer" - }, - "mission_track_ID": { - "description": "Track number - stage in mission (e.g. 4 --> Waypoint 3 to Waypoint 4)", - "example": 4, + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "planning_configuration" + ], + "example": "planning_configuration", + "type": "string" + }, + "planning_config_ID": { + "description": "Unique identifier tagged to version of this configuration plan", + "example": 3, + "type": "integer" + }, + "region_of_interest": { + "description": "Region of interest for the entire operation", + "items": { + "description": "Using GEOJSON, exact 4-point region (rectangle shaped - 5 points)", + "properties": { + "geometry_coordinates": { + "example": [ + [ + [ + -4.1777839187560915, + 50.34173405662855 + ], + [ + -4.1777839187560915, + 50.33820949229701 + ], + [ + -4.143667777943875, + 50.33820949229701 + ], + [ + -4.143667777943875, + 50.34173405662855 + ], + [ + -4.1777839187560915, + 50.34173405662855 + ] + ] + ], + "type": "array" + } + }, + "type": "object" + }, + "type": "array" + }, + "squads": { + "items": { + "properties": { + "no_of_platforms": { + "description": "Number of platforms", + "example": 3, "type": "integer" - }, - "platform_ID": { - "description": "Unique identifier for this platform", - "example": "reav-x-1", - "type": "string" - }, - "platform_state": { - "description": "Current state executed by platform. E.g. STOP, IDLE, ABORT.", - "example": "ABORT", - "type": "string" - }, - "platform_timestamp": { - "description": "Timestamp for onboard platform status message", - "example": "2022-12-21T00:00:00Z", - "format": "date-time", - "type": "string" - }, - "range_to_go": { - "description": "Estimated distance to reach next waypoint", - "example": 124.3, - "format": "float", - "type": "number" - }, - "sensor_config": { - "description": "Scanning sensor on platform available to be controlled by the Autonomy Engine", - "properties": { - "additional_data": { + }, + "platforms": { + "description": "Squad consists of these platforms", + "items": { + "properties": { + "active": { + "description": "If platform is active = True, and inactive = False", + "example": true, + "type": "boolean" + }, + "additional_data": { "description": "Any addition fields/data to be added here", "example": { - "payload": [ - 1.2, - 434 - ] + "new_sensor_a": "test_sensor", + "range": 10.0 }, "type": "object" - }, - "sensor_on": { - "description": "Sensor switched on (true) or off (false)", - "example": true, - "type": "boolean" - }, - "sensor_serial": { - "description": "serial number of sensor", - "example": "mbes-002a", + }, + "beacon_ID": { + "description": "Unique identifier (number) for the beacon associated to this platform", + "example": 2407, + "type": "number" + }, + "emergency": { + "properties": { + "safe_command": { + "description": "Command/Action that is native to respective partner's platform/C2", + "enum": [ + "go_home", + "abort_now", + "stop_now", + "surface_now" + ], + "example": "go_home", + "type": "string" + }, + "target_depth": { + "description": "Z-coordinate safe place for respective platform . If platform to NOT stay at depth, key in `0.0`", + "example": 10.0, + "format": "float", + "type": "number" + }, + "target_waypoint_latitude": { + "description": "Y-coordinate safe place for respective platform", + "example": 50.365, + "format": "float", + "type": "number" + }, + "target_waypoint_longitude": { + "description": "X-coordinate safe place for respective platform", + "example": -7.432, + "format": "float", + "type": "number" + } + }, + "required": [ + "target_waypoint_latitude", + "target_waypoint_longitude", + "target_depth" + ], + "type": "object" + }, + "endurance_relative_to_water_speed": { + "properties": { + "avg_battery_rating": { + "description": "Battery endurance rating during standard operational speed usage (m/s)", + "example": 1.9, + "format": "float", + "type": "number" + }, + "max_battery_rating": { + "description": "Battery endurance rating during maximum speed usage (m/s)", + "example": 1.23, + "format": "float", + "type": "number" + }, + "min_battery_rating": { + "description": "Battery endurance rating during maximum speed usage (m/s)", + "example": 3.32, + "format": "float", + "type": "number" + } + }, + "type": "object" + }, + "max_velocity": { + "description": "Maximum velocity set for platform", + "example": 0.9, + "format": "float", + "type": "number" + }, + "min_altitude": { + "description": "Minimum altitude set for platform", + "example": 15.2, + "format": "float", + "type": "number" + }, + "min_velocity": { + "description": "Minimum velocity set for platform", + "example": 0.1, + "format": "float", + "type": "number" + }, + "model": { + "example": "reav", + "type": "string" + }, + "operator": { + "description": "Operator of platform", + "example": "noc", "type": "string" - } + }, + "platform_ID": { + "description": "Unique identifier for this platform", + "example": "reav-x-1", + "type": "string" + }, + "scan_sensor": { + "properties": { + "angle": { + "description": "Angle of range of swath width (in degrees)", + "example": 140.0, + "format": "float", + "type": "number" + }, + "frequency": { + "description": "Frequency of scanning sensor (in kHz)", + "example": 700.0, + "format": "float", + "type": "number" + }, + "sensor_type": { + "description": "Unique identifier for this platform", + "enum": [ + "SIDESCAN", + "MBES" + ], + "example": "MBES", + "type": "string" + }, + "swath_width": { + "description": "Function of `target_altitude` for the platform's swath width (in metres)", + "example": 38.0, + "format": "float", + "type": "number" + }, + "warmup_time": { + "description": "Warmup time (seconds) for sensor to start up.", + "example": 180.0, + "format": "float", + "type": "number" + } + }, + "type": "object" + }, + "target_altitude": { + "description": "Target altitude set for platform. This affects swath width", + "example": 15.0, + "format": "float", + "type": "number" + }, + "turning_radius": { + "description": "Turning radius of platform (in metres)", + "example": 1.0, + "format": "float", + "type": "number" + } + }, + "required": [ + "operator", + "platform_ID", + "active", + "model" + ], + "type": "object" }, - "type": "object" - }, - "speed_over_ground": { - "description": "Speed over ground", - "example": 124.3, - "format": "float", - "type": "number" - }, - "status_source": { - "description": "Indicate if this status message is from the platform or USBL", - "enum": [ - "usbl", - "onboard_platform" - ], - "example": "usbl", - "type": "string" - }, - "thrust_applied": { - "description": "Thrust applied", - "example": 124.3, - "format": "float", - "type": "number" - }, - "transmission_mode": { - "description": "Mode in which status message was transmitted when on the surface (e.g. iridium/wifi) or underwater (e.g. acoustics)", + "type": "array" + }, + "squad_ID": { + "description": "Identifier of given squad", + "example": 23, + "type": "integer" + }, + "squad_mission_type": { + "description": "Mission of given squad: `tracking`, `survey`, `inspection`", "enum": [ - "acoustics", - "iridium", - "wifi", - "starlink" + "tracking", + "survey", + "inspection" ], - "example": "wifi", - "type": "string" - }, - "usbl_fix_seconds_ago": { - "description": "USBL Fix received x second ago.", - "example": 10.0, - "format": "float", - "type": "number" - }, - "water_current_velocity": { - "description": "Water current magnitude and direction", - "example": "124.3NE", + "example": "survey", "type": "string" - } + } + }, + "required": [ + "squad_ID", + "no_of_platforms", + "platforms", + "squad_mission_type" + ], + "type": "object" }, - "required": [ - "message_type", - "platform_ID", - "status_source", - "platform_timestamp", - "latitude", - "longitude" + "type": "array" + } + }, + "required": [ + "message_type", + "planning_config_ID", + "squads", + "exclusion_zones", + "region_of_interest" + ], + "type": "object" + }, + "platform_status": { + "properties": { + "altitude": { + "description": "Target altitude in metres", + "example": 20.0, + "format": "float", + "type": "number" + }, + "autonomy_engine_plan_ID": { + "description": "Last mission plan ID (according to Autonomy Engine's mission plan number sent) executed by platform", + "example": 1, + "type": "integer" + }, + "battery_output": { + "description": "Battery output in kW", + "example": 80.2, + "format": "float", + "type": "number" + }, + "battery_remaining_capacity": { + "description": "Battery remaining % provided by respective C2", + "example": 80.2, + "format": "float", + "type": "number" + }, + "depth": { + "default": 0.0, + "description": "Target depth in metres", + "example": 50.0, + "format": "float", + "type": "number" + }, + "endurance": { + "description": "Estimate of hours of operation remaining based on present output or performance", + "example": 7.4, + "format": "float", + "type": "number" + }, + "fuel_remaining_capacity": { + "description": "Percentage remaining capacity", + "example": 80.2, + "format": "float", + "type": "number" + }, + "fuel_volume": { + "description": "Litres of liquid fuel", + "example": 12.5, + "format": "float", + "type": "number" + }, + "heading": { + "description": "Angular distance relative to north, usually 000\u00b0 at north, clockwise through 359\u00b0, in degrees", + "example": 124.3, + "format": "float", + "type": "number" + }, + "health_status": { + "description": "Health status where 0 is OK, 1 is platform has an ERROR", + "example": false, + "type": "boolean" + }, + "latitude": { + "description": "Latitude (Y-coordinate) in decimal degrees.", + "example": 178.2, + "format": "float", + "type": "number" + }, + "localisation_east_error": { + "description": "Difference in EAST between deadreckoningand USBL update.", + "example": 0.000129, + "format": "float", + "type": "number" + }, + "localisation_north_error": { + "description": "Difference in NORTH between deadreckoning and USBL update.", + "example": 0.000129, + "format": "float", + "type": "number" + }, + "longitude": { + "description": "Longitude (X-coordinate) in decimal degrees.", + "example": -10.122, + "format": "float", + "type": "number" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "platform_status" ], - "type": "object" - }, - "platform_status_encoded": { + "example": "platform_status", + "type": "string" + }, + "mission_plan_ID": { + "description": "Mission plan ID according to platform-C2 system", + "example": 1, + "type": "integer" + }, + "mission_track_ID": { + "description": "Track number - stage in mission (e.g. 4 --> Waypoint 3 to Waypoint 4)", + "example": 4, + "type": "integer" + }, + "platform_ID": { + "description": "Unique identifier for this platform", + "example": "reav-x-1", + "type": "string" + }, + "platform_state": { + "description": "Current state executed by platform. E.g. STOP, IDLE, ABORT.", + "example": "ABORT", + "type": "string" + }, + "platform_timestamp": { + "description": "Timestamp for onboard platform status message", + "example": "2022-12-21T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "range_to_go": { + "description": "Estimated distance to reach next waypoint", + "example": 124.3, + "format": "float", + "type": "number" + }, + "sensor_config": { + "description": "Scanning sensor on platform available to be controlled by the Autonomy Engine", "properties": { - "data": { - "description": "encoded string. E.g. Base64 encoded", - "example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", - "type": "string" - }, - "file_name": { - "description": "Name of file", - "example": "ah1-0238126349247372.bin", - "type": "string" - }, - "is_binary": { - "description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", - "example": true, - "type": "boolean" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "platform_status_encoded" - ], - "example": "platform_status_encoded", - "type": "string" - }, - "mime_type": { - "description": "MIME type", - "example": "application/gzip", - "type": "string" - } + "additional_data": { + "description": "Any addition fields/data to be added here", + "example": { + "payload": [ + 1.2, + 434 + ] + }, + "type": "object" + }, + "sensor_on": { + "description": "Sensor switched on (true) or off (false)", + "example": true, + "type": "boolean" + }, + "sensor_serial": { + "description": "serial number of sensor", + "example": "mbes-002a", + "type": "string" + } }, - "required": [ - "data", - "is_binary" - ], "type": "object" - }, - "survey": { - "properties": { - "latitude_A": { - "description": "Latitude of point A(intersection of normal)from waypoint A to survey line", - "example": 178.2, - "format": "float", - "type": "number" - }, - "latitude_B": { - "description": "Latitude of point B(intersection of normal)from waypoint B to survey line", - "example": 178.2, - "format": "float", - "type": "number" - }, - "latitude_C": { - "description": "Latitude of point C(intersection of normal)from waypoint C to survey line", - "example": 178.2, - "format": "float", - "type": "number" - }, - "latitude_D": { - "description": "Latitude of point D(intersection of normal)from waypoint D to survey line", - "example": 178.2, - "format": "float", - "type": "number" - }, - "latitude_E": { - "description": "Latitude of point E(intersection of normal)from waypoint E to survey line", - "example": 178.2, - "format": "float", - "type": "number" - }, - "longitude_A": { - "description": "Longitude of point A(intersection of normal)from waypoint A to survey line", - "example": -10.122, - "format": "float", - "type": "number" - }, - "longitude_B": { - "description": "Longitude of point B(intersection of normal)from waypoint B to survey line", - "example": -10.122, - "format": "float", - "type": "number" - }, - "longitude_C": { - "description": "Longitude of point C(intersection of normal)from waypoint C to survey line", - "example": -10.122, - "format": "float", - "type": "number" - }, - "longitude_D": { - "description": "Longitude of point D(intersection of normal)from waypoint D to survey line", - "example": -10.122, - "format": "float", - "type": "number" - }, - "longitude_E": { - "description": "Longitude of point E(intersection of normal)from waypoint E to survey line", - "example": -10.122, - "format": "float", - "type": "number" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "survey" - ], - "example": "survey", - "type": "string" - }, - "platform_ID": { - "description": "Unique identifier for this platform", - "example": "ecosub-2", - "type": "string" - }, - "timestamp": { - "description": "Timestamp for onboard message", - "example": "2022-12-21T00:00:00Z", - "format": "date-time", - "type": "string" - }, - "track_ID": { - "description": "Track number of action(s) currently executed by platform", - "example": 1, - "type": "integer" - } - }, - "required": [ - "latitude_A", - "longitude_A", - "latitude_B", - "longitude_B", - "platform_ID" + }, + "speed_over_ground": { + "description": "Speed over ground", + "example": 124.3, + "format": "float", + "type": "number" + }, + "status_source": { + "description": "Indicate if this status message is from the platform or USBL", + "enum": [ + "usbl", + "onboard_platform" ], - "type": "object" - }, - "survey_encoded": { - "properties": { - "data": { - "description": "encoded string. E.g. Base64 encoded", - "example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", - "type": "string" - }, - "file_name": { - "description": "Name of file", - "example": "ah1-0238126349247372.bin", - "type": "string" - }, - "is_binary": { - "description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", - "example": true, - "type": "boolean" - }, - "message_type": { - "description": "Type of message", - "enum": [ - "survey_encoded" - ], - "example": "survey_encoded", - "type": "string" - }, - "mime_type": { - "description": "MIME type", - "example": "application/gzip", - "type": "string" - } - }, - "required": [ - "data", - "is_binary" + "example": "usbl", + "type": "string" + }, + "thrust_applied": { + "description": "Thrust applied", + "example": 124.3, + "format": "float", + "type": "number" + }, + "transmission_mode": { + "description": "Mode in which status message was transmitted when on the surface (e.g. iridium/wifi) or underwater (e.g. acoustics)", + "enum": [ + "acoustics", + "iridium", + "wifi", + "starlink" ], - "type": "object" - } + "example": "wifi", + "type": "string" + }, + "usbl_fix_seconds_ago": { + "description": "USBL Fix received x second ago.", + "example": 10.0, + "format": "float", + "type": "number" + }, + "water_current_velocity": { + "description": "Water current magnitude and direction", + "example": "124.3NE", + "type": "string" + } + }, + "required": [ + "message_type", + "platform_ID", + "status_source", + "platform_timestamp", + "latitude", + "longitude" + ], + "type": "object" + }, + "platform_status_encoded": { + "properties": { + "data": { + "description": "encoded string. E.g. Base64 encoded", + "example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", + "type": "string" + }, + "file_name": { + "description": "Name of file", + "example": "ah1-0238126349247372.bin", + "type": "string" + }, + "is_binary": { + "description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", + "example": true, + "type": "boolean" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "platform_status_encoded" + ], + "example": "platform_status_encoded", + "type": "string" + }, + "mime_type": { + "description": "MIME type", + "example": "application/gzip", + "type": "string" + } + }, + "required": [ + "data", + "is_binary" + ], + "type": "object" + }, + "survey": { + "properties": { + "latitude_A": { + "description": "Latitude of point A(intersection of normal)from waypoint A to survey line", + "example": 178.2, + "format": "float", + "type": "number" + }, + "latitude_B": { + "description": "Latitude of point B(intersection of normal)from waypoint B to survey line", + "example": 178.2, + "format": "float", + "type": "number" + }, + "latitude_C": { + "description": "Latitude of point C(intersection of normal)from waypoint C to survey line", + "example": 178.2, + "format": "float", + "type": "number" + }, + "latitude_D": { + "description": "Latitude of point D(intersection of normal)from waypoint D to survey line", + "example": 178.2, + "format": "float", + "type": "number" + }, + "latitude_E": { + "description": "Latitude of point E(intersection of normal)from waypoint E to survey line", + "example": 178.2, + "format": "float", + "type": "number" + }, + "longitude_A": { + "description": "Longitude of point A(intersection of normal)from waypoint A to survey line", + "example": -10.122, + "format": "float", + "type": "number" + }, + "longitude_B": { + "description": "Longitude of point B(intersection of normal)from waypoint B to survey line", + "example": -10.122, + "format": "float", + "type": "number" + }, + "longitude_C": { + "description": "Longitude of point C(intersection of normal)from waypoint C to survey line", + "example": -10.122, + "format": "float", + "type": "number" + }, + "longitude_D": { + "description": "Longitude of point D(intersection of normal)from waypoint D to survey line", + "example": -10.122, + "format": "float", + "type": "number" + }, + "longitude_E": { + "description": "Longitude of point E(intersection of normal)from waypoint E to survey line", + "example": -10.122, + "format": "float", + "type": "number" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "survey" + ], + "example": "survey", + "type": "string" + }, + "platform_ID": { + "description": "Unique identifier for this platform", + "example": "ecosub-2", + "type": "string" + }, + "timestamp": { + "description": "Timestamp for onboard message", + "example": "2022-12-21T00:00:00Z", + "format": "date-time", + "type": "string" + }, + "track_ID": { + "description": "Track number of action(s) currently executed by platform", + "example": 1, + "type": "integer" + } + }, + "required": [ + "latitude_A", + "longitude_A", + "latitude_B", + "longitude_B", + "platform_ID" + ], + "type": "object" + }, + "survey_encoded": { + "properties": { + "data": { + "description": "encoded string. E.g. Base64 encoded", + "example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", + "type": "string" + }, + "file_name": { + "description": "Name of file", + "example": "ah1-0238126349247372.bin", + "type": "string" + }, + "is_binary": { + "description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", + "example": true, + "type": "boolean" + }, + "message_type": { + "description": "Type of message", + "enum": [ + "survey_encoded" + ], + "example": "survey_encoded", + "type": "string" + }, + "mime_type": { + "description": "MIME type", + "example": "application/gzip", + "type": "string" + } + }, + "required": [ + "data", + "is_binary" + ], + "type": "object" } - }, - "info":{ - "description":"SoAR message protocol in schemas", - "title":"SoAR Backbone Message Formats", - "version":"0.2" - }, - "openapi": "3.0.2", - "paths": {} + } + }, + "info": { + "description": "SoAR message protocol in schemas", + "title": "SoAR Backbone Message Formats", + "version": "1.0" + }, + "openapi": "3.0.2", + "paths": {} } \ No newline at end of file diff --git a/setup.py b/setup.py index 172035dfc7b61d5e5f071d9f9269d0b2b839507a..d3db0d43710e9660c8eed5ea0a8b9ec7fd57156f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup setup( - version="0.2.0", + version="1.0.0", name="backbone_message_format", python_requires=">=3.8", packages=["backbone_formats", "soar_schema"], diff --git a/tests-js/docker/Dockerfile b/tests-js/docker/Dockerfile index 8e06b59362cbab08c8abb295db1dcab5c230aa35..27076ba3a7c46a315949c6ef7ee5648e8d189e1a 100644 --- a/tests-js/docker/Dockerfile +++ b/tests-js/docker/Dockerfile @@ -1,15 +1,24 @@ -FROM node:18.7.0-alpine +FROM python:3.9.16-alpine + +# Add node for running JS validator tests +RUN apk add --update nodejs npm WORKDIR /app/tests-js COPY tests-js/package.json /app/tests-js/package.json +RUN npm install -g yarn + RUN yarn install WORKDIR /app COPY . /app +# compile test schema from message +RUN pip install -r requirements.txt +RUN python generate_schema_config.py -f tests/test_swagger.json + WORKDIR /app/tests-js CMD [ 'yarn', 'test' ] diff --git a/tests-js/soar-examples.test.js b/tests-js/soar-examples.test.js index 22abf84943060b2f7d3e63235e8ce7fb0cf91dd7..56141d1b71911ee4015bcd3d88a5c419e50ebcb4 100644 --- a/tests-js/soar-examples.test.js +++ b/tests-js/soar-examples.test.js @@ -3,7 +3,7 @@ const Validator = require('swagger-model-validator'); const OpenAPISchemaValidator = require('openapi-schema-validator').default; const getSchema = () => { - const schema = require(`${__dirname}/../project/soar/swagger.json`); + const schema = require(`${__dirname}/../tests/test_swagger.json`); return schema; }; diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 2f9d46519be92bdd02cc108e4b7ae80c7f071603..421c48ba6e27e0bfd3260f7c0ae2f359d735111c 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -5,12 +5,28 @@ from jsonschema.validators import RefResolver import unittest import json import os +from generate_schema_config import write_schema, swagger_config -SCHEMA_DIR = "project/soar/swagger.json" MOCK_DATA_DIR = "examples/" +class SchemaTestCase(unittest.TestCase): + def load_schema(self, file_path=None): + if file_path: + schema_path = file_path + else: + schema_path = os.getenv("SCHEMA_PATH") + schema, _ = read_from_filename(schema_path) + return schema + + @classmethod + def setUpClass(cls): + test_schema_path = "tests/test_swagger.json" + os.environ["SCHEMA_PATH"] = test_schema_path + write_schema(swagger_config, test_schema_path) + + class SchemaError(Exception): """ Test config specs of swagger.json for projects @@ -19,26 +35,37 @@ class SchemaError(Exception): def __init__(self, exception_type, message): self.type = exception_type self.message = message - super().__init__(self.message) + super().__init__(message) -class TestSpecs(unittest.TestCase): +class TestCompiledSoarSpec(SchemaTestCase): + """ + Test the saved version of the compiled schema has been updated + """ + + def test_compiled_spec_matches_definitions(self): + print("TEST: definitions match project/soar/swagger.json") + derived_schema = self.load_schema() + saved_schema = self.load_schema("project/soar/swagger.json") + self.assertEqual(saved_schema, derived_schema) + + +class TestSpecs(SchemaTestCase): def test_swagger_specs(self): """ Test specs (swagger.json generated from generate_schema_config.py) """ - schema, spec_url = read_from_filename("tests/fixtures/swagger.json") + print("TEST: compiled schema matches openapi v3 spec") + schema = self.load_schema() self.assertIsNone(openapi_v30_spec_validator.validate(schema)) -class TestAllMessageExamples(unittest.TestCase): +class TestAllMessageExamples(SchemaTestCase): def test_schema_specs(self): """ Test specs (swagger.json generated from generate_schema_config.py) """ - schema_ref = open(SCHEMA_DIR) - schema = json.load(schema_ref) - schema_ref.close() + schema = self.load_schema() partner_dir_list = os.listdir(MOCK_DATA_DIR) @@ -70,8 +97,6 @@ class TestAllMessageExamples(unittest.TestCase): f.close() print("Done.") - schema_ref.close() - if __name__ == "__main__": unittest.main()