diff --git a/components/core/src/clp/ffi/ir_stream/Deserializer.hpp b/components/core/src/clp/ffi/ir_stream/Deserializer.hpp index c1fc13c85..45cca8f26 100644 --- a/components/core/src/clp/ffi/ir_stream/Deserializer.hpp +++ b/components/core/src/clp/ffi/ir_stream/Deserializer.hpp @@ -154,10 +154,8 @@ auto Deserializer::create(ReaderInterface& reader, IrUnitHandler return std::errc::protocol_error; } auto const version = version_iter->get_ref(); - // TODO: Just before the KV-pair IR format is formally released, we should replace this - // hard-coded version check with `ffi::ir_stream::validate_protocol_version`. - if (std::string_view{static_cast(cProtocol::Metadata::BetaVersionValue)} - != version) + if (ffi::ir_stream::IRProtocolErrorCode::Supported + != ffi::ir_stream::validate_protocol_version(version)) { return std::errc::protocol_not_supported; } diff --git a/components/core/src/clp/ffi/ir_stream/Serializer.cpp b/components/core/src/clp/ffi/ir_stream/Serializer.cpp index 01215eb9d..b29cf0492 100644 --- a/components/core/src/clp/ffi/ir_stream/Serializer.cpp +++ b/components/core/src/clp/ffi/ir_stream/Serializer.cpp @@ -253,7 +253,7 @@ auto Serializer::create( ir_buf.insert(ir_buf.cend(), cMagicNumber.begin(), cMagicNumber.end()); nlohmann::json metadata; - metadata.emplace(cProtocol::Metadata::VersionKey, cProtocol::Metadata::BetaVersionValue); + metadata.emplace(cProtocol::Metadata::VersionKey, cProtocol::Metadata::VersionValue); metadata.emplace(cProtocol::Metadata::VariablesSchemaIdKey, cVariablesSchemaVersion); metadata.emplace( cProtocol::Metadata::VariableEncodingMethodsIdKey, diff --git a/components/core/src/clp/ffi/ir_stream/decoding_methods.cpp b/components/core/src/clp/ffi/ir_stream/decoding_methods.cpp index 9388470e4..f61efc1df 100644 --- a/components/core/src/clp/ffi/ir_stream/decoding_methods.cpp +++ b/components/core/src/clp/ffi/ir_stream/decoding_methods.cpp @@ -1,6 +1,10 @@ #include "decoding_methods.hpp" +#include +#include #include +#include +#include #include "../../ir/types.hpp" #include "byteswap.hpp" @@ -468,13 +472,23 @@ IRErrorCode deserialize_preamble( return IRErrorCode_Success; } -IRProtocolErrorCode validate_protocol_version(std::string_view protocol_version) { - if ("v0.0.0" == protocol_version) { - // This version is hardcoded to support the oldest IR protocol version. When this version is - // no longer supported, this branch should be removed. - return IRProtocolErrorCode_Supported; +auto validate_protocol_version(std::string_view protocol_version) -> IRProtocolErrorCode { + // These versions are hardcoded to support the IR protocol version that predates the key-value + // pair IR format. + constexpr std::array cBackwardCompatibleVersions{ + "v0.0.0", + "0.0.1", + cProtocol::Metadata::LatestBackwardCompatibleVersion + }; + if (cBackwardCompatibleVersions.cend() + != std::ranges::find(cBackwardCompatibleVersions, protocol_version)) + { + return IRProtocolErrorCode::BackwardCompatible; } - std::regex const protocol_version_regex{cProtocol::Metadata::VersionRegex}; + + std::regex const protocol_version_regex{ + static_cast(cProtocol::Metadata::VersionRegex) + }; if (false == std::regex_match( protocol_version.begin(), @@ -482,19 +496,16 @@ IRProtocolErrorCode validate_protocol_version(std::string_view protocol_version) protocol_version_regex )) { - return IRProtocolErrorCode_Invalid; + return IRProtocolErrorCode::Invalid; } - std::string_view current_build_protocol_version{cProtocol::Metadata::VersionValue}; - auto get_major_version{[](std::string_view version) { - return version.substr(0, version.find('.')); - }}; - if (current_build_protocol_version < protocol_version) { - return IRProtocolErrorCode_Too_New; - } - if (get_major_version(current_build_protocol_version) > get_major_version(protocol_version)) { - return IRProtocolErrorCode_Too_Old; + + // TODO: Currently, we hardcode the supported versions. This should be removed once we + // implement a proper version parser. + if (cProtocol::Metadata::VersionValue == protocol_version) { + return IRProtocolErrorCode::Supported; } - return IRProtocolErrorCode_Supported; + + return IRProtocolErrorCode::Unsupported; } IRErrorCode deserialize_utc_offset_change(ReaderInterface& reader, UtcOffset& utc_offset) { diff --git a/components/core/src/clp/ffi/ir_stream/decoding_methods.hpp b/components/core/src/clp/ffi/ir_stream/decoding_methods.hpp index fb6f6a3c0..a9bc5c4fd 100644 --- a/components/core/src/clp/ffi/ir_stream/decoding_methods.hpp +++ b/components/core/src/clp/ffi/ir_stream/decoding_methods.hpp @@ -1,6 +1,7 @@ #ifndef CLP_FFI_IR_STREAM_DECODING_METHODS_HPP #define CLP_FFI_IR_STREAM_DECODING_METHODS_HPP +#include #include #include @@ -20,12 +21,12 @@ typedef enum { IRErrorCode_Incomplete_IR, } IRErrorCode; -typedef enum { - IRProtocolErrorCode_Supported, - IRProtocolErrorCode_Too_Old, - IRProtocolErrorCode_Too_New, - IRProtocolErrorCode_Invalid, -} IRProtocolErrorCode; +enum class IRProtocolErrorCode : uint8_t { + Supported, + BackwardCompatible, + Unsupported, + Invalid, +}; class DecodingException : public TraceableException { public: @@ -193,15 +194,19 @@ IRErrorCode deserialize_utc_offset_change(ReaderInterface& reader, UtcOffset& ut /** * Validates whether the given protocol version can be supported by the current build. * @param protocol_version - * @return IRProtocolErrorCode_Supported if the protocol version is supported. - * @return IRProtocolErrorCode_Too_Old if the protocol version is no longer supported by this - * build's protocol version. - * @return IRProtocolErrorCode_Too_New if the protocol version is newer than this build's protocol - * version. - * @return IRProtocolErrorCode_Invalid if the protocol version does not follow the SemVer + * @return IRProtocolErrorCode::Supported if the protocol version is supported by the key-value + * pair IR stream serializer and deserializer. TODO: Update this once we integrate backwards + * compatibility into the deserializer. + * @return IRProtocolErrorCode::BackwardCompatible if the protocol version is supported by the + * serializer and deserializer for the IR stream format that predates the key-value pair IR stream + * format. TODO: Update this once we integrate backwards compatibility into the key-value pair IR + * stream format. + * @return IRProtocolErrorCode::Unsupported if the protocol version is not supported by this build. + * @return IRProtocolErrorCode::Invalid if the protocol version does not follow the SemVer * specification. */ -IRProtocolErrorCode validate_protocol_version(std::string_view protocol_version); +[[nodiscard]] auto validate_protocol_version(std::string_view protocol_version +) -> IRProtocolErrorCode; namespace eight_byte_encoding { /** diff --git a/components/core/src/clp/ffi/ir_stream/encoding_methods.cpp b/components/core/src/clp/ffi/ir_stream/encoding_methods.cpp index 128d659c1..7c036de0d 100644 --- a/components/core/src/clp/ffi/ir_stream/encoding_methods.cpp +++ b/components/core/src/clp/ffi/ir_stream/encoding_methods.cpp @@ -98,7 +98,8 @@ static void add_base_metadata_fields( string_view time_zone_id, nlohmann::json& metadata ) { - metadata[cProtocol::Metadata::VersionKey] = cProtocol::Metadata::VersionValue; + metadata[cProtocol::Metadata::VersionKey] + = cProtocol::Metadata::LatestBackwardCompatibleVersion; metadata[cProtocol::Metadata::VariablesSchemaIdKey] = cVariablesSchemaVersion; metadata[cProtocol::Metadata::VariableEncodingMethodsIdKey] = cVariableEncodingMethodsVersion; metadata[cProtocol::Metadata::TimestampPatternKey] = timestamp_pattern; diff --git a/components/core/src/clp/ffi/ir_stream/protocol_constants.hpp b/components/core/src/clp/ffi/ir_stream/protocol_constants.hpp index c6fd92397..d89b99cf5 100644 --- a/components/core/src/clp/ffi/ir_stream/protocol_constants.hpp +++ b/components/core/src/clp/ffi/ir_stream/protocol_constants.hpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace clp::ffi::ir_stream::cProtocol { @@ -12,8 +13,10 @@ constexpr int8_t LengthUByte = 0x11; constexpr int8_t LengthUShort = 0x12; constexpr char VersionKey[] = "VERSION"; -constexpr char VersionValue[] = "0.0.2"; -constexpr char BetaVersionValue[] = "0.1.0-beta.1"; +constexpr std::string_view VersionValue{"0.1.0"}; + +// This is used for the IR stream format that predates the key-value pair IR format. +constexpr std::string_view LatestBackwardCompatibleVersion{"0.0.2"}; // The following regex can be used to validate a Semantic Versioning string. The source of the // regex can be found here: https://semver.org/ diff --git a/components/core/src/clp/ir/LogEventDeserializer.cpp b/components/core/src/clp/ir/LogEventDeserializer.cpp index 6106568dd..8a1064a78 100644 --- a/components/core/src/clp/ir/LogEventDeserializer.cpp +++ b/components/core/src/clp/ir/LogEventDeserializer.cpp @@ -42,7 +42,7 @@ auto LogEventDeserializer::create(ReaderInterface& reader return std::errc::protocol_error; } auto metadata_version = version_iter->get_ref(); - if (ffi::ir_stream::IRProtocolErrorCode_Supported + if (ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible != ffi::ir_stream::validate_protocol_version(metadata_version)) { return std::errc::protocol_not_supported; diff --git a/components/core/tests/test-ir_encoding_methods.cpp b/components/core/tests/test-ir_encoding_methods.cpp index b4f0257c1..1ee1e3542 100644 --- a/components/core/tests/test-ir_encoding_methods.cpp +++ b/components/core/tests/test-ir_encoding_methods.cpp @@ -630,8 +630,8 @@ TEMPLATE_TEST_CASE( auto metadata_json = nlohmann::json::parse(json_metadata); std::string const version = metadata_json.at(clp::ffi::ir_stream::cProtocol::Metadata::VersionKey); - REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Supported == validate_protocol_version(version) - ); + REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible + == validate_protocol_version(version)); REQUIRE(clp::ffi::ir_stream::cProtocol::Metadata::EncodingJson == metadata_type); set_timestamp_info(metadata_json, ts_info); REQUIRE(timestamp_pattern_syntax == ts_info.timestamp_pattern_syntax); @@ -844,17 +844,63 @@ TEST_CASE("decode_next_message_four_byte_timestamp_delta", "[ffi][deserialize_lo } TEST_CASE("validate_protocol_version", "[ffi][validate_version_protocol]") { - REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Invalid == validate_protocol_version("v0.0.1") + REQUIRE( + (clp::ffi::ir_stream::IRProtocolErrorCode::Supported + == validate_protocol_version(clp::ffi::ir_stream::cProtocol::Metadata::VersionValue)) + ); + REQUIRE( + (clp::ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible + == validate_protocol_version( + clp::ffi::ir_stream::cProtocol::Metadata::LatestBackwardCompatibleVersion + )) ); - REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Invalid == validate_protocol_version("0.1")); - REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Invalid == validate_protocol_version("0.a.1")); - - REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Too_New - == validate_protocol_version("1000.0.0")); - REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Supported - == validate_protocol_version(clp::ffi::ir_stream::cProtocol::Metadata::VersionValue)); - REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Supported - == validate_protocol_version("v0.0.0")); + + SECTION("Test invalid versions") { + auto const invalid_versions{GENERATE( + std::string_view{"v0.0.1"}, + std::string_view{"0.1"}, + std::string_view{"0.1.a"}, + std::string_view{"0.a.1"} + )}; + REQUIRE( + (clp::ffi::ir_stream::IRProtocolErrorCode::Invalid + == validate_protocol_version(invalid_versions)) + ); + } + + SECTION("Test backward compatible versions") { + auto const backward_compatible_versions{GENERATE( + std::string_view{"v0.0.0"}, + std::string_view{"0.0.1"}, + std::string_view{"0.0.2"} + )}; + REQUIRE( + (clp::ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible + == validate_protocol_version(backward_compatible_versions)) + ); + } + + SECTION("Test versions that're too old") { + auto const old_versions{GENERATE( + std::string_view{"0.0.3"}, + std::string_view{"0.0.3-beta.1"}, + std::string_view{"0.1.0-beta"} + )}; + REQUIRE( + (clp::ffi::ir_stream::IRProtocolErrorCode::Unsupported + == validate_protocol_version(old_versions)) + ); + } + + SECTION("Test versions that're too new") { + auto const new_versions{ + GENERATE(std::string_view{"10000.0.0"}, std::string_view{"0.10000.0"}) + }; + REQUIRE( + (clp::ffi::ir_stream::IRProtocolErrorCode::Unsupported + == validate_protocol_version(new_versions)) + ); + } } TEMPLATE_TEST_CASE( @@ -905,8 +951,8 @@ TEMPLATE_TEST_CASE( string_view json_metadata{json_metadata_ptr, metadata_size}; auto metadata_json = nlohmann::json::parse(json_metadata); string const version = metadata_json.at(clp::ffi::ir_stream::cProtocol::Metadata::VersionKey); - REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode_Supported == validate_protocol_version(version) - ); + REQUIRE(clp::ffi::ir_stream::IRProtocolErrorCode::BackwardCompatible + == validate_protocol_version(version)); REQUIRE(clp::ffi::ir_stream::cProtocol::Metadata::EncodingJson == metadata_type); set_timestamp_info(metadata_json, ts_info); REQUIRE(timestamp_pattern_syntax == ts_info.timestamp_pattern_syntax); @@ -1055,7 +1101,7 @@ TEMPLATE_TEST_CASE( nlohmann::json expected_metadata; expected_metadata.emplace( clp::ffi::ir_stream::cProtocol::Metadata::VersionKey, - clp::ffi::ir_stream::cProtocol::Metadata::BetaVersionValue + clp::ffi::ir_stream::cProtocol::Metadata::VersionValue ); expected_metadata.emplace( clp::ffi::ir_stream::cProtocol::Metadata::VariablesSchemaIdKey,