Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `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)
Expand Down
2 changes: 2 additions & 0 deletions algoliasearch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
THE SOFTWARE.
"""

from . import account_client
from . import client
from . import index
from . import helpers
Expand All @@ -32,6 +33,7 @@
class algoliasearch(object):
VERSION = version.VERSION

AccountClient = account_client.AccountClient
Client = client.Client
Index = index.Index
AlgoliaException = helpers.AlgoliaException
Expand Down
91 changes: 91 additions & 0 deletions algoliasearch/account_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# -*- 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


class AccountClient:
"""
The Account Client
"""

@staticmethod
def copy_index(source_index, destination_index, request_options=None):
"""
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:
pass
else:
raise AlgoliaException(
'Destination index already exists. Please delete it before copying index across applications.')

responses = []

# Copy settings
settings = source_index.get_settings()
responses.append(destination_index.set_settings(settings, False, False, request_options))

# Copy synonyms
synonyms = list(source_index.iter_synonyms())
responses.append(destination_index.batch_synonyms(synonyms, False, False, False, request_options))

# Copy rules
rules = list(source_index.iter_rules())
responses.append(destination_index.batch_rules(rules, False, False, request_options))

# Copy 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, request_options)
responses.append(response)
batch = []
count = 0

if batch:
response = destination_index.save_objects(batch, request_options)
responses.append(response)

return responses
30 changes: 30 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,36 @@ 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)
yield idx
mcm_client.delete_index(idx.index_name) # Tear down

@pytest.fixture
def client():
return create_client()
Expand Down
16 changes: 12 additions & 4 deletions tests/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import time
import datetime
from random import randint

from algoliasearch import algoliasearch
Expand All @@ -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:
Expand All @@ -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_JOB_NUMBER']
else:
instance = 'unknown'

return name
return 'python_%s_%s' % (date,instance)


class Factory:
Expand Down
50 changes: 50 additions & 0 deletions tests/test_account_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pytest

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(client_1):

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_1, index_2)

def test_copy_index_copy_the_index_and_destination_must_not_exist(index_1, index_2):
responses = [
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_1.wait_task(response['taskID'])

responses = AccountClient.copy_index(index_1, index_2)
for response in responses:
index_2.wait_task(response['taskID'])

# Assert objects got copied
res = index_2.search('')
assert len(res['hits']) == 1
assert res['hits'][0] == {'objectID': 'one'}

# Assert settings got copied
settings = index_2.get_settings()
assert settings['searchableAttributes'] == ['objectID']

# Assert synonyms got copied
rule = index_2.read_rule('one')
assert rule == rule_stub('one')

# Assert synonyms got copied
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)