Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions core/operations/management/collection_create.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
namespace couchbase::core::operations::management
{
std::error_code
collection_create_request::encode_to(encoded_request_type& encoded, http_context& /* context */) const
collection_create_request::encode_to(encoded_request_type& encoded, http_context& context) const
{
encoded.method = "POST";
encoded.path = fmt::format("/pools/default/buckets/{}/scopes/{}/collections", bucket_name, scope_name);
Expand All @@ -42,6 +42,11 @@ collection_create_request::encode_to(encoded_request_type& encoded, http_context
return couchbase::errc::common::invalid_argument;
}
if (history.has_value()) {
auto bucket_caps = context.config.bucket_capabilities;
if (bucket_caps.find(bucket_capability::non_deduped_history) == bucket_caps.end()) {
return errc::common::feature_not_available;
}

encoded.body.append(fmt::format("&history={}", history.value()));
}
return {};
Expand All @@ -57,9 +62,6 @@ collection_create_request::make_response(error_context::http&& ctx, const encode
std::regex collection_exists("Collection with name .+ already exists");
if (std::regex_search(encoded.body.data(), collection_exists)) {
response.ctx.ec = errc::management::collection_exists;
} else if (encoded.body.data().find("Not allowed on this version of cluster") != std::string::npos ||
encoded.body.data().find("Bucket must have storage_mode=magma") != std::string::npos) {
response.ctx.ec = errc::common::feature_not_available;
} else {
response.ctx.ec = errc::common::invalid_argument;
}
Expand Down
14 changes: 7 additions & 7 deletions core/operations/management/collection_update.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
namespace couchbase::core::operations::management
{
std::error_code
collection_update_request::encode_to(encoded_request_type& encoded, http_context& /* context */) const
collection_update_request::encode_to(encoded_request_type& encoded, http_context& context) const
{
encoded.method = "PATCH";
encoded.path = fmt::format("/pools/default/buckets/{}/scopes/{}/collections/{}", bucket_name, scope_name, collection_name);
Expand All @@ -42,6 +42,11 @@ collection_update_request::encode_to(encoded_request_type& encoded, http_context
}
}
if (history.has_value()) {
auto bucket_caps = context.config.bucket_capabilities;
if (bucket_caps.find(bucket_capability::non_deduped_history) == bucket_caps.end()) {
return errc::common::feature_not_available;
}

values["history"] = history.value() ? "true" : "false";
}
encoded.body = utils::string_codec::v2::form_encode(values);
Expand All @@ -55,12 +60,7 @@ collection_update_request::make_response(error_context::http&& ctx, const encode
if (!response.ctx.ec) {
switch (encoded.status_code) {
case 400: {
if (encoded.body.data().find("Not allowed on this version of cluster") != std::string::npos ||
encoded.body.data().find("Bucket must have storage_mode=magma") != std::string::npos) {
response.ctx.ec = errc::common::feature_not_available;
} else {
response.ctx.ec = errc::common::invalid_argument;
}
response.ctx.ec = errc::common::invalid_argument;
} break;
case 404: {
std::regex scope_not_found("Scope with name .+ is not found");
Expand Down
97 changes: 86 additions & 11 deletions test/test_integration_management.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ TEST_CASE("integration: bucket management history", "[integration]")
REQUIRE(get_resp.bucket.history_retention_bytes == 2147483648);
}
}

{
couchbase::core::operations::management::bucket_drop_request req{ bucket_name };
couchbase::core::operations::management::bucket_drop_request update_req{ update_bucket_name };
Expand Down Expand Up @@ -1539,46 +1540,113 @@ TEST_CASE("integration: collection management update collection with max expiry"
}
}

TEST_CASE("integration: collection management bucket dedup", "[integration]")
TEST_CASE("integration: collection management history retention not supported in bucket", "[integration]")
{
test::utils::integration_test_guard integration;
test::utils::open_bucket(integration.cluster, integration.ctx.bucket);

if (!integration.cluster_version().supports_collections()) {
SKIP("cluster does not support collections");
}
if (!integration.has_bucket_capability("nonDedupedHistory")) {
SKIP("Bucket does not support non deduped history");
if (integration.has_bucket_capability("nonDedupedHistory")) {
SKIP("bucket supports non deduped history");
}

auto scope_name = "_default";
auto collection_name = test::utils::uniq_id("collection");

SECTION("create collection")
{
couchbase::core::operations::management::collection_create_request req{
integration.ctx.bucket,
scope_name,
collection_name,
};

req.history = true;

auto resp = test::utils::execute(integration.cluster, req);
REQUIRE(resp.ctx.ec == couchbase::errc::common::feature_not_available);
}

SECTION("update collection")
{
auto ec = create_collection(integration.cluster, integration.ctx.bucket, scope_name, collection_name);
REQUIRE_SUCCESS(ec);

couchbase::core::operations::management::collection_update_request req{
integration.ctx.bucket,
scope_name,
collection_name,
};

req.history = true;

auto resp = test::utils::execute(integration.cluster, req);
REQUIRE(resp.ctx.ec == couchbase::errc::common::feature_not_available);
}

// Clean up the collection that was created
{
auto ec = drop_collection(integration.cluster, integration.ctx.bucket, scope_name, collection_name);
REQUIRE((!ec || ec == couchbase::errc::common::collection_not_found));
}
}

TEST_CASE("integration: collection management bucket dedup", "[integration]")
{
test::utils::integration_test_guard integration;

if (!integration.cluster_version().supports_collections()) {
SKIP("cluster does not support collections");
}
if (!integration.cluster_version().supports_bucket_history()) {
SKIP("cluster does not support history retention");
}

auto bucket_name = test::utils::uniq_id("bucket");
auto scope_name = test::utils::uniq_id("scope");
auto collection_name = test::utils::uniq_id("collection");

// Create a magma bucket for use in this test
{
couchbase::core::operations::management::scope_create_request req{ integration.ctx.bucket, scope_name };
couchbase::core::management::cluster::bucket_settings bucket_settings;
bucket_settings.name = bucket_name;
bucket_settings.ram_quota_mb = 1'024;
bucket_settings.storage_backend = couchbase::core::management::cluster::bucket_storage_backend::magma;
couchbase::core::operations::management::bucket_create_request req{ bucket_settings };
auto resp = test::utils::execute(integration.cluster, req);
REQUIRE_SUCCESS(resp.ctx.ec);
auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, integration.ctx.bucket, resp.uid);
}
{
auto resp = wait_for_bucket_created(integration, bucket_name);
REQUIRE_SUCCESS(resp.ctx.ec);
}
{
couchbase::core::operations::management::scope_create_request req{ bucket_name, scope_name };
auto resp = test::utils::execute(integration.cluster, req);
REQUIRE_SUCCESS(resp.ctx.ec);
auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, bucket_name, resp.uid);
REQUIRE(created);
}

{
auto created = test::utils::wait_until([&]() { return scope_exists(integration.cluster, integration.ctx.bucket, scope_name); });
auto created = test::utils::wait_until([&]() { return scope_exists(integration.cluster, bucket_name, scope_name); });
REQUIRE(created);
}

{
couchbase::core::operations::management::collection_create_request req{ integration.ctx.bucket, scope_name, collection_name };
couchbase::core::operations::management::collection_create_request req{ bucket_name, scope_name, collection_name };
req.history = true;
auto resp = test::utils::execute(integration.cluster, req);
REQUIRE_SUCCESS(resp.ctx.ec);
auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, integration.ctx.bucket, resp.uid);
auto created = test::utils::wait_until_collection_manifest_propagated(integration.cluster, bucket_name, resp.uid);
REQUIRE(created);
}
{
couchbase::core::topology::collections_manifest::collection collection;
auto created = test::utils::wait_until([&]() {
auto coll = get_collection(integration.cluster, integration.ctx.bucket, scope_name, collection_name);
auto coll = get_collection(integration.cluster, bucket_name, scope_name, collection_name);
if (coll) {
collection = *coll;
return true;
Expand All @@ -1589,15 +1657,15 @@ TEST_CASE("integration: collection management bucket dedup", "[integration]")
REQUIRE(collection.history.value());
}
{
couchbase::core::operations::management::collection_update_request req{ integration.ctx.bucket, scope_name, collection_name };
couchbase::core::operations::management::collection_update_request req{ bucket_name, scope_name, collection_name };
req.history = false;
auto resp = test::utils::execute(integration.cluster, req);
REQUIRE_SUCCESS(resp.ctx.ec);
}
{
couchbase::core::topology::collections_manifest::collection collection;
auto no_history = test::utils::wait_until([&]() {
auto coll = get_collection(integration.cluster, integration.ctx.bucket, scope_name, collection_name);
auto coll = get_collection(integration.cluster, bucket_name, scope_name, collection_name);
if (coll.has_value()) {
if (!coll.value().history.value()) {
return true;
Expand All @@ -1608,6 +1676,13 @@ TEST_CASE("integration: collection management bucket dedup", "[integration]")
});
REQUIRE(no_history);
}

// Clean up the bucket that was created for this test
{
couchbase::core::operations::management::bucket_drop_request req{ bucket_name };
auto resp = test::utils::execute(integration.cluster, req);
REQUIRE_SUCCESS(resp.ctx.ec);
}
}

void
Expand Down