algolia 2.0.0.pre.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +146 -0
  3. data/.github/ISSUE_TEMPLATE.md +20 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  5. data/.gitignore +38 -0
  6. data/.rubocop.yml +186 -0
  7. data/.rubocop_todo.yml +14 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +18 -0
  10. data/LICENSE +21 -0
  11. data/README.md +56 -0
  12. data/Rakefile +45 -0
  13. data/Steepfile +6 -0
  14. data/algolia.gemspec +41 -0
  15. data/bin/console +21 -0
  16. data/bin/setup +8 -0
  17. data/lib/algolia.rb +42 -0
  18. data/lib/algolia/account_client.rb +65 -0
  19. data/lib/algolia/analytics_client.rb +105 -0
  20. data/lib/algolia/config/algolia_config.rb +40 -0
  21. data/lib/algolia/config/analytics_config.rb +20 -0
  22. data/lib/algolia/config/insights_config.rb +20 -0
  23. data/lib/algolia/config/recommendation_config.rb +20 -0
  24. data/lib/algolia/config/search_config.rb +40 -0
  25. data/lib/algolia/defaults.rb +35 -0
  26. data/lib/algolia/enums/call_type.rb +4 -0
  27. data/lib/algolia/enums/retry_outcome_type.rb +5 -0
  28. data/lib/algolia/error.rb +29 -0
  29. data/lib/algolia/helpers.rb +83 -0
  30. data/lib/algolia/http/http_requester.rb +84 -0
  31. data/lib/algolia/http/response.rb +23 -0
  32. data/lib/algolia/insights_client.rb +238 -0
  33. data/lib/algolia/iterators/base_iterator.rb +19 -0
  34. data/lib/algolia/iterators/object_iterator.rb +27 -0
  35. data/lib/algolia/iterators/paginator_iterator.rb +44 -0
  36. data/lib/algolia/iterators/rule_iterator.rb +9 -0
  37. data/lib/algolia/iterators/synonym_iterator.rb +9 -0
  38. data/lib/algolia/logger_helper.rb +14 -0
  39. data/lib/algolia/recommendation_client.rb +60 -0
  40. data/lib/algolia/responses/add_api_key_response.rb +38 -0
  41. data/lib/algolia/responses/base_response.rb +9 -0
  42. data/lib/algolia/responses/delete_api_key_response.rb +40 -0
  43. data/lib/algolia/responses/indexing_response.rb +28 -0
  44. data/lib/algolia/responses/multiple_batch_indexing_response.rb +29 -0
  45. data/lib/algolia/responses/multiple_response.rb +45 -0
  46. data/lib/algolia/responses/restore_api_key_response.rb +36 -0
  47. data/lib/algolia/responses/update_api_key_response.rb +39 -0
  48. data/lib/algolia/search_client.rb +614 -0
  49. data/lib/algolia/search_index.rb +1094 -0
  50. data/lib/algolia/transport/request_options.rb +94 -0
  51. data/lib/algolia/transport/retry_strategy.rb +117 -0
  52. data/lib/algolia/transport/stateful_host.rb +26 -0
  53. data/lib/algolia/transport/transport.rb +161 -0
  54. data/lib/algolia/user_agent.rb +25 -0
  55. data/lib/algolia/version.rb +3 -0
  56. data/sig/config/algolia_config.rbs +24 -0
  57. data/sig/config/analytics_config.rbs +11 -0
  58. data/sig/config/insights_config.rbs +11 -0
  59. data/sig/config/recommendation_config.rbs +11 -0
  60. data/sig/config/search_config.rbs +11 -0
  61. data/sig/enums/call_type.rbs +5 -0
  62. data/sig/helpers.rbs +12 -0
  63. data/sig/http/http_requester.rbs +17 -0
  64. data/sig/http/response.rbs +14 -0
  65. data/sig/interfaces/_connection.rbs +16 -0
  66. data/sig/iterators/base_iterator.rbs +15 -0
  67. data/sig/iterators/object_iterator.rbs +6 -0
  68. data/sig/iterators/paginator_iterator.rbs +8 -0
  69. data/sig/iterators/rule_iterator.rbs +5 -0
  70. data/sig/iterators/synonym_iterator.rbs +5 -0
  71. data/sig/transport/request_options.rbs +33 -0
  72. data/sig/transport/stateful_host.rbs +21 -0
  73. data/test/algolia/integration/account_client_test.rb +47 -0
  74. data/test/algolia/integration/analytics_client_test.rb +113 -0
  75. data/test/algolia/integration/base_test.rb +9 -0
  76. data/test/algolia/integration/insights_client_test.rb +80 -0
  77. data/test/algolia/integration/mocks/mock_requester.rb +45 -0
  78. data/test/algolia/integration/recommendation_client_test.rb +30 -0
  79. data/test/algolia/integration/search_client_test.rb +361 -0
  80. data/test/algolia/integration/search_index_test.rb +698 -0
  81. data/test/algolia/unit/helpers_test.rb +69 -0
  82. data/test/algolia/unit/retry_strategy_test.rb +139 -0
  83. data/test/algolia/unit/user_agent_test.rb +16 -0
  84. data/test/test_helper.rb +89 -0
  85. data/upgrade_guide.md +595 -0
  86. metadata +307 -0
@@ -0,0 +1,69 @@
1
+ require 'algolia'
2
+ require 'test_helper'
3
+
4
+ class HelpersTest
5
+ include Helpers
6
+
7
+ describe 'test helpers' do
8
+ def test_deserialize_settings
9
+ old_settings = {
10
+ attributesToIndex: %w(attr1 attr2),
11
+ numericAttributesToIndex: %w(attr1 attr2),
12
+ slaves: %w(index1 index2)
13
+ }
14
+
15
+ new_settings = {
16
+ searchableAttributes: %w(attr1 attr2),
17
+ numericAttributesForFiltering: %w(attr1 attr2),
18
+ replicas: %w(index1 index2)
19
+ }
20
+
21
+ deserialized_settings = deserialize_settings(old_settings)
22
+ assert_equal new_settings, deserialized_settings
23
+ end
24
+ end
25
+
26
+ describe 'test hash_includes_subset' do
27
+ def test_empty_hashes
28
+ h = {}
29
+ subset = {}
30
+ assert hash_includes_subset?(h, subset)
31
+ end
32
+
33
+ def test_with_empty_subset
34
+ h = { a: 100, b: 200 }
35
+ subset = {}
36
+ assert hash_includes_subset?(h, subset)
37
+ end
38
+
39
+ def test_subset_included
40
+ h = { a: 100, b: 200 }
41
+ subset = { a: 100 }
42
+ assert hash_includes_subset?(h, subset)
43
+ end
44
+
45
+ def test_subset_not_included
46
+ h = { a: 100, b: 200 }
47
+ subset = { c: 300 }
48
+ refute hash_includes_subset?(h, subset)
49
+ end
50
+
51
+ def test_subset_included_but_wrong_value
52
+ h = { a: 100, b: 200 }
53
+ subset = { a: 200 }
54
+ refute hash_includes_subset?(h, subset)
55
+ end
56
+
57
+ def test_subset_included_with_multiple_values
58
+ h = { a: 100, b: 200, c: 300 }
59
+ subset = { a: 100, b: 200 }
60
+ assert hash_includes_subset?(h, subset)
61
+ end
62
+
63
+ def test_subset_not_included_because_too_many_values
64
+ h = { a: 100, b: 200 }
65
+ subset = { a: 100, b: 200, c: 300 }
66
+ refute hash_includes_subset?(h, subset)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,139 @@
1
+ require 'algolia'
2
+ require 'test_helper'
3
+
4
+ class RetryStrategyTest
5
+ include RetryOutcomeType
6
+ include CallType
7
+ describe 'get tryable hosts' do
8
+ def before_all
9
+ super
10
+ @app_id = 'app_id'
11
+ @api_key = 'api_key'
12
+ stateful_hosts = []
13
+ stateful_hosts << "#{@app_id}-4.algolianet.com"
14
+ stateful_hosts << "#{@app_id}-5.algolianet.com"
15
+ stateful_hosts << "#{@app_id}-6.algolianet.com"
16
+ @config = Algolia::Search::Config.new(app_id: @app_id, api_key: @api_key, custom_hosts: stateful_hosts)
17
+ end
18
+
19
+ def test_resets_expired_hosts_according_to_read_type
20
+ @config.default_hosts[1].up = false
21
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
22
+
23
+ hosts = retry_strategy.get_tryable_hosts(READ)
24
+ assert_equal 2, hosts.length
25
+ end
26
+
27
+ def test_resets_expired_hosts_according_to_write_type
28
+ @config.default_hosts[1].up = false
29
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
30
+
31
+ hosts = retry_strategy.get_tryable_hosts(WRITE)
32
+ assert_equal 2, hosts.length
33
+ end
34
+
35
+ def test_resets_expired_hosts_according_to_read_type_with_timeout
36
+ @config.default_hosts[1].up = false
37
+ @config.default_hosts[1].last_use = Time.new.utc - 1000
38
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
39
+
40
+ hosts = retry_strategy.get_tryable_hosts(READ)
41
+ assert_equal 3, hosts.length
42
+ end
43
+
44
+ def test_resets_expired_hosts_according_to_write_type_with_timeout
45
+ @config.default_hosts[1].up = false
46
+ @config.default_hosts[1].last_use = Time.new.utc - 1000
47
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
48
+
49
+ hosts = retry_strategy.get_tryable_hosts(WRITE)
50
+ assert_equal 3, hosts.length
51
+ end
52
+
53
+ def test_resets_all_hosts_when_expired_according_to_read_type
54
+ 0.upto(2).map do |i|
55
+ @config.default_hosts[i].up = false
56
+ end
57
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
58
+
59
+ hosts = retry_strategy.get_tryable_hosts(READ)
60
+ assert_equal 3, hosts.length
61
+ end
62
+
63
+ def test_resets_all_hosts_when_expired_according_to_write_type
64
+ 0.upto(2).map do |i|
65
+ @config.default_hosts[i].up = false
66
+ end
67
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
68
+
69
+ hosts = retry_strategy.get_tryable_hosts(WRITE)
70
+ assert_equal 3, hosts.length
71
+ end
72
+ end
73
+
74
+ describe 'All hosts are unreachable' do
75
+ def test_failure_when_all_hosts_are_down
76
+ stateful_hosts = ['0.0.0.0']
77
+ @config = Algolia::Search::Config.new(app_id: 'foo', api_key: 'bar', custom_hosts: stateful_hosts)
78
+ client = Algolia::Search::Client.create_with_config(@config)
79
+ index = client.init_index(get_test_index_name('failure'))
80
+
81
+ exception = assert_raises Algolia::AlgoliaUnreachableHostError do
82
+ index.save_object({ objectID: 'one' })
83
+ end
84
+
85
+ assert_equal 'Unreachable hosts', exception.message
86
+ end
87
+ end
88
+
89
+ describe 'retry stategy decisions' do
90
+ def before_all
91
+ super
92
+ @app_id = 'app_id'
93
+ @api_key = 'api_key'
94
+ @config = Algolia::Search::Config.new(app_id: @app_id, api_key: @api_key)
95
+ @retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
96
+ @hosts = @retry_strategy.get_tryable_hosts(READ|WRITE)
97
+ end
98
+
99
+ def test_retry_decision_on_300
100
+ decision = @retry_strategy.decide(@hosts.first, http_response_code: 300)
101
+ assert_equal RETRY, decision
102
+ end
103
+
104
+ def test_retry_decision_on_500
105
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
106
+
107
+ decision = retry_strategy.decide(@hosts.first, http_response_code: 500)
108
+ assert_equal RETRY, decision
109
+ end
110
+
111
+ def test_retry_decision_on_timed_out
112
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
113
+
114
+ decision = retry_strategy.decide(@hosts.first, is_timed_out: true)
115
+ assert_equal RETRY, decision
116
+ end
117
+
118
+ def test_retry_decision_on_400
119
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
120
+
121
+ decision = retry_strategy.decide(@hosts.first, http_response_code: 400)
122
+ assert_equal FAILURE, decision
123
+ end
124
+
125
+ def test_retry_decision_on_404
126
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
127
+
128
+ decision = retry_strategy.decide(@hosts.first, http_response_code: 404)
129
+ assert_equal FAILURE, decision
130
+ end
131
+
132
+ def test_retry_decision_on_200
133
+ retry_strategy = Algolia::Transport::RetryStrategy.new(@config)
134
+
135
+ decision = retry_strategy.decide(@hosts.first, http_response_code: 200)
136
+ assert_equal SUCCESS, decision
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,16 @@
1
+ require 'algolia'
2
+ require 'test_helper'
3
+
4
+ class UserAgentTest
5
+ describe 'define user agent' do
6
+ def before_all
7
+ @default = "Algolia for Ruby (#{Algolia::VERSION}), Ruby (#{RUBY_VERSION})"
8
+ end
9
+
10
+ def test_add_user_agents
11
+ Algolia::UserAgent.add('Foo Bar', 'v1.0')
12
+ Algolia::UserAgent.add('Front Web', '2.0')
13
+ assert_equal(format('%<default>s; Foo Bar (v1.0); Front Web (2.0)', default: @default), Algolia::UserAgent.value)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,89 @@
1
+ require 'simplecov'
2
+
3
+ if ENV['COVERAGE']
4
+ SimpleCov.start
5
+ end
6
+
7
+ require 'bundler/setup'
8
+ require 'algolia'
9
+ require 'minitest/autorun'
10
+ require 'minitest/hooks'
11
+ require 'algolia/integration/mocks/mock_requester'
12
+
13
+ APPLICATION_ID_1 = ENV['ALGOLIA_APPLICATION_ID_1']
14
+ ADMIN_KEY_1 = ENV['ALGOLIA_ADMIN_KEY_1']
15
+ SEARCH_KEY_1 = ENV['ALGOLIA_SEARCH_KEY_1']
16
+ APPLICATION_ID_2 = ENV['ALGOLIA_APPLICATION_ID_2']
17
+ ADMIN_KEY_2 = ENV['ALGOLIA_ADMIN_KEY_2']
18
+ MCM_APPLICATION_ID = ENV['ALGOLIA_APPLICATION_ID_MCM']
19
+ MCM_ADMIN_KEY = ENV['ALGOLIA_ADMIN_KEY_MCM']
20
+ USER_AGENT = 'test-ruby'
21
+
22
+ class Minitest::Test
23
+ attr_reader :search_client
24
+
25
+ include Minitest::Hooks
26
+ include Helpers
27
+ @@search_config = Algolia::Search::Config.new(app_id: APPLICATION_ID_1, api_key: ADMIN_KEY_1, user_agent: USER_AGENT)
28
+ @@search_client = Algolia::Search::Client.new(@@search_config)
29
+ end
30
+
31
+ def check_environment_variables
32
+ raise Algolia::AlgoliaError, 'ALGOLIA_APPLICATION_ID_1 must be defined' if ENV['ALGOLIA_APPLICATION_ID_1'].to_s.strip.empty?
33
+ raise Algolia::AlgoliaError, 'ALGOLIA_ADMIN_KEY_1 must be defined' if ENV['ALGOLIA_ADMIN_KEY_1'].to_s.strip.empty?
34
+ raise Algolia::AlgoliaError, 'ALGOLIA_SEARCH_KEY_1 must be defined' if ENV['ALGOLIA_SEARCH_KEY_1'].to_s.strip.empty?
35
+ raise Algolia::AlgoliaError, 'ALGOLIA_APPLICATION_ID_2 must be defined' if ENV['ALGOLIA_APPLICATION_ID_2'].to_s.strip.empty?
36
+ raise Algolia::AlgoliaError, 'ALGOLIA_ADMIN_KEY_2 must be defined' if ENV['ALGOLIA_ADMIN_KEY_2'].to_s.strip.empty?
37
+ raise Algolia::AlgoliaError, 'ALGOLIA_APPLICATION_ID_MCM must be defined' if ENV['ALGOLIA_APPLICATION_ID_MCM'].to_s.strip.empty?
38
+ raise Algolia::AlgoliaError, 'ALGOLIA_ADMIN_KEY_MCM must be defined' if ENV['ALGOLIA_ADMIN_KEY_MCM'].to_s.strip.empty?
39
+ end
40
+
41
+ def get_test_index_name(name)
42
+ date = DateTime.now.strftime('%Y-%m-%d_%H_%M_%S')
43
+ user = ENV['USER'] || 'unknown'
44
+
45
+ instance = ENV['CI'].to_s == 'true' ? ENV['CIRCLE_BUILD_NUM'] : user
46
+
47
+ format('ruby_%<date>s_%<instance>s_%<name>s', date: date, instance: instance, name: name)
48
+ end
49
+
50
+ def get_mcm_user_name(user_id)
51
+ date = DateTime.now.strftime('%Y-%m-%d')
52
+ user = ENV['USER'] || 'unknown'
53
+
54
+ instance = ENV['CI'].to_s == 'true' ? ENV['CIRCLE_BUILD_NUM'] : user
55
+
56
+ format('ruby-%<date>s-%<instance>s-%<user_id>s', date: date, instance: instance, user_id: user_id)
57
+ end
58
+
59
+ def generate_object(object_id = nil)
60
+ object = { property: 'property' }
61
+ if object_id
62
+ object[:objectID] = object_id
63
+ end
64
+
65
+ object
66
+ end
67
+
68
+ def create_employee_records
69
+ [
70
+ { company: 'Algolia', name: 'Julien Lemoine', objectID: 'julien-lemoine' },
71
+ { company: 'Algolia', name: 'Nicolas Dessaigne', objectID: 'nicolas-dessaigne' },
72
+ { company: 'Amazon', name: 'Jeff Bezos' },
73
+ { company: 'Apple', name: 'Steve Jobs' },
74
+ { company: 'Apple', name: 'Steve Wozniak' },
75
+ { company: 'Arista Networks', name: 'Jayshree Ullal' },
76
+ { company: 'Google', name: 'Larry Page' },
77
+ { company: 'Google', name: 'Rob Pike' },
78
+ { company: 'Google', name: 'Serguey Brin' },
79
+ { company: 'Microsoft', name: 'Bill Gates' },
80
+ { company: 'SpaceX', name: 'Elon Musk' },
81
+ { company: 'Tesla', name: 'Elon Musk' },
82
+ { company: 'Yahoo', name: 'Marissa Mayer' }
83
+ ]
84
+ end
85
+
86
+ def rule_without_metadata(rule)
87
+ rule.delete(:_metadata)
88
+ rule
89
+ end
@@ -0,0 +1,595 @@
1
+ # Upgrading to v2 of the Ruby API client
2
+
3
+ ## Gem
4
+ First, you'll have to include the new version in your Gemfile. To do so, change the following:
5
+
6
+ ```diff
7
+ - gem 'algoliasearch'
8
+ + gem 'algolia', git: 'https://github.com/algolia/algoliasearch-client-ruby.git', tag: 'v2.0.0-alpha.1'
9
+ ```
10
+
11
+ Then, you'll need to change your current `require` statements:
12
+
13
+ ```diff
14
+ - require 'algoliasearch'
15
+ + require 'algolia'
16
+ ```
17
+
18
+ ## Class names
19
+ All classes have been namespaced. The mostly used classes are now as follows:
20
+
21
+ - `Algolia::Client` -> `Algolia::Search::Client`
22
+ - `Algolia::Index` -> `Algolia::Search::Index`
23
+ - `Algolia::AccountClient` -> `Algolia::Account::Client`
24
+ - `Algolia::Analytics` -> `Algolia::Analytics::Client`
25
+ - `Algolia::Insights` -> `Algolia::Insights::Client`
26
+
27
+ ## Initialize the client and index
28
+ There's a slight change in how you initialize the client. The index initialization didn't change.
29
+ ```ruby
30
+ # Before
31
+ client = Algolia::Client.new(
32
+ application_id: 'APP_ID',
33
+ api_key: 'API_KEY'
34
+ )
35
+ index = client.init_index('index_name')
36
+
37
+ # After
38
+ client = Algolia::Search::Client.create('APP_ID', 'API_KEY')
39
+ index = client.init_index('index_name')
40
+ # or
41
+ search_config = Algolia::Search::Config.new(app_id: app_id, api_key: api_key)
42
+ client = Algolia::Search::Client.create_with_config(search_config)
43
+ index = client.init_index('index_name')
44
+ ```
45
+
46
+ ## Search parameters and request options
47
+ The search parameters and request options are still optional, but they are combined into a single hash instead of two.
48
+ For example:
49
+ ```ruby
50
+ # Before
51
+ request_opts = { 'X-Algolia-UserToken': 'user123' }
52
+ search_params = { hitsPerPage: 50 }
53
+
54
+ index.search('query', search_params, request_opts)
55
+
56
+ # After
57
+ opts = {
58
+ :headers => {
59
+ 'X-Algolia-UserToken': 'user123'
60
+ },
61
+ :params => {
62
+ hitsPerPage: 50
63
+ }
64
+ }
65
+ index.search('query', opts)
66
+ ```
67
+
68
+ ## Methods
69
+
70
+ ### `Client`
71
+
72
+ #### `multiple_queries`
73
+ The `strategy` parameter is no longer a string, but a key in the `requestOptions`.
74
+ ```ruby
75
+ queries = [
76
+ { indexName: 'index_name1', params: { query: 'query', hitsPerPage: 2 } },
77
+ { indexName: 'index_name2', params: { query: 'another_query', hitsPerPage: 5 } }
78
+ ]
79
+
80
+ # Before
81
+ client.multiple_queries(queries, 'stopIfEnoughMatches')
82
+
83
+ # After
84
+ client.multiple_queries(queries, { strategy: 'stopIfEnoughMatches' })
85
+ ```
86
+
87
+ #### `copy_settings`
88
+ No change.
89
+
90
+ #### `list_indexes`
91
+ No change.
92
+
93
+ #### `copy_index`
94
+ No change.
95
+
96
+ #### `move_index`
97
+ No change.
98
+
99
+ #### `generate_secured_api_key`
100
+ This method is moved to the `Algolia::Search::Client` class.
101
+ ```ruby
102
+ # Before
103
+ secured_api_key = Algolia.generate_secured_api_key('api_key', {
104
+ validUntil: now - (10 * 60)
105
+ })
106
+
107
+ # After
108
+ secured_api_key = Algolia::Search::Client.generate_secured_api_key('api_key', {
109
+ validUntil: now - (10 * 60)
110
+ })
111
+ ```
112
+
113
+ #### `add_api_key`
114
+ `acl` is still the first parameter. The other parameters have been moved to the `requestOptions`.
115
+
116
+ ```ruby
117
+ # Before
118
+ client.add_api_key({ acl: ['search'], description: 'A description', indexes: ['index']})
119
+
120
+ # After
121
+ client.add_api_key(['search'], {
122
+ description: 'A description',
123
+ indexes: ['index']
124
+ })
125
+ ```
126
+
127
+ #### `update_api_key`
128
+ This method is moved to the `Algolia::Search::Client` class.
129
+ ```ruby
130
+ # Before
131
+ Algolia.update_api_key('api_key', { maxHitsPerQuery: 42 })
132
+
133
+ # After
134
+ client.update_api_key('api_key', { maxHitsPerQuery: 42 })
135
+ ```
136
+
137
+ #### `delete_api_key`
138
+ No change.
139
+
140
+ #### `restore_api_key`
141
+ No change.
142
+
143
+ #### `get_api_key`
144
+ No change.
145
+
146
+ #### `list_api_keys`
147
+ No change.
148
+
149
+ #### `get_secured_api_key_remaining_validity`
150
+ This method is moved to the `Algolia::Search::Client` class.
151
+ ```ruby
152
+ # Before
153
+ Algolia.get_secured_api_key_remaining_validity('api_key')
154
+
155
+ # After
156
+ Algolia::Search::Client.get_secured_api_key_remaining_validity('api_key')
157
+ ```
158
+
159
+ #### `copy_synonyms`
160
+ No change.
161
+
162
+ #### `assign_user_id`
163
+ No change.
164
+
165
+ #### `assign_user_ids`
166
+ Newly added method to add multiple userIDs to a cluster.
167
+ ```ruby
168
+ user_ids = ['1','2','3']
169
+
170
+ # Before
171
+ user_ids.each { |id| client.assign_user_id(id, 'my-cluster')}
172
+
173
+ # After
174
+ client.assign_user_ids(user_ids, 'my-cluster')
175
+ ```
176
+
177
+ #### `get_top_user_id`
178
+ No change.
179
+
180
+ #### `get_user_id`
181
+ No change.
182
+
183
+ #### `list_clusters`
184
+ No change.
185
+
186
+ #### `list_user_ids`
187
+ The `page` and `hitsPerPage` parameters are now part of the `requestOptions`.
188
+ ```ruby
189
+ # Before
190
+ page = 0
191
+ hits_per_page = 20
192
+ client.list_user_ids(page, hits_per_page)
193
+
194
+ # After
195
+ client.list_user_ids({ hitPerPage: 20, page: 0 })
196
+ ```
197
+
198
+ #### `remove_user_id`
199
+ No change.
200
+
201
+ #### `search_user_ids`
202
+ The `clusterName`, `page` and `hitsPerPage` parameters are now part of the `requestOptions`.
203
+ ```ruby
204
+ # Before
205
+ page = 0
206
+ hits_per_page = 12
207
+ client.search_user_ids('query', 'my-cluster', page, hits_per_page)
208
+
209
+ # After
210
+ client.search_user_ids('query', {clusterName: 'my-cluster', hitPerPage: 12, page: 0 })
211
+ ```
212
+
213
+ #### `pending_mappings`
214
+ New method to check the status of your clusters' migration or user creation.
215
+ ```ruby
216
+ client.pending_mapping?({ retrieveMappings: true })
217
+ ```
218
+
219
+ #### `get_logs`
220
+ The `offset`, `length`, and `type` parameters are now part of the `requestOptions`.
221
+ ```ruby
222
+ # Before
223
+ offset = 5
224
+ length = 100
225
+ puts client.get_logs(offset, length, 'all')
226
+
227
+ # After
228
+ client.get_logs({ offset: 5, length: 10, type: 'all' })
229
+ ```
230
+
231
+ #### `copy_rules`
232
+ No change.
233
+
234
+ ### `Index`
235
+ #### `search`
236
+ `searchParameters` and `requestOptions` are a single parameter now.
237
+
238
+ ```ruby
239
+ # Before
240
+ request_opts = { 'X-Algolia-UserToken': 'user123' }
241
+ search_params = { hitsPerPage: 50 }
242
+
243
+ index.search('query', search_params, request_opts)
244
+
245
+ # After
246
+ opts = {
247
+ :headers => {
248
+ 'X-Algolia-UserToken': 'user123'
249
+ },
250
+ :params => {
251
+ hitsPerPage: 50
252
+ }
253
+ }
254
+ index.search('query', opts)
255
+ ```
256
+
257
+ #### `search_for_facet_values`
258
+ `searchParameters` and `requestOptions` are a single parameter now.
259
+ ```ruby
260
+ # Before
261
+ request_opts = { 'X-Algolia-UserToken': 'user123' }
262
+ search_params = { hitsPerPage: 50 }
263
+
264
+ index.search_for_facet_values('category', 'phone', search_params, request_opts)
265
+
266
+ # After
267
+ opts = {
268
+ :headers => {
269
+ 'X-Algolia-UserToken': 'user123'
270
+ },
271
+ :params => {
272
+ hitsPerPage: 50
273
+ }
274
+ }
275
+ index.search_for_facet_values('category', 'phone', opts)
276
+ ```
277
+
278
+ #### `find_object`
279
+ The method takes a lambda, proc or block as the first argument (anything that responds to `call`), and the `requestOptions` as the second
280
+ ```ruby
281
+ # Before
282
+ index.find_object({query: 'query', paginate: true}) { |hit| hit[:title].include?('algolia') }
283
+
284
+ # After
285
+ index.find_object(-> (hit) { hit[:title].include?('algolia') }, { query: 'query', paginate: true })
286
+ ```
287
+
288
+ #### `get_object_position`
289
+ The classname has changed, not the method itself.
290
+ ```ruby
291
+ # Before
292
+ position = Algolia::Index.get_object_position(results, 'object')
293
+
294
+ # After
295
+ position = Algolia::Search::Index.get_object_position(results, 'object')
296
+ ```
297
+
298
+ #### `add_object` and `add_objects`
299
+ These methods have been removed in favor of `save_object` and `save_objects`.
300
+
301
+ #### `save_object` and `save_objects`
302
+ No change.
303
+
304
+ #### `partial_update_object`
305
+ The `objectID` parameter is removed. `create_if_not_exists` is now part of the `requestOptions` parameter.
306
+
307
+ ```ruby
308
+ obj = { objectID: '1234', prop: 'value' }
309
+
310
+ # Before
311
+ create_if_not_exists = true
312
+ index.partial_update_object(obj, obj[:objectID], create_if_not_exists)
313
+
314
+ # After
315
+ index.partial_update_object(obj, { createIfNotExists: true })
316
+ ```
317
+
318
+ #### `partial_update_objects`
319
+ The `create_if_not_exists` parameter is now part of the `requestOptions` parameter.
320
+
321
+ ```ruby
322
+ # Before
323
+ create_if_not_exists = true
324
+ index.partial_update_objects(objects, create_if_not_exists)
325
+
326
+ # After
327
+ index.partial_update_objects(objects, { createIfNotExists: true })
328
+ ```
329
+
330
+ #### `delete_object` and `delete_objects`
331
+ No change.
332
+
333
+ #### `replace_all_objects`
334
+ No change.
335
+
336
+ #### `delete_by`
337
+ No change.
338
+
339
+ #### `clear_index`
340
+ Renamed to `clear_objects`.
341
+ ```ruby
342
+ # Before
343
+ index.clear_index
344
+
345
+ # After
346
+ index.clear_objects
347
+ ```
348
+
349
+ #### `get_object` and `get_objects`
350
+ The `attributesToRetrieve` parameter is now part of the `requestOptions`.
351
+ ```ruby
352
+ # Before
353
+ index.get_object('1234', ['title'])
354
+ index.get_objects([1,2,3], ['title'])
355
+
356
+ # After
357
+ index.get_object('1234', { attributesToRetrieve: ['title'] })
358
+ index.get_objects([1,2,3], { attributesToRetrieve: ['title'] })
359
+ ```
360
+
361
+ #### `multiple_get_objects`
362
+ No change.
363
+
364
+ #### `batch`
365
+ No change.
366
+
367
+ #### `get_settings`
368
+ No change.
369
+
370
+ #### `set_settings`
371
+ No change.
372
+
373
+ #### `delete_index`
374
+ Instead of calling the `delete_index` method on the client, you should call the `delete` method directly on the index object.
375
+
376
+ ```ruby
377
+ # Before
378
+ client.delete_index('foo')
379
+
380
+ # After
381
+ index.delete
382
+ ```
383
+
384
+ #### `browse`
385
+ Renamed to `browse_objects`.
386
+ ```ruby
387
+ # Before
388
+ request_opts = { 'X-Algolia-UserToken': 'user123' }
389
+ index.browse({ query: 'query'}, nil, request_opts) do |hit|
390
+ puts hit
391
+ end
392
+
393
+ # After
394
+ opts = {
395
+ query: 'query',
396
+ headers: { 'X-Algolia-UserToken': 'user123' }
397
+ }
398
+ index.browse_objects(opts) do |hit|
399
+ puts hit
400
+ end
401
+ ```
402
+
403
+ #### `index.exists?`
404
+ No change.
405
+
406
+ #### `save_synonym`
407
+ The `objectID` parameter has been removed, and should be part of the synonym hash.
408
+ ```ruby
409
+ # Before
410
+ forward_to_replicas = true
411
+ index.save_synonym('one', { objectID: 'one', type: 'synonym', synonyms: %w(one two) }, forward_to_replicas)
412
+
413
+ # After
414
+ index.save_synonym({ objectID: 'one', type: 'synonym', synonyms: %w(one two) }, { forwardToReplicas: true})
415
+ ```
416
+
417
+ #### `batch_synonyms`
418
+ Renamed to `save_synonyms`. `forwardToReplicas` and `replaceExistingSynonyms` parmameters are now part of `requestOptions`.
419
+ ```ruby
420
+ # Before
421
+ forward_to_replicas = true
422
+ replace_existing_synonyms = true
423
+ index.batch_synonyms(synonyms, forward_to_replicas, replace_existing_synonyms)
424
+ # After
425
+ index.save_synonyms(synonyms, { forwardToReplicas: true, replaceExistingSynonyms: true })
426
+ ```
427
+
428
+ #### `delete_synonym`
429
+ No change.
430
+
431
+ #### `clear_synonyms`
432
+ No change.
433
+
434
+ #### `get_synonym`
435
+ No change.
436
+
437
+ #### `search_synonyms`
438
+ No change.
439
+
440
+ #### `replace_all_synonyms`
441
+ No change.
442
+
443
+ #### `export_synonyms`
444
+ Renamed to `browse_synonyms`.
445
+ ```ruby
446
+ # Before
447
+ synonyms = index.export_synonyms
448
+
449
+ # After
450
+ synonyms = index.browse_synonyms
451
+ ```
452
+
453
+ #### `save_rule`
454
+ The `objectID` parameter has been removed, and should be part of the Rule object.
455
+ ```ruby
456
+ # Before
457
+ index.save_rule('unique-id', {
458
+ objectID: 'unique-id',
459
+ condition: { anchoring: 'is', pattern: 'pattern' },
460
+ consequence: {
461
+ params: {
462
+ query: {
463
+ edits: [
464
+ { type: 'remove', delete: 'pattern' }
465
+ ]
466
+ }
467
+ }
468
+ }
469
+ })
470
+
471
+ # After
472
+ index.save_rule({
473
+ objectID: 'unique-id',
474
+ condition: { anchoring: 'is', pattern: 'pattern' },
475
+ consequence: {
476
+ params: {
477
+ query: {
478
+ edits: [
479
+ { type: 'remove', delete: 'pattern' }
480
+ ]
481
+ }
482
+ }
483
+ }
484
+ })
485
+ ```
486
+
487
+ #### `batch_rules`
488
+ Renamed to `save_rules`. The `forwardToReplicas` and `clearExistingRules` parameters should now be part of the `requestOptions`.
489
+ ```ruby
490
+ # Before
491
+ forward_to_replicas = true
492
+ clear_existing_rules = true
493
+ index.batch_rules(rules, forward_to_replicas, clear_existing_rules)
494
+
495
+ # After
496
+ index.save_rules(rules, { forwardToReplicas: true, clearExistingRules: true })
497
+ ```
498
+
499
+ #### `get_rule`
500
+ No change.
501
+
502
+ #### `delete_rule`
503
+ The `forwardToReplicas` parameter is now part of the `requestOptions`.
504
+ ```ruby
505
+ # Before
506
+ forward_to_replicas = true
507
+ index.delete_rule('rule-id', forward_to_replicas)
508
+
509
+ # After
510
+ index.delete_rule('rule-id', { forwardToReplicas: true })
511
+ ```
512
+
513
+ #### `clear_rules`
514
+ The `forwardToReplicas` parameter is now part of the `requestOptions`.
515
+ ```ruby
516
+ # Before
517
+ forward_to_replicas = true
518
+ index.clear_rules(forward_to_replicas)
519
+
520
+ # After
521
+ index.clear_rules({ forwardToReplicas: true })
522
+ ```
523
+
524
+ #### `search_rules`
525
+ No change.
526
+
527
+ #### `replace_all_rules`
528
+ No change.
529
+
530
+ #### `export_rules`
531
+ Renamed to `browse_rules`.
532
+
533
+ ### `AnalyticsClient`
534
+ #### `add_ab_test`
535
+ No change.
536
+
537
+ #### `get_ab_test`
538
+ No change.
539
+
540
+ #### `get_ab_tests`
541
+ No change.
542
+
543
+ #### `stop_ab_test`
544
+ No change.
545
+
546
+ #### `delete_ab_test`
547
+ No change.
548
+
549
+ ### `InsightsClient`
550
+ #### `clicked_object_ids_after_search`
551
+ No change.
552
+
553
+ #### `clicked_object_ids`
554
+ No change.
555
+
556
+ #### `clicked_filters`
557
+ No change.
558
+
559
+ #### `converted_object_ids_after_search`
560
+ No change.
561
+
562
+ #### `converted_object_ids`
563
+ No change.
564
+
565
+ #### `converted_filters`
566
+ No change.
567
+
568
+ #### `viewed_object_ids`
569
+ No change.
570
+
571
+ #### `viewed_filters`
572
+ No change.
573
+
574
+ ### `RecommendationClient`
575
+ #### `set_personalization_strategy`
576
+ This new method is available on the `Algolia::Recommendation::Client` class.
577
+
578
+ #### `set_personalization_strategy`
579
+ This new method is available on the `Algolia::Recommendation::Client` class.
580
+
581
+ ## Configuring timeouts
582
+ You can configure timeouts by passing a custom `Algolia::Search::Config` object to the constructor of your client.
583
+ ```ruby
584
+ # Before
585
+ client = Algolia::Client.new({
586
+ application_id: 'app_id',
587
+ api_key: 'api_key',
588
+ connect_timeout: 2,
589
+ receive_timeout: 10,
590
+ })
591
+
592
+ # After
593
+ search_config = Algolia::Search::Config.new(app_id: 'app_id', api_key: 'api_key', read_timeout: 10, connect_timeout: 2)
594
+ client = Algolia::Search::Client.create_with_config(search_config)
595
+ ```