couchbase 3.4.4 → 3.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/couchbase/CMakeLists.txt +7 -0
  4. data/ext/couchbase/core/cluster.hxx +7 -0
  5. data/ext/couchbase/core/impl/create_bucket.cxx +3 -0
  6. data/ext/couchbase/core/impl/create_collection.cxx +83 -0
  7. data/ext/couchbase/core/impl/create_scope.cxx +69 -0
  8. data/ext/couchbase/core/impl/drop_collection.cxx +76 -0
  9. data/ext/couchbase/core/impl/drop_scope.cxx +68 -0
  10. data/ext/couchbase/core/impl/get_all_buckets.cxx +19 -4
  11. data/ext/couchbase/core/impl/get_all_scopes.cxx +94 -0
  12. data/ext/couchbase/core/impl/get_bucket.cxx +19 -4
  13. data/ext/couchbase/core/impl/lookup_in_all_replicas.cxx +2 -0
  14. data/ext/couchbase/core/impl/lookup_in_any_replica.cxx +2 -0
  15. data/ext/couchbase/core/impl/lookup_in_replica.cxx +8 -1
  16. data/ext/couchbase/core/impl/update_bucket.cxx +3 -0
  17. data/ext/couchbase/core/impl/update_collection.cxx +83 -0
  18. data/ext/couchbase/core/management/bucket_settings.hxx +8 -5
  19. data/ext/couchbase/core/management/bucket_settings_json.hxx +12 -2
  20. data/ext/couchbase/core/meta/features.hxx +17 -0
  21. data/ext/couchbase/core/operations/document_lookup_in.cxx +8 -1
  22. data/ext/couchbase/core/operations/management/CMakeLists.txt +1 -0
  23. data/ext/couchbase/core/operations/management/bucket_create.cxx +30 -9
  24. data/ext/couchbase/core/operations/management/bucket_update.cxx +27 -6
  25. data/ext/couchbase/core/operations/management/collection_create.cxx +5 -1
  26. data/ext/couchbase/core/operations/management/collection_create.hxx +1 -0
  27. data/ext/couchbase/core/operations/management/collection_update.cxx +87 -0
  28. data/ext/couchbase/core/operations/management/collection_update.hxx +54 -0
  29. data/ext/couchbase/core/operations/management/collections.hxx +1 -0
  30. data/ext/couchbase/core/timeout_defaults.hxx +1 -1
  31. data/ext/couchbase/core/topology/capabilities.hxx +1 -0
  32. data/ext/couchbase/core/topology/capabilities_fmt.hxx +3 -0
  33. data/ext/couchbase/core/topology/collections_manifest.hxx +2 -0
  34. data/ext/couchbase/core/topology/collections_manifest_json.hxx +3 -0
  35. data/ext/couchbase/core/topology/configuration.hxx +5 -0
  36. data/ext/couchbase/core/topology/configuration_json.hxx +2 -0
  37. data/ext/couchbase/couchbase/bucket.hxx +14 -0
  38. data/ext/couchbase/couchbase/collection_manager.hxx +160 -0
  39. data/ext/couchbase/couchbase/create_collection_options.hxx +44 -0
  40. data/ext/couchbase/couchbase/create_scope_options.hxx +41 -0
  41. data/ext/couchbase/couchbase/drop_collection_options.hxx +41 -0
  42. data/ext/couchbase/couchbase/drop_scope_options.hxx +41 -0
  43. data/ext/couchbase/couchbase/get_all_scopes_options.hxx +44 -0
  44. data/ext/couchbase/couchbase/management/bucket_settings.hxx +8 -5
  45. data/ext/couchbase/couchbase/management/collection_spec.hxx +29 -0
  46. data/ext/couchbase/couchbase/management/scope_spec.hxx +29 -0
  47. data/ext/couchbase/couchbase/update_collection_options.hxx +44 -0
  48. data/ext/couchbase/test/test_integration_management.cxx +305 -48
  49. data/ext/couchbase/test/test_integration_subdoc.cxx +81 -22
  50. data/ext/couchbase.cxx +155 -34
  51. data/ext/revisions.rb +3 -3
  52. data/lib/couchbase/collection_options.rb +1 -2
  53. data/lib/couchbase/management/bucket_manager.rb +22 -15
  54. data/lib/couchbase/management/collection_manager.rb +158 -9
  55. data/lib/couchbase/version.rb +1 -1
  56. metadata +23 -6
@@ -0,0 +1,83 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-Present Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include <couchbase/manager_error_context.hxx>
19
+
20
+ #include "core/cluster.hxx"
21
+ #include "core/operations/management/collection_update.hxx"
22
+ #include "couchbase/collection_manager.hxx"
23
+
24
+ namespace couchbase
25
+ {
26
+ template<typename Response>
27
+ static manager_error_context
28
+ build_context(Response& resp)
29
+ {
30
+ return manager_error_context(internal_manager_error_context{ resp.ctx.ec,
31
+ resp.ctx.last_dispatched_to,
32
+ resp.ctx.last_dispatched_from,
33
+ resp.ctx.retry_attempts,
34
+ std::move(resp.ctx.retry_reasons),
35
+ std::move(resp.ctx.client_context_id),
36
+ resp.ctx.http_status,
37
+ std::move(resp.ctx.http_body),
38
+ std::move(resp.ctx.path) });
39
+ }
40
+
41
+ static core::operations::management::collection_update_request
42
+ build_collection_update_request(std::string bucket_name,
43
+ std::string scope_name,
44
+ std::string collection_name,
45
+ const update_collection_settings& settings,
46
+ const update_collection_options::built& options)
47
+ {
48
+ core::operations::management::collection_update_request request{
49
+ std::move(bucket_name), std::move(scope_name), std::move(collection_name), settings.max_expiry, settings.history, {},
50
+ options.timeout
51
+ };
52
+ return request;
53
+ }
54
+
55
+ void
56
+ collection_manager::update_collection(std::string scope_name,
57
+ std::string collection_name,
58
+ const couchbase::update_collection_settings& settings,
59
+ const couchbase::update_collection_options& options,
60
+ couchbase::update_collection_handler&& handler) const
61
+ {
62
+ auto request =
63
+ build_collection_update_request(bucket_name_, std::move(scope_name), std::move(collection_name), settings, options.build());
64
+
65
+ core_->execute(std::move(request),
66
+ [handler = std::move(handler)](core::operations::management::collection_update_response resp) mutable {
67
+ return handler(build_context(resp));
68
+ });
69
+ }
70
+
71
+ auto
72
+ collection_manager::update_collection(std::string scope_name,
73
+ std::string collection_name,
74
+ const couchbase::update_collection_settings& settings,
75
+ const couchbase::update_collection_options& options) const -> std::future<manager_error_context>
76
+ {
77
+ auto barrier = std::make_shared<std::promise<manager_error_context>>();
78
+ update_collection(std::move(scope_name), std::move(collection_name), settings, options, [barrier](auto ctx) mutable {
79
+ barrier->set_value(std::move(ctx));
80
+ });
81
+ return barrier->get_future();
82
+ }
83
+ } // namespace couchbase
@@ -105,16 +105,19 @@ struct bucket_settings {
105
105
 
106
106
  std::string name;
107
107
  std::string uuid;
108
+ std::uint64_t ram_quota_mb{ 0 }; // If not explicitly set, defaults to 100 on create_bucket, unset on update_bucket
108
109
  cluster::bucket_type bucket_type{ cluster::bucket_type::unknown };
109
- std::uint64_t ram_quota_mb{ 100 };
110
- std::uint32_t max_expiry{ 0 };
110
+ std::optional<std::uint32_t> max_expiry{};
111
111
  bucket_compression compression_mode{ bucket_compression::unknown };
112
112
  std::optional<couchbase::durability_level> minimum_durability_level{};
113
- std::uint32_t num_replicas{ 1 };
114
- bool replica_indexes{ false };
115
- bool flush_enabled{ false };
113
+ std::optional<std::uint32_t> num_replicas{};
114
+ std::optional<bool> replica_indexes{};
115
+ std::optional<bool> flush_enabled{};
116
116
  bucket_eviction_policy eviction_policy{ bucket_eviction_policy::unknown };
117
117
  bucket_conflict_resolution conflict_resolution_type{ bucket_conflict_resolution::unknown };
118
+ std::optional<bool> history_retention_collection_default{};
119
+ std::optional<std::uint32_t> history_retention_bytes;
120
+ std::optional<std::uint32_t> history_retention_duration{};
118
121
 
119
122
  /**
120
123
  * UNCOMMITTED: This API may change in the future
@@ -33,10 +33,20 @@ struct traits<couchbase::core::management::cluster::bucket_settings> {
33
33
  result.uuid = v.at("uuid").get_string();
34
34
  const static std::uint64_t megabyte = 1024LLU * 1024LLU;
35
35
  result.ram_quota_mb = v.at("quota").at("rawRAM").get_unsigned() / megabyte;
36
- result.num_replicas = v.at("replicaNumber").template as<std::uint32_t>();
36
+ result.num_replicas = v.at("replicaNumber").template as<std::optional<std::uint32_t>>();
37
37
 
38
38
  if (auto* max_ttl = v.find("maxTTL"); max_ttl != nullptr) {
39
- result.max_expiry = max_ttl->template as<std::uint32_t>();
39
+ result.max_expiry = max_ttl->template as<std::optional<std::uint32_t>>();
40
+ }
41
+
42
+ if (auto* history_retention_default = v.find("historyRetentionCollectionDefault"); history_retention_default != nullptr) {
43
+ result.history_retention_collection_default = history_retention_default->template as<std::optional<bool>>();
44
+ }
45
+ if (auto* history_retention_bytes = v.find("historyRetentionBytes"); history_retention_bytes != nullptr) {
46
+ result.history_retention_bytes = history_retention_bytes->template as<std::optional<std::uint32_t>>();
47
+ }
48
+ if (auto* history_retention_duration = v.find("historyRetentionSeconds"); history_retention_duration != nullptr) {
49
+ result.history_retention_duration = history_retention_duration->template as<std::optional<std::uint32_t>>();
40
50
  }
41
51
 
42
52
  if (auto& str = v.at("bucketType").get_string(); str == "couchbase" || str == "membase") {
@@ -54,3 +54,20 @@
54
54
  * couchbase::core::lookup_in_replica support
55
55
  */
56
56
  #define COUCHBASE_CXX_CLIENT_CORE_HAS_SUBDOC_READ_REPLICA 1
57
+
58
+ /**
59
+ * Collection management is accessible from the public API
60
+ * couchbase::bucket::collections() support
61
+ */
62
+ #define COUCHBASE_CXX_CLIENT_HAS_PUBLIC_COLLECTION_MANAGEMENT 1
63
+
64
+ /**
65
+ * Support for bucket no-deduplication feature in bucket and collection management
66
+ */
67
+ #define COUCHBASE_CXX_CLIENT_HAS_BUCKET_NO_DEDUP 1
68
+
69
+ /**
70
+ * Collection query index management is available in the public API
71
+ * couchbase::collection::query_indexes() support
72
+ */
73
+ #define COUCHBASE_CXX_CLIENT_HAS_COLLECTION_QUERY_INDEX_MANAGEMENT 1
@@ -70,6 +70,9 @@ lookup_in_request::make_response(key_value_error_context&& ctx, const encoded_re
70
70
  fields[i].status = res_entry.status;
71
71
  fields[i].ec =
72
72
  protocol::map_status_code(protocol::client_opcode::subdoc_multi_mutation, static_cast<std::uint16_t>(res_entry.status));
73
+ if (fields[i].opcode == protocol::subdoc_opcode::exists && fields[i].ec == errc::key_value::path_not_found) {
74
+ fields[i].ec.clear();
75
+ }
73
76
  if (!fields[i].ec && !ctx.ec()) {
74
77
  ec = fields[i].ec;
75
78
  }
@@ -79,7 +82,11 @@ lookup_in_request::make_response(key_value_error_context&& ctx, const encoded_re
79
82
  }
80
83
  fields[i].exists =
81
84
  res_entry.status == key_value_status_code::success || res_entry.status == key_value_status_code::subdoc_success_deleted;
82
- fields[i].value = utils::to_binary(res_entry.value);
85
+ if (fields[i].opcode == protocol::subdoc_opcode::exists && !fields[i].ec) {
86
+ fields[i].value = utils::json::generate_binary(fields[i].exists);
87
+ } else {
88
+ fields[i].value = utils::to_binary(res_entry.value);
89
+ }
83
90
  }
84
91
  if (!ec) {
85
92
  cas = encoded.cas();
@@ -28,6 +28,7 @@ add_library(
28
28
  collection_create.cxx
29
29
  collection_drop.cxx
30
30
  collections_manifest_get.cxx
31
+ collection_update.cxx
31
32
  error_utils.cxx
32
33
  eventing_deploy_function.cxx
33
34
  eventing_drop_function.cxx
@@ -48,17 +48,35 @@ bucket_create_request::encode_to(encoded_request_type& encoded, http_context& /*
48
48
  case couchbase::core::management::cluster::bucket_type::unknown:
49
49
  break;
50
50
  }
51
- encoded.body.append(fmt::format("&ramQuotaMB={}", bucket.ram_quota_mb));
52
- if (bucket.bucket_type != couchbase::core::management::cluster::bucket_type::memcached) {
53
- encoded.body.append(fmt::format("&replicaNumber={}", bucket.num_replicas));
51
+ if (bucket.ram_quota_mb == 0) {
52
+ encoded.body.append(fmt::format("&ramQuotaMB={}", 100)); // If not explicitly set, set to prior default value of 100
53
+ } else {
54
+ encoded.body.append(fmt::format("&ramQuotaMB={}", bucket.ram_quota_mb));
54
55
  }
55
- if (bucket.max_expiry > 0) {
56
- encoded.body.append(fmt::format("&maxTTL={}", bucket.max_expiry));
56
+
57
+ if (bucket.bucket_type != couchbase::core::management::cluster::bucket_type::memcached && bucket.num_replicas.has_value()) {
58
+ encoded.body.append(fmt::format("&replicaNumber={}", bucket.num_replicas.value()));
59
+ }
60
+ if (bucket.max_expiry.has_value()) {
61
+ encoded.body.append(fmt::format("&maxTTL={}", bucket.max_expiry.value()));
62
+ }
63
+ if (bucket.bucket_type != couchbase::core::management::cluster::bucket_type::ephemeral && bucket.replica_indexes.has_value()) {
64
+ encoded.body.append(fmt::format("&replicaIndex={}", bucket.replica_indexes.value() ? "1" : "0"));
65
+ }
66
+ if (bucket.history_retention_collection_default.has_value()) {
67
+ encoded.body.append(
68
+ fmt::format("&historyRetentionCollectionDefault={}", bucket.history_retention_collection_default.value() ? "true" : "false"));
57
69
  }
58
- if (bucket.bucket_type != couchbase::core::management::cluster::bucket_type::ephemeral) {
59
- encoded.body.append(fmt::format("&replicaIndex={}", bucket.replica_indexes ? "1" : "0"));
70
+ if (bucket.history_retention_bytes.has_value()) {
71
+ encoded.body.append(fmt::format("&historyRetentionBytes={}", bucket.history_retention_bytes.value()));
60
72
  }
61
- encoded.body.append(fmt::format("&flushEnabled={}", bucket.flush_enabled ? "1" : "0"));
73
+ if (bucket.history_retention_duration.has_value()) {
74
+ encoded.body.append(fmt::format("&historyRetentionSeconds={}", bucket.history_retention_duration.value()));
75
+ }
76
+ if (bucket.flush_enabled.has_value()) {
77
+ encoded.body.append(fmt::format("&flushEnabled={}", bucket.flush_enabled.value() ? "1" : "0"));
78
+ }
79
+
62
80
  switch (bucket.eviction_policy) {
63
81
  case couchbase::core::management::cluster::bucket_eviction_policy::full:
64
82
  encoded.body.append("&evictionPolicy=fullEviction");
@@ -101,7 +119,7 @@ bucket_create_request::encode_to(encoded_request_type& encoded, http_context& /*
101
119
  case couchbase::core::management::cluster::bucket_conflict_resolution::unknown:
102
120
  break;
103
121
  }
104
- if (bucket.minimum_durability_level) {
122
+ if (bucket.minimum_durability_level.has_value()) {
105
123
  switch (bucket.minimum_durability_level.value()) {
106
124
  case durability_level::none:
107
125
  encoded.body.append("&durabilityMinLevel=none");
@@ -151,6 +169,9 @@ bucket_create_request::make_response(error_context::http&& ctx, const encoded_re
151
169
  if (errors != nullptr) {
152
170
  std::vector<std::string> error_list{};
153
171
  for (const auto& [code, message] : errors->get_object()) {
172
+ if (message.get_string().find("Bucket with given name already exists") != std::string::npos) {
173
+ response.ctx.ec = errc::management::bucket_exists;
174
+ }
154
175
  error_list.emplace_back(message.get_string());
155
176
  }
156
177
  if (!error_list.empty()) {
@@ -33,13 +33,34 @@ bucket_update_request::encode_to(encoded_request_type& encoded, http_context& /*
33
33
  encoded.path = fmt::format("/pools/default/buckets/{}", bucket.name);
34
34
 
35
35
  encoded.headers["content-type"] = "application/x-www-form-urlencoded";
36
- encoded.body.append(fmt::format("&ramQuotaMB={}", bucket.ram_quota_mb));
37
- encoded.body.append(fmt::format("&replicaNumber={}", bucket.num_replicas));
38
- if (bucket.max_expiry > 0) {
39
- encoded.body.append(fmt::format("&maxTTL={}", bucket.max_expiry));
36
+
37
+ if (bucket.ram_quota_mb > 0) {
38
+ encoded.body.append(fmt::format("&ramQuotaMB={}", bucket.ram_quota_mb));
39
+ }
40
+ if (bucket.num_replicas.has_value()) {
41
+ encoded.body.append(fmt::format("&replicaNumber={}", bucket.num_replicas.value()));
42
+ }
43
+
44
+ if (bucket.max_expiry.has_value()) {
45
+ encoded.body.append(fmt::format("&maxTTL={}", bucket.max_expiry.value()));
46
+ }
47
+ if (bucket.history_retention_collection_default.has_value()) {
48
+ encoded.body.append(
49
+ fmt::format("&historyRetentionCollectionDefault={}", bucket.history_retention_collection_default.value() ? "true" : "false"));
40
50
  }
41
- encoded.body.append(fmt::format("&replicaIndex={}", bucket.replica_indexes ? "1" : "0"));
42
- encoded.body.append(fmt::format("&flushEnabled={}", bucket.flush_enabled ? "1" : "0"));
51
+ if (bucket.history_retention_bytes.has_value()) {
52
+ encoded.body.append(fmt::format("&historyRetentionBytes={}", bucket.history_retention_bytes.value()));
53
+ }
54
+ if (bucket.history_retention_duration.has_value()) {
55
+ encoded.body.append(fmt::format("&historyRetentionSeconds={}", bucket.history_retention_duration.value()));
56
+ }
57
+ if (bucket.replica_indexes.has_value()) {
58
+ encoded.body.append(fmt::format("&replicaIndex={}", bucket.replica_indexes.value() ? "1" : "0"));
59
+ }
60
+ if (bucket.flush_enabled.has_value()) {
61
+ encoded.body.append(fmt::format("&flushEnabled={}", bucket.flush_enabled.value() ? "1" : "0"));
62
+ }
63
+
43
64
  switch (bucket.eviction_policy) {
44
65
  case couchbase::core::management::cluster::bucket_eviction_policy::full:
45
66
  encoded.body.append("&evictionPolicy=fullEviction");
@@ -35,6 +35,9 @@ collection_create_request::encode_to(encoded_request_type& encoded, http_context
35
35
  if (max_expiry > 0) {
36
36
  encoded.body.append(fmt::format("&maxTTL={}", max_expiry));
37
37
  }
38
+ if (history.has_value()) {
39
+ encoded.body.append(fmt::format("&history={}", history.value()));
40
+ }
38
41
  return {};
39
42
  }
40
43
 
@@ -48,7 +51,8 @@ collection_create_request::make_response(error_context::http&& ctx, const encode
48
51
  std::regex collection_exists("Collection with name .+ already exists");
49
52
  if (std::regex_search(encoded.body.data(), collection_exists)) {
50
53
  response.ctx.ec = errc::management::collection_exists;
51
- } else if (encoded.body.data().find("Not allowed on this version of cluster") != std::string::npos) {
54
+ } else if (encoded.body.data().find("Not allowed on this version of cluster") != std::string::npos ||
55
+ encoded.body.data().find("Bucket must have storage_mode=magma") != std::string::npos) {
52
56
  response.ctx.ec = errc::common::feature_not_available;
53
57
  } else {
54
58
  response.ctx.ec = errc::common::invalid_argument;
@@ -42,6 +42,7 @@ struct collection_create_request {
42
42
  std::string scope_name;
43
43
  std::string collection_name;
44
44
  std::uint32_t max_expiry{ 0 };
45
+ std::optional<bool> history{};
45
46
 
46
47
  std::optional<std::string> client_context_id{};
47
48
  std::optional<std::chrono::milliseconds> timeout{};
@@ -0,0 +1,87 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-2021 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "collection_update.hxx"
19
+
20
+ #include "core/utils/json.hxx"
21
+ #include "core/utils/url_codec.hxx"
22
+ #include "error_utils.hxx"
23
+
24
+ #include <regex>
25
+
26
+ namespace couchbase::core::operations::management
27
+ {
28
+ std::error_code
29
+ collection_update_request::encode_to(encoded_request_type& encoded, http_context& /* context */) const
30
+ {
31
+ encoded.method = "PATCH";
32
+ encoded.path = fmt::format("/pools/default/buckets/{}/scopes/{}/collections/{}", bucket_name, scope_name, collection_name);
33
+ encoded.headers["content-type"] = "application/x-www-form-urlencoded";
34
+ std::map<std::string, std::string> values{};
35
+ if (max_expiry.has_value()) {
36
+ values["maxTTL"] = std::to_string(max_expiry.value());
37
+ }
38
+ if (history.has_value()) {
39
+ values["history"] = history.value() ? "true" : "false";
40
+ }
41
+ encoded.body = utils::string_codec::v2::form_encode(values);
42
+ return {};
43
+ }
44
+
45
+ collection_update_response
46
+ collection_update_request::make_response(error_context::http&& ctx, const encoded_response_type& encoded) const
47
+ {
48
+ collection_update_response response{ std::move(ctx) };
49
+ if (!response.ctx.ec) {
50
+ switch (encoded.status_code) {
51
+ case 400: {
52
+ if (encoded.body.data().find("Not allowed on this version of cluster") != std::string::npos ||
53
+ encoded.body.data().find("Bucket must have storage_mode=magma") != std::string::npos) {
54
+ response.ctx.ec = errc::common::feature_not_available;
55
+ } else {
56
+ response.ctx.ec = errc::common::invalid_argument;
57
+ }
58
+ } break;
59
+ case 404: {
60
+ std::regex scope_not_found("Scope with name .+ is not found");
61
+ std::regex collection_not_found("Collection with name .+ is not found");
62
+ if (std::regex_search(encoded.body.data(), collection_not_found)) {
63
+ response.ctx.ec = errc::common::collection_not_found;
64
+ } else if (std::regex_search(encoded.body.data(), scope_not_found)) {
65
+ response.ctx.ec = errc::common::scope_not_found;
66
+ } else {
67
+ response.ctx.ec = errc::common::bucket_not_found;
68
+ }
69
+ } break;
70
+ case 200: {
71
+ tao::json::value payload{};
72
+ try {
73
+ payload = utils::json::parse(encoded.body.data());
74
+ } catch (const tao::pegtl::parse_error&) {
75
+ response.ctx.ec = errc::common::parsing_failure;
76
+ return response;
77
+ }
78
+ response.uid = std::stoull(payload.at("uid").get_string(), nullptr, 16);
79
+ } break;
80
+ default:
81
+ response.ctx.ec = extract_common_error_code(encoded.status_code, encoded.body.data());
82
+ break;
83
+ }
84
+ }
85
+ return response;
86
+ }
87
+ } // namespace couchbase::core::operations::management
@@ -0,0 +1,54 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-2021 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #pragma once
19
+
20
+ #include "core/error_context/http.hxx"
21
+ #include "core/io/http_context.hxx"
22
+ #include "core/io/http_message.hxx"
23
+ #include "core/platform/uuid.h"
24
+ #include "core/timeout_defaults.hxx"
25
+
26
+ namespace couchbase::core::operations::management
27
+ {
28
+ struct collection_update_response {
29
+ error_context::http ctx;
30
+ std::uint64_t uid{ 0 };
31
+ };
32
+
33
+ struct collection_update_request {
34
+ using response_type = collection_update_response;
35
+ using encoded_request_type = io::http_request;
36
+ using encoded_response_type = io::http_response;
37
+ using error_context_type = error_context::http;
38
+
39
+ static const inline service_type type = service_type::management;
40
+
41
+ std::string bucket_name;
42
+ std::string scope_name;
43
+ std::string collection_name;
44
+ std::optional<std::uint32_t> max_expiry{};
45
+ std::optional<bool> history{};
46
+
47
+ std::optional<std::string> client_context_id{};
48
+ std::optional<std::chrono::milliseconds> timeout{};
49
+
50
+ [[nodiscard]] std::error_code encode_to(encoded_request_type& encoded, http_context& context) const;
51
+
52
+ [[nodiscard]] collection_update_response make_response(error_context::http&& ctx, const encoded_response_type& encoded) const;
53
+ };
54
+ } // namespace couchbase::core::operations::management
@@ -19,6 +19,7 @@
19
19
 
20
20
  #include "collection_create.hxx"
21
21
  #include "collection_drop.hxx"
22
+ #include "collection_update.hxx"
22
23
  #include "collections_manifest_get.hxx"
23
24
  #include "scope_create.hxx"
24
25
  #include "scope_drop.hxx"
@@ -40,5 +40,5 @@ constexpr std::chrono::milliseconds tcp_keep_alive_interval{ 60'000 };
40
40
  constexpr std::chrono::milliseconds config_poll_interval{ 2'500 };
41
41
  constexpr std::chrono::milliseconds config_poll_floor{ 50 };
42
42
  constexpr std::chrono::milliseconds config_idle_redial_timeout{ 5 * 60'000 };
43
- constexpr std::chrono::milliseconds idle_http_connection_timeout{ 4'500 };
43
+ constexpr std::chrono::milliseconds idle_http_connection_timeout{ 1'000 };
44
44
  } // namespace couchbase::core::timeout_defaults
@@ -33,6 +33,7 @@ enum class bucket_capability {
33
33
  tombstoned_user_xattrs,
34
34
  range_scan,
35
35
  replica_read,
36
+ non_deduped_history,
36
37
  };
37
38
 
38
39
  enum class cluster_capability {
@@ -73,6 +73,9 @@ struct fmt::formatter<couchbase::core::bucket_capability> {
73
73
  case couchbase::core::bucket_capability::replica_read:
74
74
  name = "replica_read";
75
75
  break;
76
+ case couchbase::core::bucket_capability::non_deduped_history:
77
+ name = "non_deduped_history";
78
+ break;
76
79
  }
77
80
  return format_to(ctx.out(), "{}", name);
78
81
  }
@@ -17,6 +17,7 @@
17
17
 
18
18
  #pragma once
19
19
 
20
+ #include <optional>
20
21
  #include <vector>
21
22
 
22
23
  #include "core/platform/uuid.h"
@@ -28,6 +29,7 @@ struct collections_manifest {
28
29
  std::uint64_t uid;
29
30
  std::string name;
30
31
  std::uint32_t max_expiry{ 0 };
32
+ std::optional<bool> history{};
31
33
  };
32
34
 
33
35
  struct scope {
@@ -43,6 +43,9 @@ struct traits<couchbase::core::topology::collections_manifest> {
43
43
  if (const auto* max_ttl = c.find("maxTTL"); max_ttl != nullptr) {
44
44
  collection.max_expiry = max_ttl->template as<std::uint32_t>();
45
45
  }
46
+ if (const auto* history = c.find("history"); history != nullptr) {
47
+ collection.history = history->template as<std::optional<bool>>();
48
+ }
46
49
  scope.collections.emplace_back(collection);
47
50
  }
48
51
  result.scopes.emplace_back(scope);
@@ -134,6 +134,11 @@ struct configuration {
134
134
  return bucket_capabilities.find(bucket_capability::replica_read) != bucket_capabilities.end();
135
135
  }
136
136
 
137
+ [[nodiscard]] bool supports_non_deduped_history() const
138
+ {
139
+ return bucket_capabilities.find(bucket_capability::non_deduped_history) != bucket_capabilities.end();
140
+ }
141
+
137
142
  [[nodiscard]] std::size_t index_for_this_node() const;
138
143
  [[nodiscard]] bool has_node(const std::string& network,
139
144
  service_type type,
@@ -236,6 +236,8 @@ struct traits<couchbase::core::topology::configuration> {
236
236
  result.bucket_capabilities.insert(couchbase::core::bucket_capability::range_scan);
237
237
  } else if (name == "subdoc.ReplicaRead") {
238
238
  result.bucket_capabilities.insert(couchbase::core::bucket_capability::replica_read);
239
+ } else if (name == "nonDedupedHistory") {
240
+ result.bucket_capabilities.insert(couchbase::core::bucket_capability::non_deduped_history);
239
241
  }
240
242
  }
241
243
  }
@@ -18,6 +18,7 @@
18
18
  #pragma once
19
19
 
20
20
  #include <couchbase/collection.hxx>
21
+ #include <couchbase/collection_manager.hxx>
21
22
  #include <couchbase/scope.hxx>
22
23
 
23
24
  #include <memory>
@@ -82,6 +83,19 @@ class bucket
82
83
  return { core_, name_, scope_name };
83
84
  }
84
85
 
86
+ /**
87
+ * Provides access to the collection management services.
88
+ *
89
+ * @return a manager instance
90
+ *
91
+ * @since 1.0.0
92
+ * @committed
93
+ */
94
+ [[nodiscard]] auto collections() const -> collection_manager
95
+ {
96
+ return collection_manager{ core_, name_ };
97
+ }
98
+
85
99
  private:
86
100
  friend class cluster;
87
101