typesense 0.1.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,12 +4,21 @@ module Typesense
4
4
  class Client
5
5
  attr_reader :configuration
6
6
  attr_reader :collections
7
+ attr_reader :aliases
8
+ attr_reader :keys
7
9
  attr_reader :debug
10
+ attr_reader :health
11
+ attr_reader :metrics
8
12
 
9
13
  def initialize(options = {})
10
- @configuration ||= Configuration.new(options)
11
- @collections = Collections.new(@configuration)
12
- @debug = Debug.new(@configuration)
14
+ @configuration = Configuration.new(options)
15
+ @api_call = ApiCall.new(@configuration)
16
+ @collections = Collections.new(@api_call)
17
+ @aliases = Aliases.new(@api_call)
18
+ @keys = Keys.new(@api_call)
19
+ @debug = Debug.new(@api_call)
20
+ @health = Health.new(@api_call)
21
+ @metrics = Metrics.new(@api_call)
13
22
  end
14
23
  end
15
24
  end
@@ -3,19 +3,21 @@
3
3
  module Typesense
4
4
  class Collection
5
5
  attr_reader :documents
6
+ attr_reader :overrides
6
7
 
7
- def initialize(configuration, name)
8
- @configuration = configuration
9
- @name = name
10
- @documents = Documents.new(@configuration, @name)
8
+ def initialize(name, api_call)
9
+ @name = name
10
+ @api_call = api_call
11
+ @documents = Documents.new(@name, @api_call)
12
+ @overrides = Overrides.new(@name, @api_call)
11
13
  end
12
14
 
13
15
  def retrieve
14
- ApiCall.new(@configuration).get(endpoint_path)
16
+ @api_call.get(endpoint_path)
15
17
  end
16
18
 
17
19
  def delete
18
- ApiCall.new(@configuration).delete(endpoint_path)
20
+ @api_call.delete(endpoint_path)
19
21
  end
20
22
 
21
23
  private
@@ -4,21 +4,21 @@ module Typesense
4
4
  class Collections
5
5
  RESOURCE_PATH = '/collections'
6
6
 
7
- def initialize(configuration)
8
- @configuration = configuration
9
- @collections = {}
7
+ def initialize(api_call)
8
+ @api_call = api_call
9
+ @collections = {}
10
10
  end
11
11
 
12
12
  def create(schema)
13
- ApiCall.new(@configuration).post(RESOURCE_PATH, schema)
13
+ @api_call.post(RESOURCE_PATH, schema)
14
14
  end
15
15
 
16
16
  def retrieve
17
- ApiCall.new(@configuration).get(RESOURCE_PATH)
17
+ @api_call.get(RESOURCE_PATH)
18
18
  end
19
19
 
20
20
  def [](collection_name)
21
- @collections[collection_name] ||= Collection.new(@configuration, collection_name)
21
+ @collections[collection_name] ||= Collection.new(collection_name, @api_call)
22
22
  end
23
23
  end
24
24
  end
@@ -2,37 +2,53 @@
2
2
 
3
3
  module Typesense
4
4
  class Configuration
5
- attr_accessor :master_node
6
- attr_accessor :read_replica_nodes
7
- attr_accessor :timeout_seconds
5
+ attr_accessor :nodes
6
+ attr_accessor :nearest_node
7
+ attr_accessor :connection_timeout_seconds
8
+ attr_accessor :healthcheck_interval_seconds
9
+ attr_accessor :num_retries
10
+ attr_accessor :retry_interval_seconds
11
+ attr_accessor :api_key
12
+ attr_accessor :logger
13
+ attr_accessor :log_level
8
14
 
9
15
  def initialize(options = {})
10
- @master_node = options[:master_node] || {
11
- host: 'localhost',
12
- port: '8108',
13
- protocol: 'http'
14
- }
15
-
16
- @read_replica_nodes = options[:read_replica_nodes] || []
17
- @timeout_seconds = options[:timeout_seconds] || 10
16
+ @nodes = options[:nodes] || []
17
+ @nearest_node = options[:nearest_node]
18
+ @connection_timeout_seconds = options[:connection_timeout_seconds] || options[:timeout_seconds] || 10
19
+ @healthcheck_interval_seconds = options[:healthcheck_interval_seconds] || 15
20
+ @num_retries = options[:num_retries] || @nodes.length + (@nearest_node.nil? ? 0 : 1) || 3
21
+ @retry_interval_seconds = options[:retry_interval_seconds] || 0.1
22
+ @api_key = options[:api_key]
23
+
24
+ @logger = options[:logger] || Logger.new(STDOUT)
25
+ @log_level = options[:log_level] || Logger::WARN
26
+ @logger.level = @log_level
27
+
28
+ show_deprecation_warnings(options)
29
+ validate!
18
30
  end
19
31
 
20
32
  def validate!
21
- if @master_node.nil? ||
22
- node_missing_parameters?(@master_node)
23
- raise Error::MissingConfiguration, 'Missing required configuration. Ensure that master_node[:protocol], master_node[:host], master_node[:port] and master_node[:api_key] are set.'
33
+ if @nodes.nil? ||
34
+ @nodes.empty? ||
35
+ @nodes.any? { |node| node_missing_parameters?(node) }
36
+ raise Error::MissingConfiguration, 'Missing required configuration. Ensure that nodes[][:protocol], nodes[][:host] and nodes[][:port] are set.'
24
37
  end
25
38
 
26
- if !@read_replica_nodes.nil? &&
27
- @read_replica_nodes.any? { |node| node_missing_parameters?(node) }
28
- raise Error::MissingConfiguration, 'Missing required configuration for read_replica_nodes. Ensure that read_replica_nodes[][:protocol], read_replica_nodes[][:host], read_replica_nodes[][:port] and read_replica_nodes[][:api_key] are set.'
29
- end
39
+ raise Error::MissingConfiguration, 'Missing required configuration. Ensure that api_key is set.' if @api_key.nil?
30
40
  end
31
41
 
32
42
  private
33
43
 
34
44
  def node_missing_parameters?(node)
35
- %i[protocol host port api_key].any? { |attr| node.send(:[], attr).nil? }
45
+ %i[protocol host port].any? { |attr| node.send(:[], attr).nil? }
46
+ end
47
+
48
+ def show_deprecation_warnings(options)
49
+ @logger.warn 'Deprecation warning: timeout_seconds is now renamed to connection_timeout_seconds' unless options[:timeout_seconds].nil?
50
+ @logger.warn 'Deprecation warning: master_node is now consolidated to nodes, starting with Typesense Server v0.12' unless options[:master_node].nil?
51
+ @logger.warn 'Deprecation warning: read_replica_nodes is now consolidated to nodes, starting with Typesense Server v0.12' unless options[:read_replica_nodes].nil?
36
52
  end
37
53
  end
38
54
  end
@@ -4,12 +4,12 @@ module Typesense
4
4
  class Debug
5
5
  RESOURCE_PATH = '/debug'
6
6
 
7
- def initialize(configuration)
8
- @configuration = configuration
7
+ def initialize(api_call)
8
+ @api_call = api_call
9
9
  end
10
10
 
11
11
  def retrieve
12
- ApiCall.new(@configuration).get(RESOURCE_PATH)
12
+ @api_call.get(RESOURCE_PATH)
13
13
  end
14
14
  end
15
15
  end
@@ -2,18 +2,18 @@
2
2
 
3
3
  module Typesense
4
4
  class Document
5
- def initialize(configuration, collection_name, document_id)
6
- @configuration = configuration
5
+ def initialize(collection_name, document_id, api_call)
7
6
  @collection_name = collection_name
8
7
  @document_id = document_id
8
+ @api_call = api_call
9
9
  end
10
10
 
11
11
  def retrieve
12
- ApiCall.new(@configuration).get(endpoint_path)
12
+ @api_call.get(endpoint_path)
13
13
  end
14
14
 
15
15
  def delete
16
- ApiCall.new(@configuration).delete(endpoint_path)
16
+ @api_call.delete(endpoint_path)
17
17
  end
18
18
 
19
19
  private
@@ -4,26 +4,31 @@ module Typesense
4
4
  class Documents
5
5
  RESOURCE_PATH = '/documents'
6
6
 
7
- def initialize(configuration, collection_name)
8
- @configuration = configuration
7
+ def initialize(collection_name, api_call)
9
8
  @collection_name = collection_name
9
+ @api_call = api_call
10
10
  @documents = {}
11
11
  end
12
12
 
13
13
  def create(document)
14
- ApiCall.new(@configuration).post(endpoint_path, document)
14
+ @api_call.post(endpoint_path, document)
15
+ end
16
+
17
+ def create_many(documents)
18
+ documents_in_jsonl_format = documents.map { |document| JSON.dump(document) }.join("\n")
19
+ @api_call.post(endpoint_path('import'), as_json: false, body: documents_in_jsonl_format)
15
20
  end
16
21
 
17
22
  def export
18
- ApiCall.new(@configuration).get_unparsed_response(endpoint_path('export')).split("\n")
23
+ (@api_call.get(endpoint_path('export')) || '').split("\n")
19
24
  end
20
25
 
21
26
  def search(search_parameters)
22
- ApiCall.new(@configuration).get(endpoint_path('search'), search_parameters)
27
+ @api_call.get(endpoint_path('search'), search_parameters)
23
28
  end
24
29
 
25
30
  def [](document_id)
26
- @documents[document_id] ||= Document.new(@configuration, @collection_name, document_id)
31
+ @documents[document_id] ||= Document.new(@collection_name, document_id, @api_call)
27
32
  end
28
33
 
29
34
  private
@@ -23,7 +23,13 @@ module Typesense
23
23
  class ServerError < Error
24
24
  end
25
25
 
26
+ class HTTPStatus0Error < Error
27
+ end
28
+
26
29
  class NoMethodError < ::NoMethodError
27
30
  end
31
+
32
+ class HTTPError < Error
33
+ end
28
34
  end
29
35
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Typesense
4
+ class Health
5
+ RESOURCE_PATH = '/health'
6
+
7
+ def initialize(api_call)
8
+ @api_call = api_call
9
+ end
10
+
11
+ def retrieve
12
+ @api_call.get(RESOURCE_PATH)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Typesense
4
+ class Key
5
+ def initialize(id, api_call)
6
+ @id = id
7
+ @api_call = api_call
8
+ end
9
+
10
+ def retrieve
11
+ @api_call.get(endpoint_path)
12
+ end
13
+
14
+ def delete
15
+ @api_call.delete(endpoint_path)
16
+ end
17
+
18
+ private
19
+
20
+ def endpoint_path
21
+ "#{Keys::RESOURCE_PATH}/#{@id}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Typesense
6
+ class Keys
7
+ RESOURCE_PATH = '/keys'
8
+
9
+ def initialize(api_call)
10
+ @api_call = api_call
11
+ @keys = {}
12
+ end
13
+
14
+ def create(parameters)
15
+ @api_call.post(RESOURCE_PATH, parameters)
16
+ end
17
+
18
+ def retrieve
19
+ @api_call.get(RESOURCE_PATH)
20
+ end
21
+
22
+ def generate_scoped_search_key(search_key, parameters)
23
+ parameters_json = JSON.dump(parameters)
24
+ digest = Base64.encode64(OpenSSL::HMAC.digest('sha256', search_key, parameters_json)).gsub("\n", '')
25
+ key_prefix = search_key[0...4]
26
+ raw_scoped_key = "#{digest}#{key_prefix}#{parameters_json}"
27
+ Base64.encode64(raw_scoped_key).gsub("\n", '')
28
+ end
29
+
30
+ def [](id)
31
+ @keys[id] ||= Key.new(id, @api_call)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Typesense
4
+ class Metrics
5
+ RESOURCE_PATH = '/metrics.json'
6
+
7
+ def initialize(api_call)
8
+ @api_call = api_call
9
+ end
10
+
11
+ def retrieve
12
+ @api_call.get(RESOURCE_PATH)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Typesense
4
+ class Override
5
+ def initialize(collection_name, override_id, api_call)
6
+ @collection_name = collection_name
7
+ @override_id = override_id
8
+ @api_call = api_call
9
+ end
10
+
11
+ def retrieve
12
+ @api_call.get(endpoint_path)
13
+ end
14
+
15
+ def delete
16
+ @api_call.delete(endpoint_path)
17
+ end
18
+
19
+ private
20
+
21
+ def endpoint_path
22
+ "#{Collections::RESOURCE_PATH}/#{@collection_name}#{Overrides::RESOURCE_PATH}/#{@override_id}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Typesense
4
+ class Overrides
5
+ RESOURCE_PATH = '/overrides'
6
+
7
+ def initialize(collection_name, api_call)
8
+ @collection_name = collection_name
9
+ @api_call = api_call
10
+ @overrides = {}
11
+ end
12
+
13
+ def create(params)
14
+ @api_call.put(endpoint_path, params)
15
+ end
16
+
17
+ def retrieve
18
+ @api_call.get(endpoint_path)
19
+ end
20
+
21
+ def [](override_id)
22
+ @overrides[override_id] ||= Override.new(@collection_name, override_id, @api_call)
23
+ end
24
+
25
+ private
26
+
27
+ def endpoint_path(operation = nil)
28
+ "#{Collections::RESOURCE_PATH}/#{@collection_name}#{Overrides::RESOURCE_PATH}#{operation.nil? ? '' : '/' + operation}"
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Typesense
4
- VERSION = '0.1.1'
4
+ VERSION = '0.5.2'
5
5
  end
@@ -7,7 +7,7 @@ require 'typesense/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'typesense'
9
9
  spec.version = Typesense::VERSION
10
- spec.authors = ['Wreally Studios']
10
+ spec.authors = ['Typesense, Inc.']
11
11
  spec.email = ['contact@typesense.org']
12
12
 
13
13
  spec.summary = 'Ruby Library for Typesense'
@@ -23,15 +23,18 @@ Gem::Specification.new do |spec|
23
23
  spec.require_paths = ['lib']
24
24
 
25
25
  spec.add_development_dependency 'awesome_print', '~> 1.8'
26
- spec.add_development_dependency 'bundler', '~> 1.16'
27
- spec.add_development_dependency 'pry-byebug', '~> 3.5'
28
- spec.add_development_dependency 'rake', '~> 10.0'
29
- spec.add_development_dependency 'rspec', '~> 3.0'
30
- spec.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
31
- spec.add_development_dependency 'rubocop', '~> 0.53.0'
32
- spec.add_development_dependency 'rubocop-rspec', '~> 1.24'
33
- spec.add_development_dependency 'simplecov', '~> 0.15'
34
- spec.add_development_dependency 'webmock', '~> 3.2'
26
+ spec.add_development_dependency 'bundler', '~> 2.0'
27
+ spec.add_development_dependency 'codecov', '~> 0.1'
28
+ spec.add_development_dependency 'pry-byebug', '~> 3.9'
29
+ spec.add_development_dependency 'rake', '~> 13.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.9'
31
+ spec.add_development_dependency 'rspec-legacy_formatters', '~> 1.0' # For codecov formatter
32
+ spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4'
33
+ spec.add_development_dependency 'rubocop', '~> 0.83'
34
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.39'
35
+ spec.add_development_dependency 'simplecov', '~> 0.18'
36
+ spec.add_development_dependency 'timecop', '~> 0.9'
37
+ spec.add_development_dependency 'webmock', '~> 3.8'
35
38
 
36
- spec.add_dependency 'httparty', '~> 0.15'
39
+ spec.add_dependency 'httparty', '~> 0.18'
37
40
  end