Make message registries use 2 digit versions

Redfish specification, section 9.5.11.2 says:

The MessageId property value shall be in the format:
<MessageRegistryPrefix>.<MajorVersion>.<MinorVersion>.<MessageKey>

bmcweb in certain places has incorrectly used the 3 digit version
instead of the 2 digit version.  This commit fixes that by modifying the
parse_registries script to generate 3 separate struct entries to
represent the registry version, and parse them where appropriate.

MessageRegistryFileCollection uses the 3 digit version.  No behavior
changes.
Message/event log entries use the 2 digit version.  This will cause a
MessageId change from:
Base.1.19.0.InternalError
to
Base.1.19.InternalError

This is a breaking change, so a new option to allow the old behavior is
provided.

Tested: Redfish Service validator passes.
Heartbeat events on EventService show 2 digit versions.

Change-Id: I4165e994f73e200f13bed8ea76cb58bee2b69faa
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/config/meson.build b/config/meson.build
index d338b77..be54d2d 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -23,6 +23,7 @@
     'mutual-tls-auth',
     'redfish-aggregation',
     'redfish-allow-deprecated-power-thermal',
+    'redfish-use-3-digit-messageid',
     'redfish-bmc-journal',
     'redfish-cpu-log',
     'redfish-dbus-log',
diff --git a/meson.options b/meson.options
index d5b44bd..915f427 100644
--- a/meson.options
+++ b/meson.options
@@ -25,6 +25,17 @@
                     https://github.com/openbmc/jsnbd/blob/master/README.''',
 )
 
+option(
+    'redfish-use-3-digit-messageid',
+    type: 'feature',
+    value: 'disabled',
+    description: '''Prior to a bug fix, bmcweb exposed error messages with a
+                    MessageId of Base.x.y.z.Message which was incorrect.
+                    Enabling this option causes return codes to return the old
+                    incorrect version for backward compatibility.  Will be
+                    removed Q2-2025'''
+)
+
 # BMCWEB_NBDPROXY
 # if you use this option and are seeing this comment, please comment here:
 # https://github.com/openbmc/bmcweb/issues/188 and put forward your intentions
diff --git a/redfish-core/include/registries.hpp b/redfish-core/include/registries.hpp
index f7aa32b..ce6d7b8 100644
--- a/redfish-core/include/registries.hpp
+++ b/redfish-core/include/registries.hpp
@@ -15,11 +15,14 @@
 */
 #pragma once
 
+#include "bmcweb_config.h"
+
 #include <nlohmann/json.hpp>
 
 #include <array>
 #include <charconv>
 #include <cstddef>
+#include <format>
 #include <numeric>
 #include <span>
 #include <string>
@@ -32,12 +35,13 @@
 {
     const char* copyright;
     const char* type;
-    const char* id;
+    unsigned int versionMajor;
+    unsigned int versionMinor;
+    unsigned int versionPatch;
     const char* name;
     const char* language;
     const char* description;
     const char* registryPrefix;
-    const char* registryVersion;
     const char* owningEntity;
 };
 
@@ -101,10 +105,19 @@
     {
         jArgs.push_back(arg);
     }
-    std::string msgId = header.id;
-    msgId += ".";
-    msgId += entry.first;
-
+    std::string msgId;
+    if (BMCWEB_REDFISH_USE_3_DIGIT_MESSAGEID)
+    {
+        msgId = std::format("{}.{}.{}.{}.{}", header.registryPrefix,
+                            header.versionMajor, header.versionMinor,
+                            header.versionPatch, entry.first);
+    }
+    else
+    {
+        msgId =
+            std::format("{}.{}.{}.{}", header.registryPrefix,
+                        header.versionMajor, header.versionMinor, entry.first);
+    }
     nlohmann::json::object_t response;
     response["@odata.type"] = "#Message.v1_1_1.Message";
     response["MessageId"] = std::move(msgId);
diff --git a/redfish-core/include/registries/base_message_registry.hpp b/redfish-core/include/registries/base_message_registry.hpp
index 8f89a6f..755833f 100644
--- a/redfish-core/include/registries/base_message_registry.hpp
+++ b/redfish-core/include/registries/base_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2014-2024 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "Base.1.19.0",
+    1,
+    19,
+    0,
     "Base Message Registry",
     "en",
     "This registry defines the base messages for Redfish.",
     "Base",
-    "1.19.0",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/composition_message_registry.hpp b/redfish-core/include/registries/composition_message_registry.hpp
index a566083..9af663f 100644
--- a/redfish-core/include/registries/composition_message_registry.hpp
+++ b/redfish-core/include/registries/composition_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2019-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "Composition.1.1.2",
+    1,
+    1,
+    2,
     "Composition Message Registry",
     "en",
     "This registry defines the messages for composition related events.",
     "Composition",
-    "1.1.2",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/environmental_message_registry.hpp b/redfish-core/include/registries/environmental_message_registry.hpp
index 9267240..dcb6181 100644
--- a/redfish-core/include/registries/environmental_message_registry.hpp
+++ b/redfish-core/include/registries/environmental_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "Environmental.1.0.1",
+    1,
+    0,
+    1,
     "Environmental Message Registry",
     "en",
     "This registry defines messages related to environmental sensors, heating and cooling equipment, or other environmental conditions.",
     "Environmental",
-    "1.0.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/ethernet_fabric_message_registry.hpp b/redfish-core/include/registries/ethernet_fabric_message_registry.hpp
index 5b0f377..dc4d380 100644
--- a/redfish-core/include/registries/ethernet_fabric_message_registry.hpp
+++ b/redfish-core/include/registries/ethernet_fabric_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2020-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "EthernetFabric.1.0.1",
+    1,
+    0,
+    1,
     "Ethernet Fabric Message Registry",
     "en",
     "This registry defines messages for Ethernet fabrics.",
     "EthernetFabric",
-    "1.0.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/fabric_message_registry.hpp b/redfish-core/include/registries/fabric_message_registry.hpp
index 8b8f236..92fca2c 100644
--- a/redfish-core/include/registries/fabric_message_registry.hpp
+++ b/redfish-core/include/registries/fabric_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2014-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "Fabric.1.0.2",
+    1,
+    0,
+    2,
     "Fabric Message Registry",
     "en",
     "This registry defines messages for generic fabrics.",
     "Fabric",
-    "1.0.2",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/heartbeat_event_message_registry.hpp b/redfish-core/include/registries/heartbeat_event_message_registry.hpp
index 71d5b03..9592224 100644
--- a/redfish-core/include/registries/heartbeat_event_message_registry.hpp
+++ b/redfish-core/include/registries/heartbeat_event_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2021-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "HeartbeatEvent.1.0.1",
+    1,
+    0,
+    1,
     "Heartbeat Event Message Registry",
     "en",
     "This registry defines the messages to use for periodic heartbeat, also known as 'keep alive', events.",
     "HeartbeatEvent",
-    "1.0.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/job_event_message_registry.hpp b/redfish-core/include/registries/job_event_message_registry.hpp
index a6d9378..eaf8ecd 100644
--- a/redfish-core/include/registries/job_event_message_registry.hpp
+++ b/redfish-core/include/registries/job_event_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2014-2023 DMTF in cooperation with the Storage Networking Industry Association (SNIA). All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "JobEvent.1.0.1",
+    1,
+    0,
+    1,
     "Job Event Message Registry",
     "en",
     "This registry defines the messages for job related events.",
     "JobEvent",
-    "1.0.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/license_message_registry.hpp b/redfish-core/include/registries/license_message_registry.hpp
index 1132cb4..8427329 100644
--- a/redfish-core/include/registries/license_message_registry.hpp
+++ b/redfish-core/include/registries/license_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2014-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "License.1.0.3",
+    1,
+    0,
+    3,
     "License Message Registry",
     "en",
     "This registry defines the license status and error messages.",
     "License",
-    "1.0.3",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/log_service_message_registry.hpp b/redfish-core/include/registries/log_service_message_registry.hpp
index b855323..a0b126f 100644
--- a/redfish-core/include/registries/log_service_message_registry.hpp
+++ b/redfish-core/include/registries/log_service_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2020-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "LogService.1.0.1",
+    1,
+    0,
+    1,
     "Log Service Message Registry",
     "en",
     "This registry defines the messages for log service related events.",
     "LogService",
-    "1.0.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/network_device_message_registry.hpp b/redfish-core/include/registries/network_device_message_registry.hpp
index c7c54ea..70461a2 100644
--- a/redfish-core/include/registries/network_device_message_registry.hpp
+++ b/redfish-core/include/registries/network_device_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2019-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "NetworkDevice.1.0.3",
+    1,
+    0,
+    3,
     "Network Device Message Registry",
     "en",
     "This registry defines the messages for networking devices.",
     "NetworkDevice",
-    "1.0.3",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp
index 2bd4d1d..e572622 100644
--- a/redfish-core/include/registries/openbmc_message_registry.hpp
+++ b/redfish-core/include/registries/openbmc_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2022 OpenBMC. All rights reserved.",
     "#MessageRegistry.v1_4_0.MessageRegistry",
-    "OpenBMC.0.5.0",
+    0,
+    5,
+    0,
     "OpenBMC Message Registry",
     "en",
     "This registry defines the base messages for OpenBMC.",
     "OpenBMC",
-    "0.5.0",
     "OpenBMC",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/platform_message_registry.hpp b/redfish-core/include/registries/platform_message_registry.hpp
index 4af3ed2..af5a6f0 100644
--- a/redfish-core/include/registries/platform_message_registry.hpp
+++ b/redfish-core/include/registries/platform_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "Platform.1.0.1",
+    1,
+    0,
+    1,
     "Compute Platform Message Registry",
     "en",
     "This registry defines messages for compute platforms, covering topics related to processor, memory, and I/O device connectivity.",
     "Platform",
-    "1.0.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/power_message_registry.hpp b/redfish-core/include/registries/power_message_registry.hpp
index 5db4262..0cf039f 100644
--- a/redfish-core/include/registries/power_message_registry.hpp
+++ b/redfish-core/include/registries/power_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "Power.1.0.1",
+    1,
+    0,
+    1,
     "Power Message Registry",
     "en",
     "This registry defines messages related to electrical measurements and power distribution equipment.",
     "Power",
-    "1.0.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/resource_event_message_registry.hpp b/redfish-core/include/registries/resource_event_message_registry.hpp
index 126c277..dc192ee 100644
--- a/redfish-core/include/registries/resource_event_message_registry.hpp
+++ b/redfish-core/include/registries/resource_event_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2014-2023 DMTF in cooperation with the Storage Networking Industry Association (SNIA). All rights reserved.",
     "#MessageRegistry.v1_6_0.MessageRegistry",
-    "ResourceEvent.1.3.0",
+    1,
+    3,
+    0,
     "Resource Event Message Registry",
     "en",
     "This registry defines the messages to use for resource events.",
     "ResourceEvent",
-    "1.3.0",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/sensor_event_message_registry.hpp b/redfish-core/include/registries/sensor_event_message_registry.hpp
index 4692c8e..18c40d5 100644
--- a/redfish-core/include/registries/sensor_event_message_registry.hpp
+++ b/redfish-core/include/registries/sensor_event_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2022-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "SensorEvent.1.0.1",
+    1,
+    0,
+    1,
     "Sensor Event Message Registry",
     "en",
     "This registry defines messages used for general events related to Sensor resources.",
     "SensorEvent",
-    "1.0.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/storage_device_message_registry.hpp b/redfish-core/include/registries/storage_device_message_registry.hpp
index cf60d8a..8dfaff6 100644
--- a/redfish-core/include/registries/storage_device_message_registry.hpp
+++ b/redfish-core/include/registries/storage_device_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2020-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "StorageDevice.1.2.1",
+    1,
+    2,
+    1,
     "Storage Device Message Registry",
     "en",
     "This registry defines the messages for storage devices.",
     "StorageDevice",
-    "1.2.1",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/task_event_message_registry.hpp b/redfish-core/include/registries/task_event_message_registry.hpp
index 627ef13..ed59e7d 100644
--- a/redfish-core/include/registries/task_event_message_registry.hpp
+++ b/redfish-core/include/registries/task_event_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2014-2020 DMTF in cooperation with the Storage Networking Industry Association (SNIA). All rights reserved.",
     "#MessageRegistry.v1_4_1.MessageRegistry",
-    "TaskEvent.1.0.3",
+    1,
+    0,
+    3,
     "Task Event Message Registry",
     "en",
     "This registry defines the messages for task related events.",
     "TaskEvent",
-    "1.0.3",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/telemetry_message_registry.hpp b/redfish-core/include/registries/telemetry_message_registry.hpp
index 6b1135e..720ec50 100644
--- a/redfish-core/include/registries/telemetry_message_registry.hpp
+++ b/redfish-core/include/registries/telemetry_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "Telemetry.1.0.0",
+    1,
+    0,
+    0,
     "Telemetry Message Registry",
     "en",
     "This registry defines the messages for telemetry related events.",
     "Telemetry",
-    "1.0.0",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/include/registries/update_message_registry.hpp b/redfish-core/include/registries/update_message_registry.hpp
index 1c7b492..22686f6 100644
--- a/redfish-core/include/registries/update_message_registry.hpp
+++ b/redfish-core/include/registries/update_message_registry.hpp
@@ -20,12 +20,13 @@
 const Header header = {
     "Copyright 2014-2023 DMTF. All rights reserved.",
     "#MessageRegistry.v1_6_2.MessageRegistry",
-    "Update.1.0.2",
+    1,
+    0,
+    2,
     "Update Message Registry",
     "en",
     "This registry defines the update status and error messages.",
     "Update",
-    "1.0.2",
     "DMTF",
 };
 constexpr const char* url =
diff --git a/redfish-core/lib/message_registries.hpp b/redfish-core/lib/message_registries.hpp
index a6e108e..b327d63 100644
--- a/redfish-core/lib/message_registries.hpp
+++ b/redfish-core/lib/message_registries.hpp
@@ -23,6 +23,7 @@
 #include <boost/url/format.hpp>
 
 #include <array>
+#include <format>
 
 namespace redfish
 {
@@ -106,7 +107,9 @@
     asyncResp->res.jsonValue["Description"] =
         dmtf + registry + " Message Registry File Location";
     asyncResp->res.jsonValue["Id"] = header.registryPrefix;
-    asyncResp->res.jsonValue["Registry"] = header.id;
+    asyncResp->res.jsonValue["Registry"] =
+        std::format("{}.{}.{}", header.registryPrefix, header.versionMajor,
+                    header.versionMinor);
     nlohmann::json::array_t languages;
     languages.emplace_back(header.language);
     asyncResp->res.jsonValue["Languages@odata.count"] = languages.size();
@@ -162,12 +165,16 @@
 
     asyncResp->res.jsonValue["@Redfish.Copyright"] = header.copyright;
     asyncResp->res.jsonValue["@odata.type"] = header.type;
-    asyncResp->res.jsonValue["Id"] = header.id;
+    asyncResp->res.jsonValue["Id"] =
+        std::format("{}.{}.{}.{}", header.registryPrefix, header.versionMajor,
+                    header.versionMinor, header.versionPatch);
     asyncResp->res.jsonValue["Name"] = header.name;
     asyncResp->res.jsonValue["Language"] = header.language;
     asyncResp->res.jsonValue["Description"] = header.description;
     asyncResp->res.jsonValue["RegistryPrefix"] = header.registryPrefix;
-    asyncResp->res.jsonValue["RegistryVersion"] = header.registryVersion;
+    asyncResp->res.jsonValue["RegistryVersion"] =
+        std::format("{}.{}.{}", header.versionMajor, header.versionMinor,
+                    header.versionPatch);
     asyncResp->res.jsonValue["OwningEntity"] = header.owningEntity;
 
     nlohmann::json& messageObj = asyncResp->res.jsonValue["Messages"];
diff --git a/scripts/parse_registries.py b/scripts/parse_registries.py
index 0311adb..5562120 100755
--- a/scripts/parse_registries.py
+++ b/scripts/parse_registries.py
@@ -81,18 +81,22 @@
             print("{} not found".format(file))
 
         with open(file, "w") as registry:
+
+            version_split = json_dict["RegistryVersion"].split(".")
+
             registry.write(REGISTRY_HEADER.format(namespace))
             # Parse the Registry header info
             registry.write(
                 "const Header header = {{\n"
                 '    "{json_dict[@Redfish.Copyright]}",\n'
                 '    "{json_dict[@odata.type]}",\n'
-                '    "{json_dict[Id]}",\n'
+                "    {version_split[0]},\n"
+                "    {version_split[1]},\n"
+                "    {version_split[2]},\n"
                 '    "{json_dict[Name]}",\n'
                 '    "{json_dict[Language]}",\n'
                 '    "{json_dict[Description]}",\n'
                 '    "{json_dict[RegistryPrefix]}",\n'
-                '    "{json_dict[RegistryVersion]}",\n'
                 '    "{json_dict[OwningEntity]}",\n'
                 "}};\n"
                 "constexpr const char* url =\n"
@@ -102,6 +106,7 @@
                 "{{\n".format(
                     json_dict=json_dict,
                     url=url,
+                    version_split=version_split,
                 )
             )
 
diff --git a/test/redfish-core/include/utils/dbus_utils.cpp b/test/redfish-core/include/utils/dbus_utils.cpp
index 6a941c2..ebde409 100644
--- a/test/redfish-core/include/utils/dbus_utils.cpp
+++ b/test/redfish-core/include/utils/dbus_utils.cpp
@@ -50,7 +50,7 @@
                             "@odata.type": "#Message.v1_1_1.Message",
                             "Message": "The request completed successfully.",
                             "MessageArgs": [],
-                            "MessageId": "Base.1.19.0.Success",
+                            "MessageId": "Base.1.19.Success",
                             "MessageSeverity": "OK",
                             "Resolution": "None."
                         }
@@ -82,12 +82,12 @@
                         "@odata.type": "#Message.v1_1_1.Message",
                         "Message": "The request failed due to an internal service error.  The service is still operational.",
                         "MessageArgs": [],
-                        "MessageId": "Base.1.19.0.InternalError",
+                        "MessageId": "Base.1.19.InternalError",
                         "MessageSeverity": "Critical",
                         "Resolution": "Resubmit the request.  If the problem persists, consider resetting the service."
                         }
                     ],
-                    "code": "Base.1.19.0.InternalError",
+                    "code": "Base.1.19.InternalError",
                     "message": "The request failed due to an internal service error.  The service is still operational."
                     }
                 })"_json);
diff --git a/test/redfish-core/include/utils/json_utils_test.cpp b/test/redfish-core/include/utils/json_utils_test.cpp
index 93671cc..76bcc7b 100644
--- a/test/redfish-core/include/utils/json_utils_test.cpp
+++ b/test/redfish-core/include/utils/json_utils_test.cpp
@@ -388,7 +388,7 @@
         res.jsonValue["error"]["@Message.ExtendedInfo"];
     EXPECT_THAT(resExtInfo[0]["@odata.type"], "#Message.v1_1_1.Message");
     EXPECT_THAT(resExtInfo[0]["MessageId"],
-                "Base.1.19.0.PropertyValueOutOfRange");
+                "Base.1.19.PropertyValueOutOfRange");
     EXPECT_THAT(resExtInfo[0]["MessageSeverity"], "Warning");
 }