From d92730e5ff4214ff3d5acdbad51000b3a72da76e Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Nov 2018 11:37:29 +0100 Subject: [PATCH 01/15] Adds reindex method --- algoliasearch/__init__.py | 2 + algoliasearch/index.py | 77 +++++++++++++++ algoliasearch/index_content.py | 53 ++++++++++ tests/test_index.py | 174 +++++++++++++++++++++++++++++++++ 4 files changed, 306 insertions(+) create mode 100644 algoliasearch/index_content.py diff --git a/algoliasearch/__init__.py b/algoliasearch/__init__.py index 4d4e5a10b..b9f06b86b 100644 --- a/algoliasearch/__init__.py +++ b/algoliasearch/__init__.py @@ -24,6 +24,7 @@ from . import client from . import index +from . import index_content from . import helpers from . import version @@ -34,5 +35,6 @@ class algoliasearch(object): Client = client.Client Index = index.Index + IndexContent = index_content.IndexContent AlgoliaException = helpers.AlgoliaException RequestOptions = client.RequestOptions diff --git a/algoliasearch/index.py b/algoliasearch/index.py index cbde3d6f5..3238980c7 100644 --- a/algoliasearch/index.py +++ b/algoliasearch/index.py @@ -1142,6 +1142,83 @@ def search_rules(self, query=None, anchoring=None, context=None, page=None, hits return self._req(True, '/rules/search', 'POST', request_options, data=params) + def reindex(self, index_content, request_options=None): + """ + Reindex the current index using the provided index_content + """ + safe = False + if request_options is not None and 'safe' in request_options.parameters: + safe = request_options.parameters['safe'] + request_options.parameters.pop('safe', None) + + letters = string.ascii_letters + random_string = ''.join(random.choice(letters) for i in range(10)) + tmp_index_name = self.index_name + '_tmp_' + random_string + + tmp_index = self.client.init_index(tmp_index_name) + + scope = [] + settings = index_content.get_settings() + if not isinstance(settings, dict): + scope.append('settings') + + synonyms = index_content.get_synonyms() + if not isinstance(synonyms, list): + scope.append('synonyms') + + rules = index_content.get_rules() + if not isinstance(rules, list): + scope.append('rules') + + responses = [] + + if scope: + response = self.client.copy_index(self.index_name, tmp_index_name, scope=scope) + responses.append(response) + if safe: + tmp_index.wait_task(response['taskID']) + + if isinstance(settings, dict): + response = tmp_index.set_settings(settings) + responses.append(response) + + if isinstance(synonyms, list): + response = tmp_index.batch_synonyms(synonyms) + responses.append(response) + + if isinstance(rules, list): + response = tmp_index.batch_rules(rules) + responses.append(response) + + batch = [] + batch_size = 1000 + count = 0 + for obj in index_content.get_objects(): + batch.append(obj) + count += 1 + + if count == batch_size: + response = tmp_index.save_objects(batch) + responses.append(response) + batch = [] + count = 0 + + if batch: + response = tmp_index.save_objects(batch) + responses.append(response) + + if safe: + for response in responses: + tmp_index.wait_task(response['taskID']) + + response = self.client.move_index(tmp_index_name, self.index_name) + responses.append(response) + + if safe: + self.wait_task(response['taskID']) + + return responses + def _req(self, is_search, path, meth, request_options=None, params=None, data=None): """Perform an HTTPS request with retry logic.""" path = '%s%s' % (self._request_path, path) diff --git a/algoliasearch/index_content.py b/algoliasearch/index_content.py new file mode 100644 index 000000000..331e0b04b --- /dev/null +++ b/algoliasearch/index_content.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2013 Algolia +http://www.algolia.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights lw1 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + + +class IndexContent: + """ + Content of index. + """ + + def get_objects(self): + """ + Return index's objects. + """ + return [] + + def get_settings(self): + """ + Return index's settings. + """ + return False + + def get_synonyms(self): + """ + Return index's synonyms. + """ + return False + + def get_rules(self): + """ + Return index's rules. + """ + return False diff --git a/tests/test_index.py b/tests/test_index.py index 03e2719e6..c2eebc886 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -6,6 +6,7 @@ from algoliasearch.client import RequestOptions, MAX_API_KEY_LENGTH from algoliasearch.helpers import AlgoliaException +from algoliasearch.index_content import IndexContent from .helpers import Factory, rule_stub, synonym_stub @@ -663,3 +664,176 @@ def test_batch(rw_index): assert False except AlgoliaException as e: assert 'does not exist' in str(e) + + +class ObjectsStub(IndexContent): + def get_objects(self): + return [{'objectID': 'B', 'method': 'Foo'}] + + +def test_reindex_objects(index): + obj = {'objectID': 'A', 'method': 'Bar'} + res = index.save_object(obj) + index.wait_task(res['taskID']) + + stub = ObjectsStub() + responses = index.reindex(stub) + for response in responses: + index.wait_task(response['taskID']) + res = index.search('') + assert len(res['hits']) == 1 + del res['hits'][0]['_highlightResult'] + assert stub.get_objects()[0] in res['hits'] + + +class WithListRulesStub(IndexContent): + def get_rules(self): + return [rule_stub('reindex-rule-foo')] + + +def test_reindex_with_list_rules(index): + rule = rule_stub('reindex-rule-bar') + res = index.batch_rules([rule]) + index.wait_task(res['taskID']) + + stub = WithListRulesStub() + responses = index.reindex(stub) + for response in responses: + index.wait_task(response['taskID']) + + rules = index.search_rules() + assert rules['nbHits'] == 1 + assert index.read_rule('reindex-rule-foo') == rule_stub('reindex-rule-foo') + + +class FalseRulesStub(IndexContent): + def get_rules(self): + return False + + +def test_reindex_false_rules(index): + rule = rule_stub('reindex-rule-bar') + res = index.batch_rules([rule]) + index.wait_task(res['taskID']) + + stub = FalseRulesStub() + responses = index.reindex(stub) + for response in responses: + index.wait_task(response['taskID']) + + rules = index.search_rules() + assert rules['nbHits'] == 1 + assert index.read_rule('reindex-rule-bar') == rule + + +class WithListSynonymsStub(IndexContent): + def get_synonyms(self): + return [synonym_stub('reindex-synonym-foo')] + + +def test_reindex_with_list_synonyms(index): + synonym = synonym_stub('reindex-synonym-bar') + res = index.batch_synonyms([synonym]) + index.wait_task(res['taskID']) + + stub = WithListSynonymsStub() + responses = index.reindex(stub) + for response in responses: + index.wait_task(response['taskID']) + + synonyms = index.search_synonyms('') + assert synonyms['nbHits'] == 1 + assert index.get_synonym('reindex-synonym-foo') == synonym_stub('reindex-synonym-foo') + + +class FalseSynonymsStub(IndexContent): + def get_synonyms(self): + return False + + +def test_reindex_false_synonyms(index): + synonym = synonym_stub('reindex-synonym-bar') + res = index.batch_synonyms([synonym]) + index.wait_task(res['taskID']) + + stub = FalseSynonymsStub() + responses = index.reindex(stub) + for response in responses: + index.wait_task(response['taskID']) + + synonyms = index.search_synonyms('') + assert synonyms['nbHits'] == 1 + assert index.get_synonym('reindex-synonym-bar') == synonym + + +class WithDictSettingsStub(IndexContent): + def get_settings(self): + return {'userData': 'reindex-settings-foo'} + + +def test_reindex_with_dict_settings(index): + res = index.set_settings({'userData': 'reindex-settings-bar'}) + index.wait_task(res['taskID']) + + stub = WithDictSettingsStub() + responses = index.reindex(stub) + for response in responses: + index.wait_task(response['taskID']) + + settings = index.get_settings() + assert settings['userData'] == 'reindex-settings-foo' + + +class FalseSettingsStub(IndexContent): + def get_objects(self): + return [{'objectID': 'A', 'method': 'Foo'}] + + +def test_reindex_false_synonyms(index): + res = index.set_settings({'userData': 'reindex-settings-bar'}) + index.wait_task(res['taskID']) + + stub = FalseSettingsStub() + responses = index.reindex(stub) + for response in responses: + index.wait_task(response['taskID']) + + settings = index.get_settings() + assert settings['userData'] == 'reindex-settings-bar' + + +class StubToTestSafe(IndexContent): + + def get_objects(self): + return [{'objectID': 'B', 'method': 'reindex-object-safe'}] + + def get_settings(self): + return {'userData': 'reindex-settings-safe'} + + def get_rules(self): + return [rule_stub('reindex-rule-safe')] + + def get_synonyms(self): + return [synonym_stub('reindex-synonym-safe')] + + +def test_reindex_with_safe(index): + stub = StubToTestSafe() + request_options = RequestOptions({'safe': True}) + index.reindex(stub, request_options) + + res = index.search('') + assert len(res['hits']) == 1 + del res['hits'][0]['_highlightResult'] + assert stub.get_objects()[0] in res['hits'] + + settings = index.get_settings() + assert settings['userData'] == 'reindex-settings-safe' + + synonyms = index.search_synonyms('') + assert synonyms['nbHits'] == 1 + assert index.get_synonym('reindex-synonym-safe') == synonym_stub('reindex-synonym-safe') + + rules = index.search_rules() + assert rules['nbHits'] == 1 + assert index.read_rule('reindex-rule-safe') == rule_stub('reindex-rule-safe') From 5c9119984dc4f330173039777aaad345f640e140 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Nov 2018 13:31:11 +0100 Subject: [PATCH 02/15] Adds replicas feature --- algoliasearch/index.py | 10 ++++++++++ tests/test_index.py | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/algoliasearch/index.py b/algoliasearch/index.py index 3238980c7..c43af21ae 100644 --- a/algoliasearch/index.py +++ b/algoliasearch/index.py @@ -1178,7 +1178,11 @@ def reindex(self, index_content, request_options=None): if safe: tmp_index.wait_task(response['taskID']) + replicas = False if isinstance(settings, dict): + if settings['replicas'] is not None: + replicas = settings['replicas'] + settings.pop('replicas', None) response = tmp_index.set_settings(settings) responses.append(response) @@ -1217,6 +1221,12 @@ def reindex(self, index_content, request_options=None): if safe: self.wait_task(response['taskID']) + if replicas: + response = self.set_settings({'replicas': replicas}) + responses.append(response) + if safe: + self.wait_task(response['taskID']) + return responses def _req(self, is_search, path, meth, request_options=None, params=None, data=None): diff --git a/tests/test_index.py b/tests/test_index.py index c2eebc886..cfc2d13e1 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -837,3 +837,26 @@ def test_reindex_with_safe(index): rules = index.search_rules() assert rules['nbHits'] == 1 assert index.read_rule('reindex-rule-safe') == rule_stub('reindex-rule-safe') + + +class StubToTestReplicas(IndexContent): + + def __init__(self, index_name): + self.index_name = index_name + + def get_objects(self): + return [{'objectID': 'B', 'method': 'reindex-object-safe'}] + + def get_settings(self): + return {'replicas': [self.index_name + '-replica']} + + +def test_reindex_replicas(index): + stub = StubToTestReplicas(index.index_name) + request_options = RequestOptions({'safe': True}) + index.reindex(stub, request_options) + + # Assert 'replicas' settings still exist. + settings = index.get_settings() + assert settings['replicas'] == [index.index_name + '-replica'] + From b1512b82cdc6aa31ae9be25339c0bcdbec22aa40 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Nov 2018 16:43:07 +0100 Subject: [PATCH 03/15] Adds account reindex --- algoliasearch/__init__.py | 2 + algoliasearch/account_client.py | 83 +++++++++++++++++++++++++++++++++ tests/test_account_client.py | 53 +++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 algoliasearch/account_client.py create mode 100644 tests/test_account_client.py diff --git a/algoliasearch/__init__.py b/algoliasearch/__init__.py index b9f06b86b..16632aa5b 100644 --- a/algoliasearch/__init__.py +++ b/algoliasearch/__init__.py @@ -22,6 +22,7 @@ THE SOFTWARE. """ +from . import account_client from . import client from . import index from . import index_content @@ -33,6 +34,7 @@ class algoliasearch(object): VERSION = version.VERSION + AccountClient = account_client.AccountClient Client = client.Client Index = index.Index IndexContent = index_content.IndexContent diff --git a/algoliasearch/account_client.py b/algoliasearch/account_client.py new file mode 100644 index 000000000..781d9538e --- /dev/null +++ b/algoliasearch/account_client.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2013 Algolia +http://www.algolia.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights lw1 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from .helpers import AlgoliaException +import sys + + +class AccountClient: + """ + The Account Client + """ + + @staticmethod + def reindex(source_index, destination_index, request_options=None): + """ + Copies the source index into the destination index. It works + even between different accounts. + """ + settings = {} + try: + settings = destination_index.get_settings() + except AlgoliaException: + pass + + if settings: + raise AlgoliaException( + 'Destination index already exists. Please delete it before copying index across applications.') + + responses = [] + + # Transfer settings + settings = source_index.get_settings() + responses.append(destination_index.set_settings(settings)) + + # Transfer synonyms + synonyms = list(source_index.iter_synonyms()) + responses.append(destination_index.batch_synonyms(synonyms)) + + # Transfer rules + rules = list(source_index.iter_rules()) + responses.append(destination_index.batch_rules(rules)) + + # Transfer objects + responses = [] + batch = [] + batch_size = 1000 + count = 0 + for obj in source_index.browse_all(): + batch.append(obj) + count += 1 + + if count == batch_size: + response = destination_index.save_objects(batch) + responses.append(response) + batch = [] + count = 0 + + if batch: + response = destination_index.save_objects(batch) + responses.append(response) + + return responses diff --git a/tests/test_account_client.py b/tests/test_account_client.py new file mode 100644 index 000000000..0cb90d427 --- /dev/null +++ b/tests/test_account_client.py @@ -0,0 +1,53 @@ +import pytest + +from algoliasearch.account_client import AccountClient +from algoliasearch.helpers import AlgoliaException +from .helpers import rule_stub, synonym_stub + + +def test_reindex_destination_must_not_exist(index, ro_index): + response = index.save_object({'objectID': 'Foo', 'name': 'Bar'}) + index.wait_task(response['taskID']) + + response = ro_index.save_object({'objectID': 'Foo', 'name': 'Bar'}) + ro_index.wait_task(response['taskID']) + + with pytest.raises(AlgoliaException): + AccountClient.reindex(index, ro_index) + + +def test_reindex_copy_the_index(index, ro_index): + responses = [ + index.save_object({'objectID': 'Foo', 'name': 'Bar'}), + index.batch_rules([rule_stub('foo')]), + index.batch_synonyms([synonym_stub('foo')]), + index.set_settings({'userData': 'foo'}) + ] + + for response in responses: + index.wait_task(response['taskID']) + + response = ro_index.client.delete_index(ro_index.index_name) # Make sure target don't exist. + ro_index.wait_task(response['taskID']) + + responses = AccountClient.reindex(index, ro_index) + for response in responses: + ro_index.wait_task(response['taskID']) + + # Assert objects got copied + res = ro_index.search('') + assert len(res['hits']) == 1 + del res['hits'][0]['_highlightResult'] + assert res['hits'][0] == {'objectID': 'Foo', 'name': 'Bar'} + + # Assert settings got copied + settings = ro_index.get_settings() + assert settings['userData'] == 'foo' + + # Assert synonyms got copied + rule = ro_index.read_rule('foo') + assert rule == rule_stub('foo') + + # Assert synonyms got copied + synonym = ro_index.get_synonym('foo') + assert synonym == synonym_stub('foo') From b8d1c9a11003bcd904142fc27a38a29b90d25dd4 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Nov 2018 17:14:34 +0100 Subject: [PATCH 04/15] Remove unused import --- algoliasearch/account_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/algoliasearch/account_client.py b/algoliasearch/account_client.py index 781d9538e..ab52e4d64 100644 --- a/algoliasearch/account_client.py +++ b/algoliasearch/account_client.py @@ -23,7 +23,6 @@ """ from .helpers import AlgoliaException -import sys class AccountClient: From 971de476522f60a5c15e4a45e949b241a8542999 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Nov 2018 17:37:07 +0100 Subject: [PATCH 05/15] Renames reindex to copy index --- algoliasearch/account_client.py | 8 +++----- tests/test_account_client.py | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/algoliasearch/account_client.py b/algoliasearch/account_client.py index ab52e4d64..c6be1956f 100644 --- a/algoliasearch/account_client.py +++ b/algoliasearch/account_client.py @@ -31,18 +31,16 @@ class AccountClient: """ @staticmethod - def reindex(source_index, destination_index, request_options=None): + def copy_index(source_index, destination_index, request_options=None): """ Copies the source index into the destination index. It works even between different accounts. """ - settings = {} try: - settings = destination_index.get_settings() + destination_index.get_settings() except AlgoliaException: pass - - if settings: + else: raise AlgoliaException( 'Destination index already exists. Please delete it before copying index across applications.') diff --git a/tests/test_account_client.py b/tests/test_account_client.py index 0cb90d427..f720f3ca3 100644 --- a/tests/test_account_client.py +++ b/tests/test_account_client.py @@ -5,7 +5,7 @@ from .helpers import rule_stub, synonym_stub -def test_reindex_destination_must_not_exist(index, ro_index): +def test_copy_index_destination_must_not_exist(index, ro_index): response = index.save_object({'objectID': 'Foo', 'name': 'Bar'}) index.wait_task(response['taskID']) @@ -13,10 +13,10 @@ def test_reindex_destination_must_not_exist(index, ro_index): ro_index.wait_task(response['taskID']) with pytest.raises(AlgoliaException): - AccountClient.reindex(index, ro_index) + AccountClient.copy_index(index, ro_index) -def test_reindex_copy_the_index(index, ro_index): +def test_copy_index_copy_the_index(index, ro_index): responses = [ index.save_object({'objectID': 'Foo', 'name': 'Bar'}), index.batch_rules([rule_stub('foo')]), @@ -30,7 +30,7 @@ def test_reindex_copy_the_index(index, ro_index): response = ro_index.client.delete_index(ro_index.index_name) # Make sure target don't exist. ro_index.wait_task(response['taskID']) - responses = AccountClient.reindex(index, ro_index) + responses = AccountClient.copy_index(index, ro_index) for response in responses: ro_index.wait_task(response['taskID']) From 92970a6d9f477e9d212105dfadcda311cd637358 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 14 Nov 2018 17:39:15 +0100 Subject: [PATCH 06/15] Updates changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ae0e1d37..1db1df4c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ * Adds `replace_all_rules` and `replace_all_synonyms` methods on client - PR [#390](https://github.com/algolia/algoliasearch-client-python/pull/390) +* Adds `Index.reindex` and `AccountClient.copy_index` methods on client - PR [#391](https://github.com/algolia/algoliasearch-client-python/pull/391) + ### 1.17.0 - 2018-06-19 * Introduce AB Testing feature - PR [#408](https://github.com/algolia/algoliasearch-client-php/pull/#408) From dd6701d530ad36c831ccad24a871e0bd79a4f3da Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 16 Nov 2018 10:47:19 +0100 Subject: [PATCH 07/15] Adds account client verification between different accounts --- algoliasearch/account_client.py | 34 ++++++++++++++++++++++----------- tests/conftest.py | 6 ++++++ tests/test_account_client.py | 30 +++++++++++++++++++---------- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/algoliasearch/account_client.py b/algoliasearch/account_client.py index c6be1956f..be498d570 100644 --- a/algoliasearch/account_client.py +++ b/algoliasearch/account_client.py @@ -33,9 +33,20 @@ class AccountClient: @staticmethod def copy_index(source_index, destination_index, request_options=None): """ - Copies the source index into the destination index. It works - even between different accounts. + The method copy settings, synonyms, rules and objects from the source + index to the destination index. The replicas of the source index will + not be copied. + + Throw an exception if the destination index already exists + Throw an exception if the indices are on the same application + + @param source_index the source index + @param destination_index the destination index + @param request_options """ + if source_index.client.app_id == destination_index.client.app_id: + raise AlgoliaException('The indexes are on the same application. Use client.copy_index instead.') + try: destination_index.get_settings() except AlgoliaException: @@ -46,19 +57,20 @@ def copy_index(source_index, destination_index, request_options=None): responses = [] - # Transfer settings + # Copy settings settings = source_index.get_settings() - responses.append(destination_index.set_settings(settings)) + settings + responses.append(destination_index.set_settings(settings, False, False, request_options)) - # Transfer synonyms + # Copy synonyms synonyms = list(source_index.iter_synonyms()) - responses.append(destination_index.batch_synonyms(synonyms)) + responses.append(destination_index.batch_synonyms(synonyms, False, False, False, request_options)) - # Transfer rules + # Copy rules rules = list(source_index.iter_rules()) - responses.append(destination_index.batch_rules(rules)) + responses.append(destination_index.batch_rules(rules, False, False, request_options)) - # Transfer objects + # Copy objects responses = [] batch = [] batch_size = 1000 @@ -68,13 +80,13 @@ def copy_index(source_index, destination_index, request_options=None): count += 1 if count == batch_size: - response = destination_index.save_objects(batch) + response = destination_index.save_objects(batch, request_options) responses.append(response) batch = [] count = 0 if batch: - response = destination_index.save_objects(batch) + response = destination_index.save_objects(batch, request_options) responses.append(response) return responses diff --git a/tests/conftest.py b/tests/conftest.py index 89ba620c2..0073a6f22 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,6 +9,12 @@ def mcm_client(): return create_client('ALGOLIA_APP_ID_MCM', 'ALGOLIA_API_KEY_MCM') +@pytest.fixture +def mcm_index(mcm_client): + idx = create_index(mcm_client) + yield idx + mcm_client.delete_index(idx.index_name) # Tear down + @pytest.fixture def client(): return create_client() diff --git a/tests/test_account_client.py b/tests/test_account_client.py index f720f3ca3..63422439e 100644 --- a/tests/test_account_client.py +++ b/tests/test_account_client.py @@ -5,7 +5,7 @@ from .helpers import rule_stub, synonym_stub -def test_copy_index_destination_must_not_exist(index, ro_index): +def test_copy_index_applications_must_be_different(index, ro_index): response = index.save_object({'objectID': 'Foo', 'name': 'Bar'}) index.wait_task(response['taskID']) @@ -15,8 +15,18 @@ def test_copy_index_destination_must_not_exist(index, ro_index): with pytest.raises(AlgoliaException): AccountClient.copy_index(index, ro_index) +def test_copy_index_destination_must_not_exist(index, mcm_index): + response = index.save_object({'objectID': 'Foo', 'name': 'Bar'}) + index.wait_task(response['taskID']) -def test_copy_index_copy_the_index(index, ro_index): + response = mcm_index.save_object({'objectID': 'Foo', 'name': 'Bar'}) + mcm_index.wait_task(response['taskID']) + + with pytest.raises(AlgoliaException): + AccountClient.copy_index(index, mcm_index) + + +def test_copy_index_copy_the_index(index, mcm_index): responses = [ index.save_object({'objectID': 'Foo', 'name': 'Bar'}), index.batch_rules([rule_stub('foo')]), @@ -27,27 +37,27 @@ def test_copy_index_copy_the_index(index, ro_index): for response in responses: index.wait_task(response['taskID']) - response = ro_index.client.delete_index(ro_index.index_name) # Make sure target don't exist. - ro_index.wait_task(response['taskID']) + response = mcm_index.client.delete_index(mcm_index.index_name) # Make sure target don't exist. + mcm_index.wait_task(response['taskID']) - responses = AccountClient.copy_index(index, ro_index) + responses = AccountClient.copy_index(index, mcm_index) for response in responses: - ro_index.wait_task(response['taskID']) + mcm_index.wait_task(response['taskID']) # Assert objects got copied - res = ro_index.search('') + res = mcm_index.search('') assert len(res['hits']) == 1 del res['hits'][0]['_highlightResult'] assert res['hits'][0] == {'objectID': 'Foo', 'name': 'Bar'} # Assert settings got copied - settings = ro_index.get_settings() + settings = mcm_index.get_settings() assert settings['userData'] == 'foo' # Assert synonyms got copied - rule = ro_index.read_rule('foo') + rule = mcm_index.read_rule('foo') assert rule == rule_stub('foo') # Assert synonyms got copied - synonym = ro_index.get_synonym('foo') + synonym = mcm_index.get_synonym('foo') assert synonym == synonym_stub('foo') From 6ce2bf1614848810bb4dca03107c101f7b41c0cc Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 16 Nov 2018 10:52:00 +0100 Subject: [PATCH 08/15] Skips tests that can't be tested by the community --- tests/test_account_client.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_account_client.py b/tests/test_account_client.py index 63422439e..5fae68f23 100644 --- a/tests/test_account_client.py +++ b/tests/test_account_client.py @@ -3,7 +3,7 @@ from algoliasearch.account_client import AccountClient from algoliasearch.helpers import AlgoliaException from .helpers import rule_stub, synonym_stub - +from .helpers import is_community def test_copy_index_applications_must_be_different(index, ro_index): response = index.save_object({'objectID': 'Foo', 'name': 'Bar'}) @@ -15,6 +15,9 @@ def test_copy_index_applications_must_be_different(index, ro_index): with pytest.raises(AlgoliaException): AccountClient.copy_index(index, ro_index) + +@pytest.mark.skipif(is_community, + reason='Cross application methods cannot be tested by the community') def test_copy_index_destination_must_not_exist(index, mcm_index): response = index.save_object({'objectID': 'Foo', 'name': 'Bar'}) index.wait_task(response['taskID']) @@ -26,6 +29,8 @@ def test_copy_index_destination_must_not_exist(index, mcm_index): AccountClient.copy_index(index, mcm_index) +@pytest.mark.skipif(is_community, + reason='Cross application methods cannot be tested by the community') def test_copy_index_copy_the_index(index, mcm_index): responses = [ index.save_object({'objectID': 'Foo', 'name': 'Bar'}), From 903374d2d9673293061af98a4c754fc6ff020dfc Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 19 Nov 2018 11:54:02 +0100 Subject: [PATCH 09/15] Reverts index::reindex method --- CHANGELOG.md | 2 +- algoliasearch/index.py | 89 --------------- algoliasearch/index_content.py | 53 --------- tests/helpers.py | 8 -- tests/test_index.py | 199 +-------------------------------- 5 files changed, 2 insertions(+), 349 deletions(-) delete mode 100644 algoliasearch/index_content.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 1db1df4c0..87b114016 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ * Adds `replace_all_rules` and `replace_all_synonyms` methods on client - PR [#390](https://github.com/algolia/algoliasearch-client-python/pull/390) -* Adds `Index.reindex` and `AccountClient.copy_index` methods on client - PR [#391](https://github.com/algolia/algoliasearch-client-python/pull/391) +* Adds `AccountClient.copy_index` methods on client - PR [#391](https://github.com/algolia/algoliasearch-client-python/pull/391) ### 1.17.0 - 2018-06-19 diff --git a/algoliasearch/index.py b/algoliasearch/index.py index c43af21ae..9b31a83f9 100644 --- a/algoliasearch/index.py +++ b/algoliasearch/index.py @@ -23,8 +23,6 @@ """ import time -import random -import string try: from urllib import urlencode @@ -1142,93 +1140,6 @@ def search_rules(self, query=None, anchoring=None, context=None, page=None, hits return self._req(True, '/rules/search', 'POST', request_options, data=params) - def reindex(self, index_content, request_options=None): - """ - Reindex the current index using the provided index_content - """ - safe = False - if request_options is not None and 'safe' in request_options.parameters: - safe = request_options.parameters['safe'] - request_options.parameters.pop('safe', None) - - letters = string.ascii_letters - random_string = ''.join(random.choice(letters) for i in range(10)) - tmp_index_name = self.index_name + '_tmp_' + random_string - - tmp_index = self.client.init_index(tmp_index_name) - - scope = [] - settings = index_content.get_settings() - if not isinstance(settings, dict): - scope.append('settings') - - synonyms = index_content.get_synonyms() - if not isinstance(synonyms, list): - scope.append('synonyms') - - rules = index_content.get_rules() - if not isinstance(rules, list): - scope.append('rules') - - responses = [] - - if scope: - response = self.client.copy_index(self.index_name, tmp_index_name, scope=scope) - responses.append(response) - if safe: - tmp_index.wait_task(response['taskID']) - - replicas = False - if isinstance(settings, dict): - if settings['replicas'] is not None: - replicas = settings['replicas'] - settings.pop('replicas', None) - response = tmp_index.set_settings(settings) - responses.append(response) - - if isinstance(synonyms, list): - response = tmp_index.batch_synonyms(synonyms) - responses.append(response) - - if isinstance(rules, list): - response = tmp_index.batch_rules(rules) - responses.append(response) - - batch = [] - batch_size = 1000 - count = 0 - for obj in index_content.get_objects(): - batch.append(obj) - count += 1 - - if count == batch_size: - response = tmp_index.save_objects(batch) - responses.append(response) - batch = [] - count = 0 - - if batch: - response = tmp_index.save_objects(batch) - responses.append(response) - - if safe: - for response in responses: - tmp_index.wait_task(response['taskID']) - - response = self.client.move_index(tmp_index_name, self.index_name) - responses.append(response) - - if safe: - self.wait_task(response['taskID']) - - if replicas: - response = self.set_settings({'replicas': replicas}) - responses.append(response) - if safe: - self.wait_task(response['taskID']) - - return responses - def _req(self, is_search, path, meth, request_options=None, params=None, data=None): """Perform an HTTPS request with retry logic.""" path = '%s%s' % (self._request_path, path) diff --git a/algoliasearch/index_content.py b/algoliasearch/index_content.py deleted file mode 100644 index 331e0b04b..000000000 --- a/algoliasearch/index_content.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Copyright (c) 2013 Algolia -http://www.algolia.com/ - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights lw1 -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - - -class IndexContent: - """ - Content of index. - """ - - def get_objects(self): - """ - Return index's objects. - """ - return [] - - def get_settings(self): - """ - Return index's settings. - """ - return False - - def get_synonyms(self): - """ - Return index's synonyms. - """ - return False - - def get_rules(self): - """ - Return index's rules. - """ - return False diff --git a/tests/helpers.py b/tests/helpers.py index 9ef964f19..abe909731 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -120,14 +120,6 @@ def __getattr__(self, name): return getattr(self._index, name) -def synonym_stub(objid='my-synonym'): - return { - 'objectID': objid, - 'type': 'synonym', - 'synonyms': ['San Francisco', 'SF'] - } - - def rule_stub(objid='my-rule'): return { 'objectID': objid, diff --git a/tests/test_index.py b/tests/test_index.py index cfc2d13e1..9b58c7a6e 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -6,9 +6,8 @@ from algoliasearch.client import RequestOptions, MAX_API_KEY_LENGTH from algoliasearch.helpers import AlgoliaException -from algoliasearch.index_content import IndexContent -from .helpers import Factory, rule_stub, synonym_stub +from .helpers import Factory, rule_stub def test_add_object(index): @@ -664,199 +663,3 @@ def test_batch(rw_index): assert False except AlgoliaException as e: assert 'does not exist' in str(e) - - -class ObjectsStub(IndexContent): - def get_objects(self): - return [{'objectID': 'B', 'method': 'Foo'}] - - -def test_reindex_objects(index): - obj = {'objectID': 'A', 'method': 'Bar'} - res = index.save_object(obj) - index.wait_task(res['taskID']) - - stub = ObjectsStub() - responses = index.reindex(stub) - for response in responses: - index.wait_task(response['taskID']) - res = index.search('') - assert len(res['hits']) == 1 - del res['hits'][0]['_highlightResult'] - assert stub.get_objects()[0] in res['hits'] - - -class WithListRulesStub(IndexContent): - def get_rules(self): - return [rule_stub('reindex-rule-foo')] - - -def test_reindex_with_list_rules(index): - rule = rule_stub('reindex-rule-bar') - res = index.batch_rules([rule]) - index.wait_task(res['taskID']) - - stub = WithListRulesStub() - responses = index.reindex(stub) - for response in responses: - index.wait_task(response['taskID']) - - rules = index.search_rules() - assert rules['nbHits'] == 1 - assert index.read_rule('reindex-rule-foo') == rule_stub('reindex-rule-foo') - - -class FalseRulesStub(IndexContent): - def get_rules(self): - return False - - -def test_reindex_false_rules(index): - rule = rule_stub('reindex-rule-bar') - res = index.batch_rules([rule]) - index.wait_task(res['taskID']) - - stub = FalseRulesStub() - responses = index.reindex(stub) - for response in responses: - index.wait_task(response['taskID']) - - rules = index.search_rules() - assert rules['nbHits'] == 1 - assert index.read_rule('reindex-rule-bar') == rule - - -class WithListSynonymsStub(IndexContent): - def get_synonyms(self): - return [synonym_stub('reindex-synonym-foo')] - - -def test_reindex_with_list_synonyms(index): - synonym = synonym_stub('reindex-synonym-bar') - res = index.batch_synonyms([synonym]) - index.wait_task(res['taskID']) - - stub = WithListSynonymsStub() - responses = index.reindex(stub) - for response in responses: - index.wait_task(response['taskID']) - - synonyms = index.search_synonyms('') - assert synonyms['nbHits'] == 1 - assert index.get_synonym('reindex-synonym-foo') == synonym_stub('reindex-synonym-foo') - - -class FalseSynonymsStub(IndexContent): - def get_synonyms(self): - return False - - -def test_reindex_false_synonyms(index): - synonym = synonym_stub('reindex-synonym-bar') - res = index.batch_synonyms([synonym]) - index.wait_task(res['taskID']) - - stub = FalseSynonymsStub() - responses = index.reindex(stub) - for response in responses: - index.wait_task(response['taskID']) - - synonyms = index.search_synonyms('') - assert synonyms['nbHits'] == 1 - assert index.get_synonym('reindex-synonym-bar') == synonym - - -class WithDictSettingsStub(IndexContent): - def get_settings(self): - return {'userData': 'reindex-settings-foo'} - - -def test_reindex_with_dict_settings(index): - res = index.set_settings({'userData': 'reindex-settings-bar'}) - index.wait_task(res['taskID']) - - stub = WithDictSettingsStub() - responses = index.reindex(stub) - for response in responses: - index.wait_task(response['taskID']) - - settings = index.get_settings() - assert settings['userData'] == 'reindex-settings-foo' - - -class FalseSettingsStub(IndexContent): - def get_objects(self): - return [{'objectID': 'A', 'method': 'Foo'}] - - -def test_reindex_false_synonyms(index): - res = index.set_settings({'userData': 'reindex-settings-bar'}) - index.wait_task(res['taskID']) - - stub = FalseSettingsStub() - responses = index.reindex(stub) - for response in responses: - index.wait_task(response['taskID']) - - settings = index.get_settings() - assert settings['userData'] == 'reindex-settings-bar' - - -class StubToTestSafe(IndexContent): - - def get_objects(self): - return [{'objectID': 'B', 'method': 'reindex-object-safe'}] - - def get_settings(self): - return {'userData': 'reindex-settings-safe'} - - def get_rules(self): - return [rule_stub('reindex-rule-safe')] - - def get_synonyms(self): - return [synonym_stub('reindex-synonym-safe')] - - -def test_reindex_with_safe(index): - stub = StubToTestSafe() - request_options = RequestOptions({'safe': True}) - index.reindex(stub, request_options) - - res = index.search('') - assert len(res['hits']) == 1 - del res['hits'][0]['_highlightResult'] - assert stub.get_objects()[0] in res['hits'] - - settings = index.get_settings() - assert settings['userData'] == 'reindex-settings-safe' - - synonyms = index.search_synonyms('') - assert synonyms['nbHits'] == 1 - assert index.get_synonym('reindex-synonym-safe') == synonym_stub('reindex-synonym-safe') - - rules = index.search_rules() - assert rules['nbHits'] == 1 - assert index.read_rule('reindex-rule-safe') == rule_stub('reindex-rule-safe') - - -class StubToTestReplicas(IndexContent): - - def __init__(self, index_name): - self.index_name = index_name - - def get_objects(self): - return [{'objectID': 'B', 'method': 'reindex-object-safe'}] - - def get_settings(self): - return {'replicas': [self.index_name + '-replica']} - - -def test_reindex_replicas(index): - stub = StubToTestReplicas(index.index_name) - request_options = RequestOptions({'safe': True}) - index.reindex(stub, request_options) - - # Assert 'replicas' settings still exist. - settings = index.get_settings() - assert settings['replicas'] == [index.index_name + '-replica'] - From d886cc081d938c6ccbbd42368ac6bb2b7ba7d5ae Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 19 Nov 2018 12:42:27 +0100 Subject: [PATCH 10/15] Adds symfony stub --- tests/helpers.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/helpers.py b/tests/helpers.py index abe909731..9ef964f19 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -120,6 +120,14 @@ def __getattr__(self, name): return getattr(self._index, name) +def synonym_stub(objid='my-synonym'): + return { + 'objectID': objid, + 'type': 'synonym', + 'synonyms': ['San Francisco', 'SF'] + } + + def rule_stub(objid='my-rule'): return { 'objectID': objid, From 7859b8d03fa096f8d69de01d02c78a891e5f5ca8 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 19 Nov 2018 12:42:39 +0100 Subject: [PATCH 11/15] Fixes import of non class --- algoliasearch/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/algoliasearch/__init__.py b/algoliasearch/__init__.py index 16632aa5b..1182e2a9b 100644 --- a/algoliasearch/__init__.py +++ b/algoliasearch/__init__.py @@ -25,7 +25,6 @@ from . import account_client from . import client from . import index -from . import index_content from . import helpers from . import version @@ -37,6 +36,5 @@ class algoliasearch(object): AccountClient = account_client.AccountClient Client = client.Client Index = index.Index - IndexContent = index_content.IndexContent AlgoliaException = helpers.AlgoliaException RequestOptions = client.RequestOptions From fc058a692b12fedebbfac5e6b73173b4115b44a8 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 20 Nov 2018 15:20:47 +0100 Subject: [PATCH 12/15] Tests account::copy_index following cts --- tests/conftest.py | 24 +++++++++++++ tests/helpers.py | 16 ++++++--- tests/test_account_client.py | 66 +++++++++++++----------------------- 3 files changed, 60 insertions(+), 46 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0073a6f22..023dfc351 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,6 +9,30 @@ def mcm_client(): return create_client('ALGOLIA_APP_ID_MCM', 'ALGOLIA_API_KEY_MCM') +@pytest.fixture +def client_1(): + return create_client('ALGOLIA_APPLICATION_ID_1', 'ALGOLIA_ADMIN_KEY_1') + + +@pytest.fixture +def client_2(): + return create_client('ALGOLIA_APPLICATION_ID_2', 'ALGOLIA_ADMIN_KEY_2') + + +@pytest.fixture +def index_1(client_1): + idx = create_index(client_1) + yield idx + client_1.delete_index(idx.index_name) # Tear down + + +@pytest.fixture +def index_2(client_2): + idx = create_index(client_2) + yield idx + client_2.delete_index(idx.index_name) # Tear down + + @pytest.fixture def mcm_index(mcm_client): idx = create_index(mcm_client) diff --git a/tests/helpers.py b/tests/helpers.py index 9ef964f19..a59f14755 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,5 +1,6 @@ import os import time +import datetime from random import randint from algoliasearch import algoliasearch @@ -12,7 +13,12 @@ def check_credentials(): 'ALGOLIA_API_KEY', 'ALGOLIA_SEARCH_API_KEY', 'ALGOLIA_APP_ID_MCM', - 'ALGOLIA_API_KEY_MCM' + 'ALGOLIA_API_KEY_MCM', + # CTS: + 'ALGOLIA_APPLICATION_ID_1', + 'ALGOLIA_ADMIN_KEY_1', + 'ALGOLIA_APPLICATION_ID_2', + 'ALGOLIA_ADMIN_KEY_2', ] for credential in credentials: @@ -25,12 +31,14 @@ def check_credentials(): def index_name(): - name = 'algolia-python{}'.format(randint(1, 100000)) + date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") if 'TRAVIS' in os.environ: - name = 'TRAVIS_PYTHON_{}_id-{}'.format(name, os.environ['TRAVIS_JOB_NUMBER']) + instance = os.environ['TRAVIS_BUILD_NUMBER'] + else: + instance = 'unknown' - return name + return 'python_%s_%s' % (date,instance) class Factory: diff --git a/tests/test_account_client.py b/tests/test_account_client.py index 5fae68f23..1e20bea1c 100644 --- a/tests/test_account_client.py +++ b/tests/test_account_client.py @@ -5,64 +5,46 @@ from .helpers import rule_stub, synonym_stub from .helpers import is_community -def test_copy_index_applications_must_be_different(index, ro_index): - response = index.save_object({'objectID': 'Foo', 'name': 'Bar'}) - index.wait_task(response['taskID']) +def test_copy_index_applications_must_be_different(client_1): - response = ro_index.save_object({'objectID': 'Foo', 'name': 'Bar'}) - ro_index.wait_task(response['taskID']) + index_1 = client_1.init_index('copy_index') + index_2 = client_1.init_index('copy_index_2') with pytest.raises(AlgoliaException): - AccountClient.copy_index(index, ro_index) + AccountClient.copy_index(index_1, index_2) - -@pytest.mark.skipif(is_community, - reason='Cross application methods cannot be tested by the community') -def test_copy_index_destination_must_not_exist(index, mcm_index): - response = index.save_object({'objectID': 'Foo', 'name': 'Bar'}) - index.wait_task(response['taskID']) - - response = mcm_index.save_object({'objectID': 'Foo', 'name': 'Bar'}) - mcm_index.wait_task(response['taskID']) - - with pytest.raises(AlgoliaException): - AccountClient.copy_index(index, mcm_index) - - -@pytest.mark.skipif(is_community, - reason='Cross application methods cannot be tested by the community') -def test_copy_index_copy_the_index(index, mcm_index): +def test_copy_index_copy_the_index_and_destination_must_not_exist(index_1, index_2): responses = [ - index.save_object({'objectID': 'Foo', 'name': 'Bar'}), - index.batch_rules([rule_stub('foo')]), - index.batch_synonyms([synonym_stub('foo')]), - index.set_settings({'userData': 'foo'}) + index_1.save_object({'objectID': 'one'}), + index_1.batch_rules([rule_stub('one')]), + index_1.batch_synonyms([synonym_stub('one')]), + index_1.set_settings({'searchableAttributes': ['objectID']}) ] for response in responses: - index.wait_task(response['taskID']) - - response = mcm_index.client.delete_index(mcm_index.index_name) # Make sure target don't exist. - mcm_index.wait_task(response['taskID']) + index_1.wait_task(response['taskID']) - responses = AccountClient.copy_index(index, mcm_index) + responses = AccountClient.copy_index(index_1, index_2) for response in responses: - mcm_index.wait_task(response['taskID']) + index_2.wait_task(response['taskID']) # Assert objects got copied - res = mcm_index.search('') + res = index_2.search('') assert len(res['hits']) == 1 - del res['hits'][0]['_highlightResult'] - assert res['hits'][0] == {'objectID': 'Foo', 'name': 'Bar'} + assert res['hits'][0] == {'objectID': 'one'} # Assert settings got copied - settings = mcm_index.get_settings() - assert settings['userData'] == 'foo' + settings = index_2.get_settings() + assert settings['searchableAttributes'] == ['objectID'] # Assert synonyms got copied - rule = mcm_index.read_rule('foo') - assert rule == rule_stub('foo') + rule = index_2.read_rule('one') + assert rule == rule_stub('one') # Assert synonyms got copied - synonym = mcm_index.get_synonym('foo') - assert synonym == synonym_stub('foo') + synonym = index_2.get_synonym('one') + assert synonym == synonym_stub('one') + + # Assert that copying again fails because index already exists + with pytest.raises(AlgoliaException): + AccountClient.copy_index(index_1, index_2) From 497fddb42e04f4eb8f76a675ab3440b01bd14a22 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 23 Nov 2018 11:08:31 +0100 Subject: [PATCH 13/15] Replaces TRAVIS_BUILD_NUMBER by TRAVIS_JOB_NUMBER --- tests/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helpers.py b/tests/helpers.py index a59f14755..eb1fc752b 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -34,7 +34,7 @@ def index_name(): date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") if 'TRAVIS' in os.environ: - instance = os.environ['TRAVIS_BUILD_NUMBER'] + instance = os.environ['TRAVIS_JOB_NUMBER'] else: instance = 'unknown' From b2085f9e2fcfc32bd784afd11591438b50367384 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 23 Nov 2018 14:58:10 +0100 Subject: [PATCH 14/15] Removes unused variable --- algoliasearch/account_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/algoliasearch/account_client.py b/algoliasearch/account_client.py index be498d570..375d27a4a 100644 --- a/algoliasearch/account_client.py +++ b/algoliasearch/account_client.py @@ -59,7 +59,6 @@ def copy_index(source_index, destination_index, request_options=None): # Copy settings settings = source_index.get_settings() - settings responses.append(destination_index.set_settings(settings, False, False, request_options)) # Copy synonyms From 9a27ee5e3a524474cbc247b6086e491f6decc8ef Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 23 Nov 2018 15:03:33 +0100 Subject: [PATCH 15/15] Fixes issues while merging --- algoliasearch/index.py | 2 ++ tests/test_index.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/algoliasearch/index.py b/algoliasearch/index.py index 9b31a83f9..cbde3d6f5 100644 --- a/algoliasearch/index.py +++ b/algoliasearch/index.py @@ -23,6 +23,8 @@ """ import time +import random +import string try: from urllib import urlencode diff --git a/tests/test_index.py b/tests/test_index.py index 9b58c7a6e..03e2719e6 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -7,7 +7,7 @@ from algoliasearch.client import RequestOptions, MAX_API_KEY_LENGTH from algoliasearch.helpers import AlgoliaException -from .helpers import Factory, rule_stub +from .helpers import Factory, rule_stub, synonym_stub def test_add_object(index):