From 50fe14e8a16649d6d835ca5b7d967dd328c30256 Mon Sep 17 00:00:00 2001 From: Mother 6000 Date: Tue, 7 Jul 2026 13:18:13 +0000 Subject: [PATCH 1/2] Add replication destination prefix support (#134) --- CHANGELOG.md | 4 ++ src/reduct/client.h | 1 + src/reduct/internal/serialisation.cc | 7 +++ tests/reduct/replication_api_test.cc | 76 ++++++++++++++++++++++++++++ tests/reduct/token_api_test.cc | 3 +- 5 files changed, 90 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce81dae..91dc514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Add replication destination prefix support, [PR-NNN](https://github.com/reductstore/reduct-cpp/pull/NNN) + ## 1.20.0 - 2026-06-16 ### Added diff --git a/src/reduct/client.h b/src/reduct/client.h index 105b614..afa306c 100644 --- a/src/reduct/client.h +++ b/src/reduct/client.h @@ -222,6 +222,7 @@ class IClient { std::string dst_bucket; // Destination bucket std::string dst_host; // Destination host URL (e.g. https://reductstore.com) std::optional dst_token; // Destination access token + std::string dst_prefix; // Prefix to add to destination entry names std::vector entries; // Entries to replicate. If empty, all entries are replicated. Wildcards are supported. std::optional when; // Replication condition diff --git a/src/reduct/internal/serialisation.cc b/src/reduct/internal/serialisation.cc index 466aff7..300486d 100644 --- a/src/reduct/internal/serialisation.cc +++ b/src/reduct/internal/serialisation.cc @@ -237,6 +237,9 @@ Result ReplicationSettingsToJsonString(IClient::ReplicationSetti if (settings.dst_token) { json_data["dst_token"] = *settings.dst_token; } + if (!settings.dst_prefix.empty()) { + json_data["dst_prefix"] = settings.dst_prefix; + } json_data["entries"] = settings.entries; json_data["mode"] = ReplicationModeToString(settings.mode); @@ -280,6 +283,10 @@ Result ParseFullReplicationInfo(const nlohmann::js info.settings.dst_token = settings.at("dst_token"); } + if (settings.contains("dst_prefix") && !settings.at("dst_prefix").is_null()) { + info.settings.dst_prefix = settings.at("dst_prefix"); + } + if (settings.contains("when") && !settings.at("when").is_null()) { info.settings.when = settings.at("when").dump(); } diff --git a/tests/reduct/replication_api_test.cc b/tests/reduct/replication_api_test.cc index 06a24c3..9e98e78 100644 --- a/tests/reduct/replication_api_test.cc +++ b/tests/reduct/replication_api_test.cc @@ -6,6 +6,7 @@ #include "fixture.h" #include "reduct/client.h" +#include "reduct/internal/serialisation.h" using reduct::Error; using reduct::IClient; @@ -78,6 +79,81 @@ TEST_CASE("reduct::Client should update a replication", "[replication_api][1_17] } } +TEST_CASE("reduct::Client should serialize replication destination prefix", "[replication_api]") { + auto settings = DefaultSettings(); + + SECTION("omits empty destination prefix") { + auto [json, err] = reduct::internal::ReplicationSettingsToJsonString(settings); + + REQUIRE(err == Error::kOk); + REQUIRE_FALSE(json.contains("dst_prefix")); + } + + SECTION("serializes non-empty destination prefix") { + settings.dst_prefix = "robot-1"; + + auto [json, err] = reduct::internal::ReplicationSettingsToJsonString(settings); + + REQUIRE(err == Error::kOk); + REQUIRE(json.at("dst_prefix") == "robot-1"); + } +} + +TEST_CASE("reduct::Client should parse replication destination prefix", "[replication_api]") { + auto response = nlohmann::json{ + {"info", + {{"name", "test_replication"}, + {"mode", "enabled"}, + {"is_active", true}, + {"is_provisioned", false}, + {"pending_records", 0}}}, + {"settings", + {{"src_bucket", "test_bucket_1"}, + {"dst_bucket", "test_bucket_2"}, + {"dst_host", "http://127.0.0.1:8383"}, + {"entries", {"entry-1"}}, + {"mode", "enabled"}}}, + {"diagnostics", {{"hourly", {{"ok", 0}, {"errored", 0}, {"errors", nlohmann::json::object()}}}}}, + }; + + SECTION("keeps destination prefix empty when absent") { + auto [replication, err] = reduct::internal::ParseFullReplicationInfo(response); + + REQUIRE(err == Error::kOk); + REQUIRE(replication.settings.dst_prefix.empty()); + } + + SECTION("parses returned destination prefix") { + response["settings"]["dst_prefix"] = "robot-1"; + + auto [replication, err] = reduct::internal::ParseFullReplicationInfo(response); + + REQUIRE(err == Error::kOk); + REQUIRE(replication.settings.dst_prefix == "robot-1"); + } +} + +TEST_CASE("reduct::Client should set replication destination prefix", "[replication_api][1_20]") { + Fixture ctx; + auto settings = DefaultSettings(); + settings.dst_prefix = "robot-1"; + + auto err = ctx.client->CreateReplication("test_replication", settings); + REQUIRE(err == Error::kOk); + + auto [replication, err_2] = ctx.client->GetReplication("test_replication"); + REQUIRE(err_2 == Error::kOk); + REQUIRE(replication.settings.dst_prefix == "robot-1"); + + settings.dst_prefix = "line-a"; + err = ctx.client->UpdateReplication("test_replication", settings); + REQUIRE(err == Error::kOk); + + auto [updated_replication, err_3] = ctx.client->GetReplication("test_replication"); + REQUIRE(err_3 == Error::kOk); + REQUIRE(updated_replication.settings.dst_prefix == "line-a"); +} + TEST_CASE("reduct::Client should set replication mode", "[replication_api][1_18]") { Fixture ctx; auto settings = DefaultSettings(); diff --git a/tests/reduct/token_api_test.cc b/tests/reduct/token_api_test.cc index 316bd08..d6f9466 100644 --- a/tests/reduct/token_api_test.cc +++ b/tests/reduct/token_api_test.cc @@ -35,7 +35,8 @@ TEST_CASE("reduct::Client should create token", "[token_api]") { REQUIRE(token.starts_with("test_token-")); SECTION("Conflict") { - REQUIRE(ctx.client->CreateToken(kTestTokenName, IClient::Permissions{}).error == Error{409, "Token 'test_token' already exists"}); + REQUIRE(ctx.client->CreateToken(kTestTokenName, IClient::Permissions{}).error == + Error{409, "Token 'test_token' already exists"}); } } From 77dd4c4a6fa840727acceffec144f8cb92b3c342 Mon Sep 17 00:00:00 2001 From: Mother 6000 Date: Tue, 7 Jul 2026 13:20:59 +0000 Subject: [PATCH 2/2] Update changelog for PR #135 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91dc514..289553e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add replication destination prefix support, [PR-NNN](https://github.com/reductstore/reduct-cpp/pull/NNN) +- Add replication destination prefix support, [PR-135](https://github.com/reductstore/reduct-cpp/pull/135) ## 1.20.0 - 2026-06-16