From ad5a448bab6b4c33368d7d42b462bdd8fa048ce9 Mon Sep 17 00:00:00 2001
From: Dan Jones <danjon@noc.ac.uk>
Date: Fri, 6 Sep 2024 09:51:56 +0100
Subject: [PATCH] refactor: make version a oneof to match git refs

+ move all schemas into formats
+ add test messages for semver, commit ref and branch
---
 examples/mas-dt/platform_status_branch.json |  24 +++++
 examples/mas-dt/platform_status_commit.json |  24 +++++
 examples/mas-dt/platform_status_semver.json |  24 +++++
 formats/__init__.py                         |  50 ----------
 formats/header.py                           | 100 ++++++++++++++++++++
 formats/message.py                          |  13 +++
 formats/payload.py                          |  33 +++++++
 generate_schema_config.py                   |  55 +----------
 project/soar/swagger.json                   |  72 ++++++++++++--
 9 files changed, 286 insertions(+), 109 deletions(-)
 create mode 100644 examples/mas-dt/platform_status_branch.json
 create mode 100644 examples/mas-dt/platform_status_commit.json
 create mode 100644 examples/mas-dt/platform_status_semver.json
 create mode 100644 formats/header.py
 create mode 100644 formats/message.py
 create mode 100644 formats/payload.py

diff --git a/examples/mas-dt/platform_status_branch.json b/examples/mas-dt/platform_status_branch.json
new file mode 100644
index 0000000..1081184
--- /dev/null
+++ b/examples/mas-dt/platform_status_branch.json
@@ -0,0 +1,24 @@
+{
+    "header":{
+        "message_ID": "b427003c-0000-11aa-a1eb-bvcdfghjgfdd",
+        "timestamp": "2024-09-05T00:00:00Z",
+        "version": "dev",
+        "source": "noc-sfmc",
+        "destination": "mas-dt.noc.slocum.unit_xxx.from_platform.platform_status",
+        "delivery_type": "publish",
+        "encoded": false
+    },
+    "payload":{
+        "message_type": "platform_status",
+        "platform_ID": "unit_xxx",
+        "platform_timestamp": "2024-09-05T00:00:00Z",
+        "status_source": "onboard_platform",
+        "autonomy_engine_plan_ID": 1,
+        "mission_plan_ID": 1,
+        "mission_track_ID": 4,
+        "latitude": 78.2,
+        "longitude": -10.122,
+        "depth": 50.0,
+        "altitude": 20
+    }
+}
diff --git a/examples/mas-dt/platform_status_commit.json b/examples/mas-dt/platform_status_commit.json
new file mode 100644
index 0000000..c832425
--- /dev/null
+++ b/examples/mas-dt/platform_status_commit.json
@@ -0,0 +1,24 @@
+{
+    "header":{
+        "message_ID": "b427003c-0000-11aa-a1eb-bvcdfghjgfdd",
+        "timestamp": "2024-09-05T00:00:00Z",
+        "version": "2bbede2412f9038c0b1ecd8c2965532ea97264a3",
+        "source": "noc-sfmc",
+        "destination": "mas-dt.noc.slocum.unit_xxx.from_platform.platform_status",
+        "delivery_type": "publish",
+        "encoded": false
+    },
+    "payload":{
+        "message_type": "platform_status",
+        "platform_ID": "unit_xxx",
+        "platform_timestamp": "2024-09-05T00:00:00Z",
+        "status_source": "onboard_platform",
+        "autonomy_engine_plan_ID": 1,
+        "mission_plan_ID": 1,
+        "mission_track_ID": 4,
+        "latitude": 78.2,
+        "longitude": -10.122,
+        "depth": 50.0,
+        "altitude": 20
+    }
+}
diff --git a/examples/mas-dt/platform_status_semver.json b/examples/mas-dt/platform_status_semver.json
new file mode 100644
index 0000000..529e91f
--- /dev/null
+++ b/examples/mas-dt/platform_status_semver.json
@@ -0,0 +1,24 @@
+{
+    "header":{
+        "message_ID": "b427003c-0000-11aa-a1eb-bvcdfghjgfdd",
+        "timestamp": "2024-09-05T00:00:00Z",
+        "version": "v2.0.0-beta.1",
+        "source": "noc-sfmc",
+        "destination": "mas-dt.noc.slocum.unit_xxx.from_platform.platform_status",
+        "delivery_type": "publish",
+        "encoded": false
+    },
+    "payload":{
+        "message_type": "platform_status",
+        "platform_ID": "unit_xxx",
+        "platform_timestamp": "2024-09-05T00:00:00Z",
+        "status_source": "onboard_platform",
+        "autonomy_engine_plan_ID": 1,
+        "mission_plan_ID": 1,
+        "mission_track_ID": 4,
+        "latitude": 78.2,
+        "longitude": -10.122,
+        "depth": 50.0,
+        "altitude": 20
+    }
+}
diff --git a/formats/__init__.py b/formats/__init__.py
index 96c6be0..2253a07 100644
--- a/formats/__init__.py
+++ b/formats/__init__.py
@@ -6,53 +6,3 @@ __all__ = [
     for x in os.listdir(os.path.dirname(__file__))
     if x.endswith(".py") and x != "__init__.py"
 ]
-
-message_header = {
-    "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",
-        },
-        "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",
-        },
-    },
-}
diff --git a/formats/header.py b/formats/header.py
new file mode 100644
index 0000000..f857660
--- /dev/null
+++ b/formats/header.py
@@ -0,0 +1,100 @@
+message_header = {
+    "type": "object",
+    "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",
+        },
+        "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",
+        },
+    },
+    "oneOf": [
+        {
+            # semver pattern https://semver.org/
+            # https://regex101.com/r/Ly7O1x/3/
+            # + optionally include 'v' prefix
+            # escape backslashes get doubled so use char groups
+            # simplify to planned variants
+            "type": "object",
+            "properties": {
+                "version": {
+                    "type": "string",
+                    "pattern": "^(v{0,1}(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)"
+                    + "(?:-(alpha|beta)[.](0|[1-9][0-9]*))?)$",
+                }
+            },
+            "required": ["version"],
+        },
+        {
+            # commit ref
+            "type": "object",
+            "properties": {
+                "version": {
+                    "type": "string",
+                    "pattern": "^([0-9a-f]+)$",
+                }
+            },
+            "required": ["version"],
+        },
+        {
+            # reserved word
+            "type": "object",
+            "properties": {
+                "version": {
+                    "type": "string",
+                    "enum": ["dev", "master", "latest"],
+                }
+            },
+            "required": ["version"],
+        },
+        {
+            # existing float version
+            "type": "object",
+            "properties": {
+                "version": {
+                    "type": "number",
+                    "format": "float",
+                    "example": 2.0,
+                }
+            },
+            "required": ["version"],
+        },
+    ],
+    "required": [
+        "message_ID",
+        "timestamp",
+        "source",
+        "destination",
+        "delivery_type",
+        "encoded",
+    ],
+}
diff --git a/formats/message.py b/formats/message.py
new file mode 100644
index 0000000..b944c48
--- /dev/null
+++ b/formats/message.py
@@ -0,0 +1,13 @@
+message_schema = {
+    "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"],
+}
diff --git a/formats/payload.py b/formats/payload.py
new file mode 100644
index 0000000..062527f
--- /dev/null
+++ b/formats/payload.py
@@ -0,0 +1,33 @@
+payload_schema = {
+    "discriminator": {
+        "propertyName": "message_type",
+        "mapping": {
+            "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",
+            "acknowledgement": "#/components/schemas/acknowledgement",
+            "survey": "#/components/schemas/survey",
+            "survey_encoded": "#/components/schemas/" + "survey_encoded",
+        },
+    },
+    "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"},
+    ],
+}
diff --git a/generate_schema_config.py b/generate_schema_config.py
index 3d223a1..57cadb6 100644
--- a/generate_schema_config.py
+++ b/generate_schema_config.py
@@ -1,8 +1,10 @@
-from formats import message_header
+from formats.header import message_header
+from formats.message import message_schema
 from formats.mission_plan import mission_plan_schema
 from formats.mission_plan_encoded import mission_plan_encoded_schema
 from formats.observation import observation_schema
 from formats.observation_encoded import observation_encoded_schema
+from formats.payload import payload_schema
 from formats.planning_configuration import planning_configuration_schema
 from formats.platform_status import platform_status_schema
 from formats.platform_status_encoded import platform_status_encoded_schema
@@ -55,56 +57,9 @@ def get_swagger_config(reload=False):
         "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": {
-                            "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",
-                            "acknowledgement": "#/components/schemas/acknowledgement",
-                            "survey": "#/components/schemas/survey",
-                            "survey_encoded": "#/components/schemas/"
-                            + "survey_encoded",
-                        },
-                    },
-                    "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"},
-                    ],
-                },
+                "MESSAGE": message_schema,
                 "header": message_header,
+                "payload": payload_schema,
                 "mission_plan": mission_plan_schema,
                 "mission_plan_encoded": mission_plan_encoded_schema,
                 "observation": observation_schema,
diff --git a/project/soar/swagger.json b/project/soar/swagger.json
index b61da63..aaee112 100644
--- a/project/soar/swagger.json
+++ b/project/soar/swagger.json
@@ -1343,9 +1343,61 @@
         "type": "object"
       },
       "header": {
-        "discriminator": {
-          "propertyName": "message_type"
-        },
+        "oneOf": [
+          {
+            "properties": {
+              "version": {
+                "pattern": "^(v{0,1}(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)(?:-(alpha|beta)[.](0|[1-9][0-9]*))?)$",
+                "type": "string"
+              }
+            },
+            "required": [
+              "version"
+            ],
+            "type": "object"
+          },
+          {
+            "properties": {
+              "version": {
+                "pattern": "^([0-9a-f]+)$",
+                "type": "string"
+              }
+            },
+            "required": [
+              "version"
+            ],
+            "type": "object"
+          },
+          {
+            "properties": {
+              "version": {
+                "enum": [
+                  "dev",
+                  "master",
+                  "latest"
+                ],
+                "type": "string"
+              }
+            },
+            "required": [
+              "version"
+            ],
+            "type": "object"
+          },
+          {
+            "properties": {
+              "version": {
+                "example": 2.0,
+                "format": "float",
+                "type": "number"
+              }
+            },
+            "required": [
+              "version"
+            ],
+            "type": "object"
+          }
+        ],
         "properties": {
           "delivery_type": {
             "default": "publish",
@@ -1382,14 +1434,16 @@
             "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"
           }
         },
+        "required": [
+          "message_ID",
+          "timestamp",
+          "source",
+          "destination",
+          "delivery_type",
+          "encoded"
+        ],
         "type": "object"
       },
       "mission_plan": {
-- 
GitLab