couchbase 3.5.2-x86_64-darwin

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +202 -0
  3. data/README.md +154 -0
  4. data/ext/extconf.rb +0 -0
  5. data/lib/active_support/cache/couchbase_store.rb +342 -0
  6. data/lib/couchbase/3.1/libcouchbase.bundle +0 -0
  7. data/lib/couchbase/3.2/libcouchbase.bundle +0 -0
  8. data/lib/couchbase/3.3/libcouchbase.bundle +0 -0
  9. data/lib/couchbase/analytics_options.rb +109 -0
  10. data/lib/couchbase/authenticator.rb +66 -0
  11. data/lib/couchbase/binary_collection.rb +130 -0
  12. data/lib/couchbase/binary_collection_options.rb +26 -0
  13. data/lib/couchbase/bucket.rb +146 -0
  14. data/lib/couchbase/cluster.rb +462 -0
  15. data/lib/couchbase/cluster_registry.rb +49 -0
  16. data/lib/couchbase/collection.rb +707 -0
  17. data/lib/couchbase/collection_options.rb +401 -0
  18. data/lib/couchbase/config_profiles.rb +57 -0
  19. data/lib/couchbase/configuration.rb +58 -0
  20. data/lib/couchbase/datastructures/couchbase_list.rb +160 -0
  21. data/lib/couchbase/datastructures/couchbase_map.rb +194 -0
  22. data/lib/couchbase/datastructures/couchbase_queue.rb +134 -0
  23. data/lib/couchbase/datastructures/couchbase_set.rb +128 -0
  24. data/lib/couchbase/datastructures.rb +26 -0
  25. data/lib/couchbase/diagnostics.rb +183 -0
  26. data/lib/couchbase/errors.rb +414 -0
  27. data/lib/couchbase/json_transcoder.rb +41 -0
  28. data/lib/couchbase/key_value_scan.rb +119 -0
  29. data/lib/couchbase/libcouchbase.rb +6 -0
  30. data/lib/couchbase/logger.rb +87 -0
  31. data/lib/couchbase/management/analytics_index_manager.rb +1129 -0
  32. data/lib/couchbase/management/bucket_manager.rb +445 -0
  33. data/lib/couchbase/management/collection_manager.rb +472 -0
  34. data/lib/couchbase/management/collection_query_index_manager.rb +224 -0
  35. data/lib/couchbase/management/query_index_manager.rb +619 -0
  36. data/lib/couchbase/management/scope_search_index_manager.rb +200 -0
  37. data/lib/couchbase/management/search_index_manager.rb +426 -0
  38. data/lib/couchbase/management/user_manager.rb +470 -0
  39. data/lib/couchbase/management/view_index_manager.rb +239 -0
  40. data/lib/couchbase/management.rb +31 -0
  41. data/lib/couchbase/mutation_state.rb +65 -0
  42. data/lib/couchbase/options.rb +2846 -0
  43. data/lib/couchbase/protostellar/binary_collection.rb +55 -0
  44. data/lib/couchbase/protostellar/bucket.rb +55 -0
  45. data/lib/couchbase/protostellar/client.rb +99 -0
  46. data/lib/couchbase/protostellar/cluster.rb +171 -0
  47. data/lib/couchbase/protostellar/collection.rb +152 -0
  48. data/lib/couchbase/protostellar/connect_options.rb +63 -0
  49. data/lib/couchbase/protostellar/error_handling.rb +203 -0
  50. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_pb.rb +61 -0
  51. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_services_pb.rb +35 -0
  52. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_pb.rb +57 -0
  53. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_services_pb.rb +36 -0
  54. data/lib/couchbase/protostellar/generated/admin/query/v1/query_pb.rb +61 -0
  55. data/lib/couchbase/protostellar/generated/admin/query/v1/query_services_pb.rb +37 -0
  56. data/lib/couchbase/protostellar/generated/admin/search/v1/search_pb.rb +72 -0
  57. data/lib/couchbase/protostellar/generated/admin/search/v1/search_services_pb.rb +44 -0
  58. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_pb.rb +52 -0
  59. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_services_pb.rb +30 -0
  60. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_pb.rb +70 -0
  61. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_services_pb.rb +36 -0
  62. data/lib/couchbase/protostellar/generated/kv/v1/kv_pb.rb +97 -0
  63. data/lib/couchbase/protostellar/generated/kv/v1/kv_services_pb.rb +46 -0
  64. data/lib/couchbase/protostellar/generated/query/v1/query_pb.rb +57 -0
  65. data/lib/couchbase/protostellar/generated/query/v1/query_services_pb.rb +30 -0
  66. data/lib/couchbase/protostellar/generated/routing/v1/routing_pb.rb +52 -0
  67. data/lib/couchbase/protostellar/generated/routing/v1/routing_services_pb.rb +30 -0
  68. data/lib/couchbase/protostellar/generated/search/v1/search_pb.rb +99 -0
  69. data/lib/couchbase/protostellar/generated/search/v1/search_services_pb.rb +30 -0
  70. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_pb.rb +57 -0
  71. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_services_pb.rb +36 -0
  72. data/lib/couchbase/protostellar/generated/view/v1/view_pb.rb +51 -0
  73. data/lib/couchbase/protostellar/generated/view/v1/view_services_pb.rb +30 -0
  74. data/lib/couchbase/protostellar/generated.rb +9 -0
  75. data/lib/couchbase/protostellar/management/bucket_manager.rb +67 -0
  76. data/lib/couchbase/protostellar/management/collection_manager.rb +94 -0
  77. data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +124 -0
  78. data/lib/couchbase/protostellar/management/query_index_manager.rb +112 -0
  79. data/lib/couchbase/protostellar/management.rb +24 -0
  80. data/lib/couchbase/protostellar/request.rb +78 -0
  81. data/lib/couchbase/protostellar/request_behaviour.rb +42 -0
  82. data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +124 -0
  83. data/lib/couchbase/protostellar/request_generator/admin/collection.rb +94 -0
  84. data/lib/couchbase/protostellar/request_generator/admin/query.rb +130 -0
  85. data/lib/couchbase/protostellar/request_generator/admin.rb +24 -0
  86. data/lib/couchbase/protostellar/request_generator/kv.rb +474 -0
  87. data/lib/couchbase/protostellar/request_generator/query.rb +133 -0
  88. data/lib/couchbase/protostellar/request_generator/search.rb +387 -0
  89. data/lib/couchbase/protostellar/request_generator.rb +26 -0
  90. data/lib/couchbase/protostellar/response_converter/admin/bucket.rb +55 -0
  91. data/lib/couchbase/protostellar/response_converter/admin/collection.rb +42 -0
  92. data/lib/couchbase/protostellar/response_converter/admin/query.rb +59 -0
  93. data/lib/couchbase/protostellar/response_converter/admin.rb +24 -0
  94. data/lib/couchbase/protostellar/response_converter/kv.rb +151 -0
  95. data/lib/couchbase/protostellar/response_converter/query.rb +84 -0
  96. data/lib/couchbase/protostellar/response_converter/search.rb +136 -0
  97. data/lib/couchbase/protostellar/response_converter.rb +26 -0
  98. data/lib/couchbase/protostellar/retry/action.rb +38 -0
  99. data/lib/couchbase/protostellar/retry/orchestrator.rb +60 -0
  100. data/lib/couchbase/protostellar/retry/reason.rb +67 -0
  101. data/lib/couchbase/protostellar/retry/strategies/best_effort.rb +49 -0
  102. data/lib/couchbase/protostellar/retry/strategies.rb +26 -0
  103. data/lib/couchbase/protostellar/retry.rb +28 -0
  104. data/lib/couchbase/protostellar/scope.rb +57 -0
  105. data/lib/couchbase/protostellar/timeout_defaults.rb +30 -0
  106. data/lib/couchbase/protostellar/timeouts.rb +83 -0
  107. data/lib/couchbase/protostellar.rb +29 -0
  108. data/lib/couchbase/query_options.rb +122 -0
  109. data/lib/couchbase/railtie.rb +47 -0
  110. data/lib/couchbase/raw_binary_transcoder.rb +39 -0
  111. data/lib/couchbase/raw_json_transcoder.rb +40 -0
  112. data/lib/couchbase/raw_string_transcoder.rb +42 -0
  113. data/lib/couchbase/scope.rb +258 -0
  114. data/lib/couchbase/search_options.rb +1650 -0
  115. data/lib/couchbase/subdoc.rb +293 -0
  116. data/lib/couchbase/transcoder_flags.rb +64 -0
  117. data/lib/couchbase/utils/generic_logger_adapter.rb +40 -0
  118. data/lib/couchbase/utils/stdlib_logger_adapter.rb +67 -0
  119. data/lib/couchbase/utils/time.rb +71 -0
  120. data/lib/couchbase/utils.rb +23 -0
  121. data/lib/couchbase/version.rb +25 -0
  122. data/lib/couchbase/view_options.rb +67 -0
  123. data/lib/couchbase.rb +30 -0
  124. data/lib/rails/generators/couchbase/config/config_generator.rb +29 -0
  125. metadata +190 -0
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
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
+ require "couchbase"
18
+ require "digest/sha2"
19
+ require "active_support/cache"
20
+
21
+ module ActiveSupport
22
+ module Cache
23
+ # A cache store implementation which stores data in Couchbase: https://couchbase.com
24
+ #
25
+ # * Local cache. Hot in-memory primary cache within block/middleware scope.
26
+ # * +read_multi+ and +write_multi+ support.
27
+ # * +delete_matched+ support using N1QL queries.
28
+ # * +clear+ for erasing whole collection (optionally can flush the bucket).
29
+ #
30
+ # To use this store, add the select it in application config
31
+ #
32
+ # config.cache_store = :couchbase_store, {
33
+ # connection_string: "couchbase://localhost",
34
+ # username: "app_cache_user",
35
+ # password: "s3cret",
36
+ # bucket: "app_cache"
37
+ # }
38
+ #
39
+ # @see https://guides.rubyonrails.org/caching_with_rails.html#cache-stores
40
+ class CouchbaseStore < Store
41
+ MAX_KEY_BYTESIZE = 250
42
+ DEFAULT_ERROR_HANDLER = lambda do |method:, returning:, exception:, logger: CouchbaseStore.logger|
43
+ logger&.error { "CouchbaseStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
44
+ end
45
+
46
+ # Advertise cache versioning support.
47
+ def self.supports_cache_versioning?
48
+ true
49
+ end
50
+
51
+ prepend Strategy::LocalCache
52
+
53
+ def initialize(options = nil)
54
+ super
55
+ @error_handler = @options.delete(:error_handler) { DEFAULT_ERROR_HANDLER }
56
+ @couchbase_options = {}
57
+ @couchbase_options[:connection_string] =
58
+ @options.delete(:connection_string) do
59
+ raise ArgumentError, "Missing connection string for Couchbase cache store. Use :connection_string in the store options"
60
+ end
61
+ @couchbase_options[:username] =
62
+ @options.delete(:username) do
63
+ raise ArgumentError, "Missing username for Couchbase cache store. Use :username in the store options"
64
+ end
65
+ @couchbase_options[:password] =
66
+ @options.delete(:password) do
67
+ raise ArgumentError, "Missing password for Couchbase cache store. Use :password in the store options"
68
+ end
69
+ @couchbase_options[:bucket] =
70
+ @options.delete(:bucket) { raise ArgumentError, "Missing bucket for Couchbase cache store. Use :bucket in the store options" }
71
+ @couchbase_options[:scope] = @options.delete(:scope) if @options.key?(:scope)
72
+ @couchbase_options[:collection] = @options.delete(:collection) if @options.key?(:collection)
73
+ @last_mutation_token = nil
74
+ end
75
+
76
+ def collection
77
+ @collection ||= build_collection
78
+ end
79
+
80
+ def cluster
81
+ @cluster ||= build_cluster
82
+ end
83
+
84
+ def inspect
85
+ "#<#{self.class} options=#{options.inspect} collection=#{@collection.inspect}>"
86
+ end
87
+
88
+ # Deletes all entries with keys matching the regular expression.
89
+ #
90
+ # The +matcher+ must be valid pattern for N1QL +REGEXP_MATCHES+ function. More info at
91
+ # https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/patternmatchingfun.html#section_regex_matches
92
+ #
93
+ # Because the operation performed on query engine, and it might take time to propagate changes
94
+ # from key/value engine to the indexer. Therefore the keys, that were created a moment ago
95
+ # might not be deleted.
96
+ #
97
+ # Also this method assumes, that primary index created on the target bucket
98
+ def delete_matched(matcher, _options = nil)
99
+ pattern =
100
+ case matcher
101
+ when Regexp
102
+ matcher.inspect[1..-2]
103
+ when String
104
+ matcher.tr("?", ".").gsub("*", ".*")
105
+ else
106
+ raise NotImplementedError, "Unable to convert #{matcher.inspect} to Regexp pattern"
107
+ end
108
+ operation_options = ::Couchbase::Options::Query(named_parameters: {"pattern" => pattern}, metrics: true)
109
+ operation_options.consistent_with(::Couchbase::MutationState.new(@last_mutation_token)) if @last_mutation_token
110
+ begin
111
+ result = cluster.query("DELETE FROM #{scope_qualifier} cache_store_ WHERE REGEXP_MATCHES(META(cache_store_).id, $pattern)",
112
+ operation_options)
113
+ result.meta_data.metrics.mutation_count
114
+ rescue ::Couchbase::Error::ParsingFailure, ::Couchbase::Error::ServiceNotAvailable
115
+ raise NotImplementedError, "The server does not support delete_matched operation"
116
+ end
117
+ end
118
+
119
+ # Increments an integer value in the cache.
120
+ #
121
+ # Note that it uses binary collection interface, therefore will fail if the value is not
122
+ # represented as ASCII-encoded number using +:raw+.
123
+ def increment(name, amount = 1, expires_in: nil, initial: nil, **options)
124
+ instrument :increment, name, amount: amount do
125
+ failsafe :increment do
126
+ key = normalize_key(name, options)
127
+ res = collection.binary.increment(
128
+ key, ::Couchbase::Options::Increment(delta: amount, expiry: expires_in, initial: initial)
129
+ )
130
+ @last_mutation_token = res.mutation_token
131
+ res.content
132
+ end
133
+ end
134
+ end
135
+
136
+ # Decrements an integer value in the cache.
137
+ #
138
+ # Note that it uses binary collection interface, therefore will fail if the value is not
139
+ # represented as ASCII-encoded number using +:raw+.
140
+ #
141
+ # Note that the counter represented by non-negative number on the server, and it will not
142
+ # cycle to maximum integer when decrementing zero.
143
+ def decrement(name, amount = 1, expires_in: nil, initial: nil, **_options)
144
+ instrument :decrement, name, amount: amount do
145
+ failsafe :decrement do
146
+ key = normalize_key(name, options)
147
+ res = collection.binary.decrement(
148
+ key, ::Couchbase::Options::Decrement(delta: amount, expiry: expires_in, initial: initial)
149
+ )
150
+ @last_mutation_token = res.mutation_token
151
+ res.content
152
+ end
153
+ end
154
+ end
155
+
156
+ # Clears the entire cache. Be careful with this method since it could
157
+ # affect other processes if shared cache is being used.
158
+ #
159
+ # When +use_flush+ option set to +true+ it will flush the bucket. Otherwise, it uses N1QL query
160
+ # and relies on default index.
161
+ def clear(use_flush: false, **_options)
162
+ failsafe(:clear) do
163
+ if use_flush
164
+ cluster.buckets.flush_bucket(@couchbase_options[:bucket_name])
165
+ else
166
+ operation_options = ::Couchbase::Options::Query.new
167
+ operation_options.consistent_with(::Couchbase::MutationState.new(@last_mutation_token)) if @last_mutation_token
168
+ cluster.query("DELETE FROM #{scope_qualifier}", operation_options)
169
+ end
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ def deserialize_entry(payload, raw: false, **)
176
+ if payload && raw
177
+ Entry.new(payload, compress: false)
178
+ else
179
+ super(payload)
180
+ end
181
+ end
182
+
183
+ def serialize_entry(entry, raw: false, **options)
184
+ if raw
185
+ entry.value.to_s
186
+ else
187
+ super(entry, **options)
188
+ end
189
+ end
190
+
191
+ def serialize_entries(entries, **options)
192
+ entries.transform_values do |entry|
193
+ serialize_entry(entry, **options)
194
+ end
195
+ end
196
+
197
+ # Reads an entry from the cache
198
+ def read_entry(key, **options)
199
+ deserialize_entry(read_serialized_entry(key, **options), **options)
200
+ end
201
+
202
+ def read_serialized_entry(key, **)
203
+ failsafe(:read_entry, returning: nil) do
204
+ res = collection.get(key, ::Couchbase::Options::Get(transcoder: nil))
205
+ res.content
206
+ end
207
+ end
208
+
209
+ # Reads multiple entries from the cache implementation. Subclasses MAY
210
+ # implement this method.
211
+ def read_multi_entries(names, **options)
212
+ return {} if names.empty?
213
+
214
+ keys = names.map { |name| normalize_key(name, options) }
215
+ return_value = {}
216
+ failsafe(:read_multi_entries, returning: return_value) do
217
+ results = collection.get_multi(keys, ::Couchbase::Options::GetMulti(transcoder: nil))
218
+ results.each_with_index do |result, index|
219
+ next unless result.success?
220
+
221
+ entry = deserialize_entry(result.content, raw: options[:raw])
222
+ unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(names[index], options))
223
+ return_value[names[index]] = entry.value
224
+ end
225
+ end
226
+ return_value
227
+ end
228
+ end
229
+
230
+ # Writes an entry to the cache
231
+ def write_entry(key, entry, raw: false, **options)
232
+ write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options)
233
+ end
234
+
235
+ def write_serialized_entry(key, payload, expires_in: nil, race_condition_ttl: nil, raw: false, **)
236
+ if race_condition_ttl && expires_in && expires_in.positive? && !raw
237
+ # Add few minutes to expiry in the future to support race condition TTLs on read
238
+ expires_in += 5.minutes
239
+ end
240
+ failsafe(:write_entry, returning: false) do
241
+ res = collection.upsert(key, payload, ::Couchbase::Options::Upsert(transcoder: nil, expiry: expires_in))
242
+ @last_mutation_token = res.mutation_token
243
+ true
244
+ end
245
+ end
246
+
247
+ def write_multi_entries(entries, **options)
248
+ return 0 if entries.empty?
249
+
250
+ return super if local_cache
251
+
252
+ expires_in = options[:expires_in]
253
+ if options[:race_condition_ttl] && expires_in && expires_in.positive?
254
+ # Add few minutes to expiry in the future to support race condition TTLs on read
255
+ expires_in += 5.minutes
256
+ end
257
+ operation_options = ::Couchbase::Options::UpsertMulti(transcoder: nil, expiry: expires_in)
258
+ failsafe(:write_multi_entries, returning: nil) do
259
+ successful = collection.upsert_multi(serialize_entries(entries, **options), operation_options).select(&:success?)
260
+ return 0 if successful.empty?
261
+
262
+ @last_mutation_token = successful.max_by { |r| r.mutation_token.sequence_number }
263
+ successful.count
264
+ end
265
+ end
266
+
267
+ # Deletes an entry from the cache implementation. Subclasses must
268
+ # implement this method.
269
+ def delete_entry(key, **_options)
270
+ failsafe(:delete_entry, returning: false) do
271
+ res = collection.remove(key)
272
+ @last_mutation_token = res.mutation_token
273
+ true
274
+ end
275
+ end
276
+
277
+ # Deletes multiple entries in the cache. Returns the number of entries deleted.
278
+ def delete_multi_entries(entries, **_options)
279
+ return if entries.empty?
280
+
281
+ failsafe(:delete_multi_entries, returning: nil) do
282
+ successful = collection.remove_multi(entries).select(&:success?)
283
+ return 0 if successful.empty?
284
+
285
+ @last_mutation_token = successful.max_by { |r| r.mutation_token.sequence_number }
286
+ successful.count
287
+ end
288
+ end
289
+
290
+ def failsafe(method, returning: nil)
291
+ yield
292
+ rescue ::Couchbase::Error::CouchbaseError => e
293
+ ActiveSupport.error_reporter&.report(e, handled: true, severity: :warning)
294
+ @error_handler&.call(method: method, exception: e, returning: returning)
295
+ returning
296
+ end
297
+
298
+ # Truncate keys that exceed 250 characters
299
+ def normalize_key(key, options)
300
+ truncate_key super
301
+ end
302
+
303
+ def truncate_key(key)
304
+ if key && key.bytesize > MAX_KEY_BYTESIZE
305
+ suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
306
+ truncate_at = MAX_KEY_BYTESIZE - suffix.bytesize
307
+ "#{key.mb_chars.limit(truncate_at)}#{suffix}"
308
+ else
309
+ key
310
+ end
311
+ end
312
+
313
+ # Connects to the Couchbase cluster
314
+ def build_cluster
315
+ ::Couchbase::Cluster.connect(
316
+ @couchbase_options[:connection_string],
317
+ ::Couchbase::Options::Cluster(authenticator: ::Couchbase::PasswordAuthenticator.new(
318
+ @couchbase_options[:username], @couchbase_options[:password]
319
+ ))
320
+ )
321
+ end
322
+
323
+ # Connects to the Couchbase cluster, opens specified bucket and returns collection object.
324
+ def build_collection
325
+ bucket = cluster.bucket(@couchbase_options[:bucket])
326
+ if @couchbase_options[:scope] && @couchbase_options[:collection]
327
+ bucket.scope(@couchbase_options[:scope]).collection(@couchbase_options[:collection])
328
+ else
329
+ bucket.default_collection
330
+ end
331
+ end
332
+
333
+ def scope_qualifier
334
+ if @couchbase_options[:scope] && @couchbase_options[:collection]
335
+ "`#{@couchbase_options[:bucket]}`.#{@couchbase_options[:scope]}.#{@couchbase_options[:collection]}"
336
+ else
337
+ "`#{@couchbase_options[:bucket]}`"
338
+ end
339
+ end
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
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
+ require "couchbase/json_transcoder"
18
+
19
+ module Couchbase
20
+ class Cluster
21
+ class AnalyticsWarning
22
+ # @return [Integer]
23
+ attr_accessor :code
24
+
25
+ # @return [String]
26
+ attr_accessor :message
27
+
28
+ # @param [Integer] code
29
+ # @param [String] message
30
+ def initialize(code, message)
31
+ @code = code
32
+ @message = message
33
+ end
34
+ end
35
+
36
+ class AnalyticsMetrics
37
+ # @return [Integer] duration in milliseconds
38
+ attr_accessor :elapsed_time
39
+
40
+ # @return [Integer] duration in milliseconds
41
+ attr_accessor :execution_time
42
+
43
+ # @return [Integer]
44
+ attr_accessor :result_count
45
+
46
+ # @return [Integer]
47
+ attr_accessor :result_size
48
+
49
+ # @return [Integer]
50
+ attr_accessor :error_count
51
+
52
+ # @return [Integer]
53
+ attr_accessor :processed_objects
54
+
55
+ # @return [Integer]
56
+ attr_accessor :warning_count
57
+
58
+ # @yieldparam [AnalyticsMetrics] self
59
+ def initialize
60
+ yield self if block_given?
61
+ end
62
+ end
63
+
64
+ class AnalyticsMetaData
65
+ # @return [String]
66
+ attr_accessor :request_id
67
+
68
+ # @return [String]
69
+ attr_accessor :client_context_id
70
+
71
+ # @return [:running, :success, :errors, :completed, :stopped, :timeout, :closed, :fatal, :aborted, :unknown]
72
+ attr_accessor :status
73
+
74
+ # @return [Hash] returns the signature as returned by the query engine which is then decoded as JSON object
75
+ attr_accessor :signature
76
+
77
+ # @return [Array<AnalyticsWarning>]
78
+ attr_accessor :warnings
79
+
80
+ # @return [AnalyticsMetrics]
81
+ attr_accessor :metrics
82
+
83
+ # @yieldparam [AnalyticsMetaData] self
84
+ def initialize
85
+ yield self if block_given?
86
+ end
87
+ end
88
+
89
+ class AnalyticsResult
90
+ # @return [AnalyticsMetaData]
91
+ attr_accessor :meta_data
92
+
93
+ attr_accessor :transcoder
94
+
95
+ # Returns all rows converted using a transcoder
96
+ #
97
+ # @return [Array]
98
+ def rows(transcoder = self.transcoder)
99
+ @rows.lazy.map { |row| transcoder.decode(row, 0) }
100
+ end
101
+
102
+ # @yieldparam [AnalyticsResult] self
103
+ def initialize
104
+ @transcoder = JsonTranscoder.new
105
+ yield self if block_given?
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
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
+ module Couchbase
18
+ # Authenticator for username/password credentials
19
+ class PasswordAuthenticator
20
+ DEFAULT_SASL_MECHANISMS = [:scram_sha512, :scram_sha256, :scram_sha1].freeze
21
+
22
+ attr_accessor :username
23
+ attr_accessor :password
24
+ attr_accessor :allowed_sasl_mechanisms
25
+
26
+ # Creates a new password authenticator with the default settings.
27
+ #
28
+ # @param [String] password the username to use for all authentication requests
29
+ # @param [String] username the password
30
+ def initialize(username, password)
31
+ @username = username
32
+ @password = password
33
+ end
34
+
35
+ # Creates a LDAP compatible password authenticator which is INSECURE if not used with TLS.
36
+ #
37
+ # Please note that this is INSECURE and will leak user credentials on the wire to eavesdroppers. This should
38
+ # only be enabled in trusted environments.
39
+ #
40
+ # @param [String] username the username to use for all authentication.
41
+ # @param [String] password the password to use alongside the username.
42
+ # @return [PasswordAuthenticator]
43
+ def self.ldap_compatible(username, password)
44
+ new(username, password).tap do |auth|
45
+ auth.allowed_sasl_mechanisms = [:plain]
46
+ end
47
+ end
48
+ end
49
+
50
+ # Authenticator for TLS client certificate
51
+ #
52
+ # @see https://docs.couchbase.com/server/current/manage/manage-security/configure-client-certificates.html
53
+ class CertificateAuthenticator
54
+ attr_accessor :certificate_path
55
+ attr_accessor :key_path
56
+
57
+ # Creates a new authenticator with certificate and key paths
58
+ #
59
+ # @param [String] certificate_path path to certificate
60
+ # @param [String] key_path path to private key
61
+ def initialize(certificate_path, key_path)
62
+ @certificate_path = certificate_path
63
+ @key_path = key_path
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
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
+ require "couchbase/options"
18
+ require "couchbase/binary_collection_options"
19
+
20
+ module Couchbase
21
+ # Allows to perform certain operations on non-JSON documents.
22
+ class BinaryCollection
23
+ alias inspect to_s
24
+
25
+ # @param [Couchbase::Collection] collection parent collection
26
+ def initialize(collection)
27
+ @collection = collection
28
+ @backend = collection.instance_variable_get(:@backend)
29
+ end
30
+
31
+ # Appends binary content to the document
32
+ #
33
+ # @param [String] id the document id which is used to uniquely identify it
34
+ # @param [String] content the binary content to append to the document
35
+ # @param [Options::Append] options custom options to customize the request
36
+ #
37
+ # @example Append "bar" to the content of the existing document
38
+ # collection.upsert("mydoc", "foo")
39
+ # collection.binary.append("mydoc", "bar", Options::Append(timeout: 3_000))
40
+ # collection.get("mydoc", Options::Get(transcoder: nil)).content #=> "foobar"
41
+ #
42
+ # @return [Collection::MutationResult]
43
+ def append(id, content, options = Options::Append::DEFAULT)
44
+ resp = @backend.document_append(@collection.bucket_name, @collection.scope_name, @collection.name,
45
+ id, content, options.to_backend)
46
+ Collection::MutationResult.new do |res|
47
+ res.cas = resp[:cas]
48
+ res.mutation_token = @collection.send(:extract_mutation_token, resp)
49
+ end
50
+ end
51
+
52
+ # Prepends binary content to the document
53
+ #
54
+ # @param [String] id the document id which is used to uniquely identify it
55
+ # @param [String] content the binary content to prepend to the document
56
+ # @param [Options::Prepend] options custom options to customize the request
57
+ #
58
+ # @example Prepend "bar" to the content of the existing document
59
+ # collection.upsert("mydoc", "foo")
60
+ # collection.binary.prepend("mydoc", "bar", Options::Prepend(timeout: 3_000))
61
+ # collection.get("mydoc", Options::Get(transcoder: nil)).content #=> "barfoo"
62
+ #
63
+ # @return [Collection::MutationResult]
64
+ def prepend(id, content, options = Options::Prepend::DEFAULT)
65
+ resp = @backend.document_prepend(@collection.bucket_name, @collection.scope_name, @collection.name,
66
+ id, content, options.to_backend)
67
+ Collection::MutationResult.new do |res|
68
+ res.cas = resp[:cas]
69
+ res.mutation_token = @collection.send(:extract_mutation_token, resp)
70
+ end
71
+ end
72
+
73
+ # Increments the counter document by one of the number defined in the options
74
+ #
75
+ # @param [String] id the document id which is used to uniquely identify it
76
+ # @param [Options::Increment] options custom options to customize the request
77
+ #
78
+ # @example Increment value by 10, and initialize to 0 if it does not exist
79
+ # res = collection.binary.increment("raw_counter", Options::Increment(delta: 10, initial: 0))
80
+ # res.content #=> 0
81
+ # res = collection.binary.increment("raw_counter", Options::Increment(delta: 10, initial: 0))
82
+ # res.content #=> 10
83
+ #
84
+ # @return [CounterResult]
85
+ def increment(id, options = Options::Increment::DEFAULT)
86
+ resp = @backend.document_increment(@collection.bucket_name, @collection.scope_name, @collection.name, id,
87
+ options.to_backend)
88
+ CounterResult.new do |res|
89
+ res.cas = resp[:cas]
90
+ res.content = resp[:content]
91
+ res.mutation_token = @collection.send(:extract_mutation_token, resp)
92
+ end
93
+ end
94
+
95
+ # Decrements the counter document by one of the number defined in the options
96
+ #
97
+ # @param [String] id the document id which is used to uniquely identify it
98
+ # @param [Options::Decrement] options custom options to customize the request
99
+ #
100
+ # @example Decrement value by 2, and initialize to 100 if it does not exist
101
+ # res = collection.binary.decrement("raw_counter", Options::Decrement(delta: 2, initial: 100))
102
+ # res.value #=> 100
103
+ # res = collection.binary.decrement("raw_counter", Options::Decrement(delta: 2, initial: 100))
104
+ # res.value #=> 98
105
+ #
106
+ # @return [CounterResult]
107
+ def decrement(id, options = Options::Decrement::DEFAULT)
108
+ resp = @backend.document_decrement(@collection.bucket_name, @collection.scope_name, @collection.name, id,
109
+ options.to_backend)
110
+ CounterResult.new do |res|
111
+ res.cas = resp[:cas]
112
+ res.content = resp[:content]
113
+ res.mutation_token = @collection.send(:extract_mutation_token, resp)
114
+ end
115
+ end
116
+
117
+ # @api private
118
+ # TODO: deprecate in 3.1
119
+ AppendOptions = ::Couchbase::Options::Append
120
+ # @api private
121
+ # TODO: deprecate in 3.1
122
+ PrependOptions = ::Couchbase::Options::Prepend
123
+ # @api private
124
+ # TODO: deprecate in 3.1
125
+ IncrementOptions = ::Couchbase::Options::Increment
126
+ # @api private
127
+ # TODO: deprecate in 3.1
128
+ DecrementOptions = ::Couchbase::Options::Decrement
129
+ end
130
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
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
+ require "couchbase/collection_options"
18
+
19
+ module Couchbase
20
+ class BinaryCollection
21
+ class CounterResult < ::Couchbase::Collection::MutationResult
22
+ # @return [Integer] current value of the counter
23
+ attr_accessor :content
24
+ end
25
+ end
26
+ end