Commit 89576e08 authored by Trishna Saeharaseelan's avatar Trishna Saeharaseelan
Browse files

fix: openapi schemas implementing oneOf

parent 4c0a0aba
......@@ -2,6 +2,8 @@
This project repository is a collaborative workspace. It consists of all messages transferred into and out of the Communications Backbone. Message type schemas will be developed once reviewing each platform's data and statuses defined by each partner.
_# TODO: Add note on OpenAPI version used._
# Message Types
Each message below will be wrapped in a `message_wrapper`:
* `mission_plan`: these would be two message types, i. encoded (platform-specific serialized message) and ii. parsed, human-readable message.
......
......@@ -7,15 +7,7 @@ __all__ = [
if x.endswith(".py") and x != "__init__.py"
]
abstract_schema = {
"allOf": [{"$ref": "#/components/schemas/Metadata"}],
"type": "object",
"properties": {
"payload": {}
}
}
message_metadata = {
message_header = {
"type": "object",
"discriminator": {
"propertyName": "message_type",
......@@ -32,11 +24,6 @@ message_metadata = {
"description": "Timestamp of message",
"example": "2022-11-16T00:00:00Z",
},
"message_type": {
"type": "string",
"description": "Type of message",
"example": "platform_status",
},
"version": {
"type": "number",
"format": "float",
......@@ -68,5 +55,4 @@ message_metadata = {
"default": "publish",
},
},
"required": ["message_type"],
}
\ No newline at end of file
......@@ -2,11 +2,14 @@
schemas: Acknowledgement status sent by the surface platform to report
receipt of message.
"""
from formats import abstract_schema
acknowledgement_schema = {
"type": "object",
"properties": {
"message_type": {
"type": "string",
"description": "Type of message",
"example": "acknowledgement",
},
"acknowledged_message_ID": {
"type": "string",
"description": "Identifier of message received and executed with "
......@@ -23,8 +26,5 @@ acknowledgement_schema = {
"example": "executed by platform",
},
},
"required": ["acknowledged_message_ID", "status"],
"required": ["message_type", "acknowledged_message_ID", "status"],
}
full_acknowledgement_schema = abstract_schema
full_acknowledgement_schema["properties"]["payload"] = acknowledgement_schema
# """
# schemas: Message Wrapper is used to wrap all message types that contain
# details of where the message is coming from, which end client is its
# destination and the type of message.
# """
# message_wrapper_schema = {
# "type": "object",
# "discriminator": {
# "propertyName": "message_type",
# },
# "properties": {
# "message_ID": {
# "type": "string",
# "description": "An identifier for the type of message received.",
# "example": "b427003c-0000-11aa-a1eb-bvcdfghjgfdd",
# },
# "timestamp": {
# "type": "string",
# "format": "date-time",
# "description": "Timestamp of message",
# "example": "2022-11-16T00:00:00Z",
# },
# "message_type": {
# "type": "string",
# "description": "Type of message",
# "example": "platform_status",
# },
# "version": {
# "type": "number",
# "format": "float",
# "description": "Version of comms backbone message format protocol",
# "example": 2.0,
# },
# "source": {
# "type": "string",
# "description": "The sender; Where is this message from",
# "example": "autonomy_engine",
# },
# "destination": {
# "type": "string",
# "description": "Publisher topic; What is the destination"
# + " of this message",
# "example": "ah1",
# },
# "encoded": {
# "type": "boolean",
# "description": "Indicate that message raw (encoded) or decoded. "
# + "Options: encoded=True, decoded=False",
# "example": False,
# },
# "delivery_type": {
# "type": "string",
# "description": "To publish or broadcast this message.",
# "enum": ["broadcast", "publish"],
# "example": "publish",
# "default": "publish",
# },
# },
# "required": ["message_type"],
# }
......@@ -3,7 +3,6 @@
sent to the respective platform's C2 to compile into a platform-specific
mission plan.
"""
from formats import abstract_schema
action_schema = {
"type": "object",
......@@ -66,6 +65,11 @@ action_schema = {
mission_plan_schema = {
"type": "object",
"properties": {
"message_type": {
"type": "string",
"description": "Type of message",
"example": "mission_plan",
},
"autonomy_engine_plan_ID": {
"type": "integer",
"description": "Unique identifier for this plan"
......@@ -82,8 +86,5 @@ mission_plan_schema = {
"items": action_schema,
},
},
"required": ["plan_ID", "platform_ID", "plan"],
"required": ["message_type", "plan_ID", "platform_ID", "plan"],
}
full_mission_plan_schema = abstract_schema
full_mission_plan_schema["properties"]["payload"] = mission_plan_schema
......@@ -2,7 +2,6 @@
schema: Observation Message sent by platforms when points of
interest are found.
"""
from formats import abstract_schema
hits_schema = {
"type": "object",
......@@ -33,36 +32,33 @@ hits_schema = {
observation_schema = {
"type": "object",
"properties": {
"payload": {
"type": "object",
"properties": {
"platform_ID": {
"type": "integer",
"description": "ID of platform to sending observations",
"example": 2,
},
"points_of_interest": {
"type": "array",
"items": hits_schema,
"description": "Points from features of interest identified by"
+ " platform if any found.", # TODO: DEFINE FORMAT.
},
"region_surveyed": {
# "type": "null",
"nullable": True,
"description": "Region surveyed by given platform."
+ " GEOJSON", # TODO: DEFINE FORMAT.
"example": "",
},
"additional_data": {
"description": "Placeholder field for any additional data",
"example": {"sensor_payload": False},
},
},
"message_type": {
"type": "string",
"description": "Type of message",
"example": "observation",
},
"platform_ID": {
"type": "integer",
"description": "ID of platform to sending observations",
"example": 2,
},
"points_of_interest": {
"type": "array",
"items": hits_schema,
"description": "Points from features of interest identified by"
+ " platform if any found.", # TODO: DEFINE FORMAT.
},
"region_surveyed": {
# "type": "null",
"nullable": True,
"description": "Region surveyed by given platform."
+ " GEOJSON", # TODO: DEFINE FORMAT.
"example": "",
},
"additional_data": {
"description": "Placeholder field for any additional data",
"example": {"sensor_payload": False},
},
},
"required": ["platform_ID"],
"required": ["message_type", "platform_ID"],
}
full_observation_schema = abstract_schema
full_observation_schema["properties"]["payload"] = observation_schema
......@@ -2,7 +2,6 @@
schemas: configuration sent to Autonomy Engine (i.e. during an emergency,
if a platform needs to be removed from the mission planning)
"""
from formats import abstract_schema
emergency_schema = {
"type": "object",
......@@ -160,6 +159,11 @@ squad_metadata_schema = {
planning_configuration_schema = {
"type": "object",
"properties": {
"message_type": {
"type": "string",
"description": "Type of message",
"example": "planning_configuration",
},
"planning_config_ID": {
"type": "integer",
"description": "Unique identifier tagged to version of this"
......@@ -177,11 +181,9 @@ planning_configuration_schema = {
},
},
"required": [
"message_type",
"config_ID",
"squads",
"exclusion_zones",
],
}
full_planning_configuration_schema = abstract_schema
full_planning_configuration_schema["properties"]["payload"] = planning_configuration_schema
"""
schema: platform-specific decoded status message
"""
from formats import abstract_schema
sensor_schema = {
"type": "object",
......@@ -30,6 +29,11 @@ sensor_schema = {
platform_status_message_schema = {
"type": "object",
"properties": {
"message_type": {
"type": "string",
"description": "Type of message",
"example": "platform_status",
},
"platform_ID": {
"type": "integer",
"description": "Identifier for platform",
......@@ -175,6 +179,7 @@ platform_status_message_schema = {
"sensor_config": sensor_schema,
},
"required": [
"message_type",
"platform_ID",
"status_source",
"platform_timestamp",
......@@ -182,7 +187,3 @@ platform_status_message_schema = {
"longitude",
],
}
full_platform_status_message_schema = abstract_schema
full_platform_status_message_schema["properties"]["payload"] = platform_status_message_schema
from formats import message_header
from formats.mission_plan import mission_plan_schema
from formats.observation import observation_schema
from formats.planning_configuration import planning_configuration_schema
from formats.platform_status import platform_status_message_schema
from formats.acknowledgement import acknowledgement_schema
from flasgger import Swagger
from flask import Flask
app = Flask(__name__)
swagger_config = {
"openapi": "3.0.2",
"swagger_ui": True,
"specs_route": "/",
"info": {
"title": "SoAR Backbone Message Formats",
"version": "1.0",
"description": "SoAR message protocol in schemas"
},
"specs": [
{
"endpoint": "swagger",
"route": "/soar_protocol.json",
}
],
"paths": {},
"components": {
"schemas": {
"MESSAGE": {
"type": "object",
"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"],
},
"payload": {
"discriminator": {
"propertyName": "message_type",
"mapping":{
"MissionPlan": "#/components/schemas/mission_plan",
"Observation": "#/components/schemas/observation",
"PlanningConfiguration": "#/components/schemas/planning_configuration",
"PlatformStatus": "#/components/schemas/platform_status",
"Acknowledgement": "#/components/schemas/acknowledgement",
},
},
"oneOf":[
{
"$ref": "#/components/schemas/"
+ "acknowledgement"
},
{
"$ref": "#/components/schemas/"
+ "mission_plan"
},
{
"$ref": "#/components/schemas/"
+ "observation"
},
{
"$ref": "#/components/schemas/"
+ "planning_configuration"
},
{
"$ref": "#/components/schemas/"
+ "platform_status"
},
]
},
"header": message_header,
"mission_plan": mission_plan_schema,
"observation": observation_schema,
"planning_configuration": planning_configuration_schema,
"platform_status": platform_status_message_schema,
"acknowledgement": acknowledgement_schema,
}
},
}
swag = Swagger(app, config=swagger_config, merge=True)
if __name__ == "__main__":
app.run(debug=True)
\ No newline at end of file
from formats import message_metadata
from formats.mission_plan import full_mission_plan_schema
from formats.observation import full_observation_schema
from formats.planning_configuration import full_planning_configuration_schema
from formats.platform_status import full_platform_status_message_schema
from formats.acknowledgement import full_acknowledgement_schema
from flasgger import Swagger
from flask import Flask
app = Flask(__name__)
swagger_config = {
"headers": [],
"openapi": "3.0.2",
"swagger_ui": True,
"specs_route": "/",
"info": {
"title": "Backbone Message Formats",
"version": "0.1",
"description": "SoAR message schemas (i.e. formats)",
},
"specs": [
{
"endpoint": "swagger",
"route": "/swagger.json",
"rule_filter": lambda rule: True,
"model_filter": lambda tag: True,
}
],
"components": {
"schemas": {
"Metadata": message_metadata,
"MissionPlan": full_mission_plan_schema,
"Observation": full_observation_schema,
"PlanningConfiguration": full_planning_configuration_schema,
"PlatformStatus": full_platform_status_message_schema,
"Acknowledgement": full_acknowledgement_schema,
}
},
"paths": {
"/all_messages": {
"get": {
"description": "Returns all messages from the system.",
"responses": {
"200": {
"description": "A list of messages.",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/"
+ "Acknowledgement"
},
{
"$ref": "#/components/schemas/"
+ "MissionPlan"
},
{
"$ref": "#/components/schemas/"
+ "Observation"
},
{
"$ref": "#/components/schemas/"
+ "PlanningConfiguration"
},
{
"$ref": "#/components/schemas/"
+ "PlatformStatus"
},
],
"discriminator": {
"propertyName": "message_type",
},
}
}
},
}
},
}
},
},
"produces": ["application/json"],
"consumes": ["application/json"],
}
message_types = [
"Metadata",
# "Acknowledgement",
"MissionPlan",
"Observation",
"PlanningConfiguration",
"PlatformStatus",
]
for item in message_types:
swagger_config["paths"]["/" + str(item.lower())] = {
"get": {
"description": "Returns message for " + item,
"responses": {
"200": {
"description": item + " message.",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/" + item,
},
],
"discriminator": {
"propertyName": "message_type",
},
}
}
},
}
},
}
}
swag = Swagger(app, config=swagger_config, merge=True)
if __name__ == "__main__":
app.run(debug=True)
# print(validate_spec_url("http://127.0.0.1:5000/swagger.json"))
# # If no exception is raised by validate_spec(), the spec is valid.
# validate_spec(swagger_config)
# print("000 Schema validation passed")
# errors_iterator = openapi_v3_spec_validator.iter_errors(swagger_config)
# print(errors_iterator)
File moved
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment