couchbase 3.4.0-arm64-darwin-20

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 (50) 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 +339 -0
  6. data/lib/couchbase/analytics_options.rb +107 -0
  7. data/lib/couchbase/authenticator.rb +65 -0
  8. data/lib/couchbase/binary_collection.rb +128 -0
  9. data/lib/couchbase/binary_collection_options.rb +24 -0
  10. data/lib/couchbase/bucket.rb +144 -0
  11. data/lib/couchbase/cluster.rb +439 -0
  12. data/lib/couchbase/cluster_registry.rb +44 -0
  13. data/lib/couchbase/collection.rb +589 -0
  14. data/lib/couchbase/collection_options.rb +300 -0
  15. data/lib/couchbase/config_profiles.rb +55 -0
  16. data/lib/couchbase/configuration.rb +57 -0
  17. data/lib/couchbase/datastructures/couchbase_list.rb +160 -0
  18. data/lib/couchbase/datastructures/couchbase_map.rb +194 -0
  19. data/lib/couchbase/datastructures/couchbase_queue.rb +134 -0
  20. data/lib/couchbase/datastructures/couchbase_set.rb +128 -0
  21. data/lib/couchbase/datastructures.rb +24 -0
  22. data/lib/couchbase/diagnostics.rb +181 -0
  23. data/lib/couchbase/errors.rb +351 -0
  24. data/lib/couchbase/json_transcoder.rb +32 -0
  25. data/lib/couchbase/libcouchbase.bundle +0 -0
  26. data/lib/couchbase/logger.rb +85 -0
  27. data/lib/couchbase/management/analytics_index_manager.rb +1127 -0
  28. data/lib/couchbase/management/bucket_manager.rb +436 -0
  29. data/lib/couchbase/management/collection_manager.rb +321 -0
  30. data/lib/couchbase/management/query_index_manager.rb +520 -0
  31. data/lib/couchbase/management/search_index_manager.rb +408 -0
  32. data/lib/couchbase/management/user_manager.rb +468 -0
  33. data/lib/couchbase/management/view_index_manager.rb +237 -0
  34. data/lib/couchbase/management.rb +27 -0
  35. data/lib/couchbase/mutation_state.rb +63 -0
  36. data/lib/couchbase/options.rb +2580 -0
  37. data/lib/couchbase/query_options.rb +120 -0
  38. data/lib/couchbase/railtie.rb +45 -0
  39. data/lib/couchbase/scope.rb +232 -0
  40. data/lib/couchbase/search_options.rb +1570 -0
  41. data/lib/couchbase/subdoc.rb +290 -0
  42. data/lib/couchbase/utils/generic_logger_adapter.rb +38 -0
  43. data/lib/couchbase/utils/stdlib_logger_adapter.rb +65 -0
  44. data/lib/couchbase/utils/time.rb +56 -0
  45. data/lib/couchbase/utils.rb +21 -0
  46. data/lib/couchbase/version.rb +23 -0
  47. data/lib/couchbase/view_options.rb +65 -0
  48. data/lib/couchbase.rb +20 -0
  49. data/lib/rails/generators/couchbase/config/config_generator.rb +27 -0
  50. metadata +101 -0
@@ -0,0 +1,144 @@
1
+ # Copyright 2020-2021 Couchbase, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "couchbase/scope"
16
+ require "couchbase/management/collection_manager"
17
+ require "couchbase/management/view_index_manager"
18
+ require "couchbase/options"
19
+ require "couchbase/view_options"
20
+ require "couchbase/diagnostics"
21
+
22
+ module Couchbase
23
+ # Provides access to a Couchbase bucket APIs
24
+ class Bucket
25
+ # @return [String] name of the bucket
26
+ attr_reader :name
27
+
28
+ alias inspect to_s
29
+
30
+ # @param [Couchbase::Backend] backend
31
+ def initialize(backend, name)
32
+ backend.open_bucket(name, true)
33
+ @backend = backend
34
+ @name = name
35
+ end
36
+
37
+ # Get default scope
38
+ #
39
+ # @return [Scope]
40
+ def default_scope
41
+ Scope.new(@backend, @name, "_default")
42
+ end
43
+
44
+ # Get a named scope
45
+ #
46
+ # @param [String] scope_name name of the scope
47
+ #
48
+ # @return [Scope]
49
+ def scope(scope_name)
50
+ Scope.new(@backend, @name, scope_name)
51
+ end
52
+
53
+ # Opens the named collection in the default scope of the bucket
54
+ #
55
+ # @param [String] collection_name name of the collection
56
+ #
57
+ # @return [Collection]
58
+ def collection(collection_name)
59
+ default_scope.collection(collection_name)
60
+ end
61
+
62
+ # Opens the default collection for this bucket
63
+ #
64
+ # @return [Collection]
65
+ def default_collection
66
+ Collection.new(@backend, @name, "_default", "_default")
67
+ end
68
+
69
+ # Performs query to view index.
70
+ #
71
+ # @param [String] design_document_name name of the design document
72
+ # @param [String] view_name name of the view to query
73
+ # @param [Options::View] options
74
+ #
75
+ # @example Make sure the view engine catch up with all mutations and return keys starting from +["random_brewery:"]+
76
+ # bucket.view_query("beer", "brewery_beers",
77
+ # Options::View(
78
+ # start_key: ["random_brewery:"],
79
+ # scan_consistency: :request_plus
80
+ # ))
81
+ #
82
+ # @return [ViewResult]
83
+ def view_query(design_document_name, view_name, options = Options::View::DEFAULT)
84
+ resp = @backend.document_view(@name, design_document_name, view_name, options.namespace, options.to_backend)
85
+ ViewResult.new do |res|
86
+ res.meta_data = ViewMetaData.new do |meta|
87
+ meta.total_rows = resp[:meta][:total_rows]
88
+ meta.debug_info = resp[:meta][:debug_info]
89
+ end
90
+ res.rows = resp[:rows].map do |entry|
91
+ ViewRow.new do |row|
92
+ row.id = entry[:id] if entry.key?(:id)
93
+ row.key = JSON.parse(entry[:key])
94
+ row.value = JSON.parse(entry[:value])
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ # @return [Management::CollectionManager]
101
+ def collections
102
+ Management::CollectionManager.new(@backend, @name)
103
+ end
104
+
105
+ # @return [Management::ViewIndexManager]
106
+ def view_indexes
107
+ Management::ViewIndexManager.new(@backend, @name)
108
+ end
109
+
110
+ # Performs application-level ping requests against services in the couchbase cluster
111
+ #
112
+ # @param [Options::Ping] options
113
+ #
114
+ # @return [PingResult]
115
+ def ping(options = Options::Ping::DEFAULT)
116
+ resp = @backend.ping(@name, options.to_backend)
117
+ PingResult.new do |res|
118
+ res.version = resp[:version]
119
+ res.id = resp[:id]
120
+ res.sdk = resp[:sdk]
121
+ resp[:services].each do |type, svcs|
122
+ res.services[type] = svcs.map do |svc|
123
+ PingResult::ServiceInfo.new do |info|
124
+ info.id = svc[:id]
125
+ info.state = svc[:state]
126
+ info.latency = svc[:latency]
127
+ info.remote = svc[:remote]
128
+ info.local = svc[:local]
129
+ info.error = svc[:error]
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ # @api private
137
+ # TODO: deprecate in 3.1
138
+ PingOptions = ::Couchbase::Options::Ping
139
+
140
+ # @api private
141
+ # TODO: deprecate in 3.1
142
+ ViewOptions = ::Couchbase::Options::View
143
+ end
144
+ end
@@ -0,0 +1,439 @@
1
+ # Copyright 2020-2021 Couchbase, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "couchbase/configuration"
16
+ require "couchbase/authenticator"
17
+ require "couchbase/bucket"
18
+ require "couchbase/cluster_registry"
19
+
20
+ require "couchbase/management"
21
+ require "couchbase/options"
22
+
23
+ require "couchbase/search_options"
24
+ require "couchbase/query_options"
25
+ require "couchbase/analytics_options"
26
+ require "couchbase/diagnostics"
27
+
28
+ module Couchbase
29
+ # The main entry point when connecting to a Couchbase cluster.
30
+ class Cluster
31
+ alias inspect to_s
32
+
33
+ # Connect to the Couchbase cluster
34
+ #
35
+ # @overload connect(connection_string, options)
36
+ # @param [String] connection_string connection string used to locate the Couchbase Cluster
37
+ # @param [Options::Cluster] options custom options when creating the cluster connection
38
+ #
39
+ # @overload connect(connection_string, username, password, options)
40
+ # Shortcut for {PasswordAuthenticator}
41
+ # @param [String] connection_string connection string used to locate the Couchbase Cluster
42
+ # @param [String] username name of the user
43
+ # @param [String] password password of the user
44
+ # @param [Options::Cluster, nil] options custom options when creating the cluster connection
45
+ #
46
+ # @example Explicitly create options object and initialize PasswordAuthenticator internally
47
+ # options = Cluster::ClusterOptions.new
48
+ # options.authenticate("Administrator", "password")
49
+ # Cluster.connect("couchbase://localhost", options)
50
+ #
51
+ # @example Pass authenticator object to Options
52
+ # Cluster.connect("couchbase://localhost",
53
+ # Options::Cluster(authenticator: PasswordAuthenticator.new("Administrator", "password")))
54
+ #
55
+ # @example Shorter version, more useful for interactive sessions
56
+ # Cluster.connect("couchbase://localhost", "Administrator", "password")
57
+ #
58
+ # @example Authentication with TLS client certificate (note +couchbases://+ schema)
59
+ # Cluster.connect("couchbases://localhost?trust_certificate=/tmp/ca.pem",
60
+ # Options::Cluster(authenticator: CertificateAuthenticator.new("/tmp/certificate.pem", "/tmp/private.key")))
61
+ #
62
+ # @see https://docs.couchbase.com/server/current/manage/manage-security/configure-client-certificates.html
63
+ #
64
+ # @return [Cluster]
65
+ def self.connect(connection_string, *options)
66
+ regexp = /^((couchbases?|http):\/\/.*)$/i
67
+ if regexp.match?(connection_string) || !connection_string.include?("://")
68
+ Cluster.new(connection_string, *options)
69
+ else
70
+ ClusterRegistry.instance.connect(connection_string, *options)
71
+ end
72
+ end
73
+
74
+ # Returns an instance of the {Bucket}
75
+ #
76
+ # @param [String] name name of the bucket
77
+ #
78
+ # @return [Bucket]
79
+ def bucket(name)
80
+ Bucket.new(@backend, name)
81
+ end
82
+
83
+ # Performs a query against the query (N1QL) services
84
+ #
85
+ # @param [String] statement the N1QL query statement
86
+ # @param [Options::Query] options the custom options for this query
87
+ #
88
+ # @example Select first ten hotels from travel sample dataset
89
+ # cluster.query("SELECT * FROM `travel-sample` WHERE type = $type LIMIT 10",
90
+ # Options::Query(named_parameters: {type: "hotel"}, metrics: true))
91
+ #
92
+ # @example Execute query with consistency requirement. Make sure that the index is in sync with selected mutation
93
+ # res = collection.upsert("user:42", {
94
+ # "name" => "Brass Doorknob",
95
+ # "email" => "brass.doorknob@example.com",
96
+ # })
97
+ # cluster.query("SELECT name, email FROM `mybucket`",
98
+ # Options::Query(consistent_with: MutationState.new(res.mutation_token)))
99
+ #
100
+ # @return [QueryResult]
101
+ def query(statement, options = Options::Query::DEFAULT)
102
+ resp = @backend.document_query(statement, options.to_backend)
103
+
104
+ QueryResult.new do |res|
105
+ res.meta_data = QueryMetaData.new do |meta|
106
+ meta.status = resp[:meta][:status]
107
+ meta.request_id = resp[:meta][:request_id]
108
+ meta.client_context_id = resp[:meta][:client_context_id]
109
+ meta.signature = JSON.parse(resp[:meta][:signature]) if resp[:meta][:signature]
110
+ meta.profile = JSON.parse(resp[:meta][:profile]) if resp[:meta][:profile]
111
+ meta.metrics = QueryMetrics.new do |metrics|
112
+ if resp[:meta][:metrics]
113
+ metrics.elapsed_time = resp[:meta][:metrics][:elapsed_time]
114
+ metrics.execution_time = resp[:meta][:metrics][:execution_time]
115
+ metrics.sort_count = resp[:meta][:metrics][:sort_count]
116
+ metrics.result_count = resp[:meta][:metrics][:result_count]
117
+ metrics.result_size = resp[:meta][:metrics][:result_size]
118
+ metrics.mutation_count = resp[:meta][:metrics][:mutation_count]
119
+ metrics.error_count = resp[:meta][:metrics][:error_count]
120
+ metrics.warning_count = resp[:meta][:metrics][:warning_count]
121
+ end
122
+ end
123
+ res[:warnings] = resp[:warnings].map { |warn| QueryWarning.new(warn[:code], warn[:message]) } if resp[:warnings]
124
+ end
125
+ res.instance_variable_set(:@rows, resp[:rows])
126
+ end
127
+ end
128
+
129
+ # Performs an analytics query
130
+ #
131
+ # @param [String] statement the N1QL query statement
132
+ # @param [Options::Analytics] options the custom options for this query
133
+ #
134
+ # @example Select name of the given user
135
+ # cluster.analytics_query("SELECT u.name AS uname FROM GleambookUsers u WHERE u.id = $user_id ",
136
+ # Options::Analytics(named_parameters: {user_id: 2}))
137
+ #
138
+ # @return [AnalyticsResult]
139
+ def analytics_query(statement, options = Options::Analytics::DEFAULT)
140
+ resp = @backend.document_analytics(statement, options.to_backend)
141
+
142
+ AnalyticsResult.new do |res|
143
+ res.transcoder = options.transcoder
144
+ res.meta_data = AnalyticsMetaData.new do |meta|
145
+ meta.status = resp[:meta][:status]
146
+ meta.request_id = resp[:meta][:request_id]
147
+ meta.client_context_id = resp[:meta][:client_context_id]
148
+ meta.signature = JSON.parse(resp[:meta][:signature]) if resp[:meta][:signature]
149
+ meta.profile = JSON.parse(resp[:meta][:profile]) if resp[:meta][:profile]
150
+ meta.metrics = AnalyticsMetrics.new do |metrics|
151
+ if resp[:meta][:metrics]
152
+ metrics.elapsed_time = resp[:meta][:metrics][:elapsed_time]
153
+ metrics.execution_time = resp[:meta][:metrics][:execution_time]
154
+ metrics.result_count = resp[:meta][:metrics][:result_count]
155
+ metrics.result_size = resp[:meta][:metrics][:result_size]
156
+ metrics.error_count = resp[:meta][:metrics][:error_count]
157
+ metrics.warning_count = resp[:meta][:metrics][:warning_count]
158
+ metrics.processed_objects = resp[:meta][:metrics][:processed_objects]
159
+ end
160
+ end
161
+ res[:warnings] = resp[:warnings].map { |warn| AnalyticsWarning.new(warn[:code], warn[:message]) } if resp[:warnings]
162
+ end
163
+ res.instance_variable_set(:@rows, resp[:rows])
164
+ end
165
+ end
166
+
167
+ # Performs a Full Text Search (FTS) query
168
+ #
169
+ # @param [String] index_name the name of the search index
170
+ # @param [SearchQuery] query the query tree
171
+ # @param [Options::Search] options the query tree
172
+ #
173
+ # @example Return first 10 results of "hop beer" query and request highlighting
174
+ # cluster.search_query("beer_index", Cluster::SearchQuery.match_phrase("hop beer"),
175
+ # Options::Search(
176
+ # limit: 10,
177
+ # fields: %w[name],
178
+ # highlight_style: :html,
179
+ # highlight_fields: %w[name description]
180
+ # ))
181
+ #
182
+ # @return [SearchResult]
183
+ def search_query(index_name, query, options = Options::Search::DEFAULT)
184
+ resp = @backend.document_search(index_name, JSON.generate(query), options.to_backend)
185
+
186
+ SearchResult.new do |res|
187
+ res.meta_data = SearchMetaData.new do |meta|
188
+ meta.metrics.max_score = resp[:meta_data][:metrics][:max_score]
189
+ meta.metrics.error_partition_count = resp[:meta_data][:metrics][:error_partition_count]
190
+ meta.metrics.success_partition_count = resp[:meta_data][:metrics][:success_partition_count]
191
+ meta.metrics.took = resp[:meta_data][:metrics][:took]
192
+ meta.metrics.total_rows = resp[:meta_data][:metrics][:total_rows]
193
+ meta.errors = resp[:meta_data][:errors]
194
+ end
195
+ res.rows = resp[:rows].map do |r|
196
+ SearchRow.new do |row|
197
+ row.transcoder = options.transcoder
198
+ row.index = r[:index]
199
+ row.id = r[:id]
200
+ row.score = r[:score]
201
+ row.fragments = r[:fragments]
202
+ unless r[:locations].empty?
203
+ row.locations = SearchRowLocations.new(
204
+ r[:locations].map do |loc|
205
+ SearchRowLocation.new do |location|
206
+ location.field = loc[:field]
207
+ location.term = loc[:term]
208
+ location.position = loc[:position]
209
+ location.start_offset = loc[:start_offset]
210
+ location.end_offset = loc[:end_offset]
211
+ location.array_positions = loc[:array_positions]
212
+ end
213
+ end
214
+ )
215
+ end
216
+ row.instance_variable_set(:@fields, r[:fields])
217
+ row.explanation = JSON.parse(r[:explanation]) if r[:explanation]
218
+ end
219
+ end
220
+ if resp[:facets]
221
+ res.facets = resp[:facets].each_with_object({}) do |(k, v), o|
222
+ facet = case options.facets[k]
223
+ when SearchFacet::SearchFacetTerm
224
+ SearchFacetResult::TermFacetResult.new do |f|
225
+ f.terms =
226
+ if v[:terms]
227
+ v[:terms].map do |t|
228
+ SearchFacetResult::TermFacetResult::TermFacet.new(t[:term], t[:count])
229
+ end
230
+ else
231
+ []
232
+ end
233
+ end
234
+ when SearchFacet::SearchFacetDateRange
235
+ SearchFacetResult::DateRangeFacetResult.new do |f|
236
+ f.date_ranges =
237
+ if v[:date_ranges]
238
+ v[:date_ranges].map do |r|
239
+ SearchFacetResult::DateRangeFacetResult::DateRangeFacet.new(r[:name], r[:count], r[:start_time], r[:end_time])
240
+ end
241
+ else
242
+ []
243
+ end
244
+ end
245
+ when SearchFacet::SearchFacetNumericRange
246
+ SearchFacetResult::NumericRangeFacetResult.new do |f|
247
+ f.numeric_ranges =
248
+ if v[:numeric_ranges]
249
+ v[:numeric_ranges].map do |r|
250
+ SearchFacetResult::NumericRangeFacetResult::NumericRangeFacet.new(r[:name], r[:count], r[:min], r[:max])
251
+ end
252
+ else
253
+ []
254
+ end
255
+ end
256
+ else
257
+ next # ignore unknown facet result
258
+ end
259
+ facet.name = v[:name]
260
+ facet.field = v[:field]
261
+ facet.total = v[:total]
262
+ facet.missing = v[:missing]
263
+ facet.other = v[:other]
264
+ o[k] = facet
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ # @return [Management::UserManager]
271
+ def users
272
+ Management::UserManager.new(@backend)
273
+ end
274
+
275
+ # @return [Management::BucketManager]
276
+ def buckets
277
+ Management::BucketManager.new(@backend)
278
+ end
279
+
280
+ # @return [Management::QueryIndexManager]
281
+ def query_indexes
282
+ Management::QueryIndexManager.new(@backend)
283
+ end
284
+
285
+ # @return [Management::AnalyticsIndexManager]
286
+ def analytics_indexes
287
+ Management::AnalyticsIndexManager.new(@backend)
288
+ end
289
+
290
+ # @return [Management::SearchIndexManager]
291
+ def search_indexes
292
+ Management::SearchIndexManager.new(@backend)
293
+ end
294
+
295
+ # Closes all connections to services and free allocated resources
296
+ #
297
+ # @return [void]
298
+ def disconnect
299
+ @backend.close
300
+ end
301
+
302
+ # Creates diagnostic report that can be used to determine the health of the network connections.
303
+ #
304
+ # It does not proactively perform any I/O against the network
305
+ #
306
+ # @param [Options::Diagnostics] options
307
+ #
308
+ # @return [DiagnosticsResult]
309
+ def diagnostics(options = Options::Diagnostics::DEFAULT)
310
+ resp = @backend.diagnostics(options.report_id)
311
+ DiagnosticsResult.new do |res|
312
+ res.version = resp[:version]
313
+ res.id = resp[:id]
314
+ res.sdk = resp[:sdk]
315
+ resp[:services].each do |type, svcs|
316
+ res.services[type] = svcs.map do |svc|
317
+ DiagnosticsResult::ServiceInfo.new do |info|
318
+ info.id = svc[:id]
319
+ info.state = svc[:state]
320
+ info.last_activity_us = svc[:last_activity_us]
321
+ info.remote = svc[:remote]
322
+ info.local = svc[:local]
323
+ info.details = svc[:details]
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+
330
+ # Performs application-level ping requests against services in the couchbase cluster
331
+ #
332
+ # @param [Options::Ping] options
333
+ #
334
+ # @return [PingResult]
335
+ def ping(options = Options::Ping::DEFAULT)
336
+ resp = @backend.ping(nil, options.to_backend)
337
+ PingResult.new do |res|
338
+ res.version = resp[:version]
339
+ res.id = resp[:id]
340
+ res.sdk = resp[:sdk]
341
+ resp[:services].each do |type, svcs|
342
+ res.services[type] = svcs.map do |svc|
343
+ PingResult::ServiceInfo.new do |info|
344
+ info.id = svc[:id]
345
+ info.state = svc[:state]
346
+ info.latency = svc[:latency]
347
+ info.remote = svc[:remote]
348
+ info.local = svc[:local]
349
+ info.error = svc[:error]
350
+ end
351
+ end
352
+ end
353
+ end
354
+ end
355
+
356
+ private
357
+
358
+ # Initialize {Cluster} object
359
+ #
360
+ # @overload new(connection_string, options)
361
+ # @param [String] connection_string connection string used to locate the Couchbase Cluster
362
+ # @param [Options::Cluster] options custom options when creating the cluster connection
363
+ #
364
+ # @overload new(connection_string, username, password, options)
365
+ # Shortcut for {PasswordAuthenticator}
366
+ # @param [String] connection_string connection string used to locate the Couchbase Cluster
367
+ # @param [String] username name of the user
368
+ # @param [String] password password of the user
369
+ # @param [Options::Cluster, nil] options custom options when creating the cluster connection
370
+ #
371
+ # @overload new(configuration)
372
+ # @param [Configuration] configuration configuration object
373
+ def initialize(connection_string, *args)
374
+ credentials = {}
375
+ open_options = {}
376
+
377
+ if connection_string.is_a?(Configuration)
378
+ options = connection_string
379
+ connection_string = options.connection_string
380
+ credentials[:username] = options.username
381
+ credentials[:password] = options.password
382
+ raise ArgumentError, "missing connection_string" unless connection_string
383
+ raise ArgumentError, "missing username" unless credentials[:username]
384
+ raise ArgumentError, "missing password" unless credentials[:password]
385
+
386
+ open_options[:allowed_sasl_mechanisms] = PasswordAuthenticator::DEFAULT_SASL_MECHANISMS
387
+ else
388
+ options = args.shift
389
+ case options
390
+ when String
391
+ credentials[:username] = options
392
+ credentials[:password] = args.shift
393
+ raise ArgumentError, "missing username" unless credentials[:username]
394
+ raise ArgumentError, "missing password" unless credentials[:password]
395
+
396
+ open_options[:allowed_sasl_mechanisms] = PasswordAuthenticator::DEFAULT_SASL_MECHANISMS
397
+ when Options::Cluster
398
+ open_options = options&.to_backend || {}
399
+ authenticator = options&.authenticator
400
+ case authenticator
401
+ when PasswordAuthenticator
402
+ credentials[:username] = authenticator&.username
403
+ raise ArgumentError, "missing username" unless credentials[:username]
404
+
405
+ credentials[:password] = authenticator&.password
406
+ raise ArgumentError, "missing password" unless credentials[:password]
407
+
408
+ open_options[:allowed_sasl_mechanisms] = authenticator&.allowed_sasl_mechanisms
409
+ when CertificateAuthenticator
410
+ credentials[:certificate_path] = authenticator&.certificate_path
411
+ raise ArgumentError, "missing certificate path" unless credentials[:certificate_path]
412
+
413
+ credentials[:key_path] = authenticator&.key_path
414
+ raise ArgumentError, "missing key path" unless credentials[:key_path]
415
+
416
+ else
417
+ raise ArgumentError, "options must have authenticator configured"
418
+ end
419
+ else
420
+ raise ArgumentError, "unexpected second argument, have to be String or ClusterOptions"
421
+ end
422
+ end
423
+
424
+ @backend = Backend.new
425
+ @backend.open(connection_string, credentials, open_options)
426
+ end
427
+
428
+ # @api private
429
+ ClusterOptions = ::Couchbase::Options::Cluster
430
+ # @api private
431
+ DiagnosticsOptions = ::Couchbase::Options::Diagnostics
432
+ # @api private
433
+ AnalyticsOptions = ::Couchbase::Options::Analytics
434
+ # @api private
435
+ QueryOptions = ::Couchbase::Options::Query
436
+ # @api private
437
+ SearchOptions = ::Couchbase::Options::Search
438
+ end
439
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2023 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 "singleton"
18
+
19
+ require "couchbase/errors"
20
+
21
+ module Couchbase
22
+ class ClusterRegistry
23
+ include Singleton
24
+
25
+ def initialize
26
+ @handlers = {}
27
+ end
28
+
29
+ def connect(connection_string, *options)
30
+ @handlers.each do |regexp, cluster_class|
31
+ return cluster_class.connect(connection_string, *options) if regexp.match?(connection_string)
32
+ end
33
+ raise(Error::FeatureNotAvailable, "Connection string '#{connection_string}' not supported.")
34
+ end
35
+
36
+ def register_connection_handler(regexp, cluster_class)
37
+ @handlers[regexp] = cluster_class
38
+ end
39
+
40
+ def deregister_connection_handler(regexp)
41
+ @handlers.delete(regexp)
42
+ end
43
+ end
44
+ end