From 6585a2063cf5d808b8a16d51fa91113675e165e4 Mon Sep 17 00:00:00 2001 From: Anthony Bretaudeau Date: Wed, 22 Apr 2020 15:02:49 +0200 Subject: [PATCH 1/3] mprove user update method + add tests --- README.rst | 3 + apollo/users/__init__.py | 22 ++++- arrow/__init__.py | 2 +- arrow/commands/users/update_user.py | 9 +- docs/commands/users.rst | 5 +- setup.py | 2 +- test/__init__.py | 36 +++++++- test/user_test.py | 132 ++++++++++++++++++++++++++++ 8 files changed, 200 insertions(+), 11 deletions(-) create mode 100644 test/user_test.py diff --git a/README.rst b/README.rst index 7ae7eac1..fd3ee602 100644 --- a/README.rst +++ b/README.rst @@ -81,6 +81,9 @@ Or with the Arrow client: History ------- +- 4.2 + - Improve user update method + - Add tests for user api - 4.1 - Fix loading attributes from gff3 - Better handling of genome sequence update, with or without the no_reload_sequences option diff --git a/apollo/users/__init__.py b/apollo/users/__init__.py index 3a21cdc8..0b28261d 100644 --- a/apollo/users/__init__.py +++ b/apollo/users/__init__.py @@ -219,7 +219,7 @@ def delete_user(self, user): """ return self.post('deleteUser', {'userToDelete': user}) - def update_user(self, email, first_name, last_name, password, metadata={}): + def update_user(self, email, first_name, last_name, password=None, metadata={}, new_email=None): """ Update an existing user @@ -233,11 +233,14 @@ def update_user(self, email, first_name, last_name, password, metadata={}): :param last_name: User's last name :type password: str - :param password: User's password + :param password: User's password (omit to keep untouched) :type metadata: dict :param metadata: User metadata + :type new_email: str + :param new_email: User's new email (if you want to change it) + :rtype: dict :return: a dictionary containing user information """ @@ -245,9 +248,22 @@ def update_user(self, email, first_name, last_name, password, metadata={}): 'email': email, 'firstName': first_name, 'lastName': last_name, - 'newPassword': password, 'metadata': metadata, } + + if password: + data['newPassword'] = password + + # If updating the email, we need to give apollo a userId + if new_email: + user = self.show_user(email) + if not isinstance(user, list): + user = [user] + user = self._assert_user(user) + + data['email'] = new_email + data['userId'] = user['userId'] + response = self.post('updateUser', data) return self._handle_empty(email, response) diff --git a/arrow/__init__.py b/arrow/__init__.py index 0a04d1bf..feecdb9d 100644 --- a/arrow/__init__.py +++ b/arrow/__init__.py @@ -1 +1 @@ -__version__ = '4.1' +__version__ = '4.2' diff --git a/arrow/commands/users/update_user.py b/arrow/commands/users/update_user.py index 7881650d..3d04cca0 100644 --- a/arrow/commands/users/update_user.py +++ b/arrow/commands/users/update_user.py @@ -13,14 +13,19 @@ help="User metadata", type=str ) +@click.option( + "--new_email", + help="User's new email (if you want to change it)", + type=str +) @pass_context @custom_exception @dict_output -def cli(ctx, email, first_name, last_name, password, metadata={}): +def cli(ctx, email, first_name, last_name, password, metadata={}, new_email=""): """Update an existing user Output: a dictionary containing user information """ - return ctx.gi.users.update_user(email, first_name, last_name, password, metadata=metadata) + return ctx.gi.users.update_user(email, first_name, last_name, password, metadata=metadata, new_email=new_email) diff --git a/docs/commands/users.rst b/docs/commands/users.rst index 0fef1f89..ae0e99ea 100644 --- a/docs/commands/users.rst +++ b/docs/commands/users.rst @@ -286,6 +286,7 @@ Update an existing user **Options**:: - --metadata TEXT User metadata - -h, --help Show this message and exit. + --metadata TEXT User metadata + --new_email TEXT User's new email (if you want to change it) + -h, --help Show this message and exit. diff --git a/setup.py b/setup.py index a2a4cb5d..bf254aed 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ setup( name="apollo", - version='4.1', + version='4.2', description="Apollo API library", long_description=readme, author="Helena Rasche;Anthony Bretaudeau", diff --git a/test/__init__.py b/test/__init__.py index 12751726..2c71a37b 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -17,9 +17,11 @@ def waitOrgDeleted(self, org_id): """ org_info = wa.organisms.show_organism(org_id) - if 'directory' in org_info: + tries = 1 + while 'directory' in org_info and tries < 10: time.sleep(1) org_info = wa.organisms.show_organism(org_id) + tries += 1 return org_info @@ -29,8 +31,38 @@ def waitOrgCreated(self, org_id): """ org_info = wa.organisms.show_organism(org_id) - if 'directory' not in org_info: + tries = 1 + while 'directory' not in org_info and tries < 10: time.sleep(1) org_info = wa.organisms.show_organism(org_id) + tries += 1 return org_info + + def waitUserDeleted(self, user_id): + """ + Wait for an user to be really deleted from Apollo + """ + + user_info = wa.users.show_user(user_id) + tries = 1 + while 'userId' in user_info and tries < 10: + time.sleep(1) + user_info = wa.users.show_user(user_id) + tries += 1 + + return user_info + + def waitUserCreated(self, user_id): + """ + Wait for an user to be really created from Apollo + """ + + user_info = wa.users.show_user(user_id) + tries = 1 + while 'userId' not in user_info and tries < 10: + time.sleep(1) + user_info = wa.users.show_user(user_id) + tries += 1 + + return user_info diff --git a/test/user_test.py b/test/user_test.py new file mode 100644 index 00000000..811ce85c --- /dev/null +++ b/test/user_test.py @@ -0,0 +1,132 @@ +import time + +from . import ApolloTestCase, wa + + +class UserTest(ApolloTestCase): + + def test_get_users(self): + + users = wa.users.get_users() + + # We at least have admin + junior + temp from setup + assert len(users) >= 3 + + first_user = users[0] + + assert 'firstName' in first_user + assert 'lastName' in first_user + assert 'inactive' in first_user + assert 'role' in first_user + assert 'availableGroups' in first_user + assert 'userCount' in first_user + assert 'searchName' in first_user + assert 'groups' in first_user + assert 'userId' in first_user + assert 'organismPermissions' in first_user + assert 'username' in first_user + + def test_show_user(self): + + users = wa.users.get_users() + + user_id = users[0]['userId'] + + user_info = wa.users.show_user(user_id) + + # userCount changes depending on what was requested to Apollo + del user_info['userCount'] + del users[0]['userCount'] + + assert user_info == users[0] + + def test_create_user(self): + + meta = {"bla": "bli"} + res = wa.users.create_user("trash@bx.psu.edu", 'Poutrelle', 'Lapinou', 'superpassword', role="user", metadata=meta) + self.waitUserCreated(res['userId']) + + res = wa.users.show_user('trash@bx.psu.edu') + + assert res['username'] == 'trash@bx.psu.edu' + assert res['firstName'] == 'Poutrelle' + assert res['lastName'] == 'Lapinou' + assert res['role'] == 'USER' + + def test_delete_user(self): + + user_info = wa.users.create_user("trash@bx.psu.edu", 'Tempxx', 'oraryxx', 'coolpasswordxx', role="user") + self.waitUserCreated(user_info['userId']) + + wa.users.delete_user(user_info['username']) + self.waitUserDeleted(user_info['userId']) + + users = wa.users.get_users() + + for user in users: + assert user['username'] != 'trash@bx.psu.edu' + + def test_update_user(self): + + user_info = wa.users.create_user("trash@bx.psu.edu", 'Tempxx', 'oraryxx', 'coolpasswordxx', role="user") + self.waitUserCreated(user_info['userId']) + + wa.users.update_user(user_info['username'], 'firstname2', 'lastname2') + + time.sleep(3) + res = wa.users.show_user('trash@bx.psu.edu') + + assert res['username'] == 'trash@bx.psu.edu' + assert res['firstName'] == 'firstname2' + assert res['lastName'] == 'lastname2' + assert res['role'] == 'USER' + + def test_update_user_email(self): + + user_info = wa.users.create_user("trash@bx.psu.edu", 'Tempxx', 'oraryxx', 'coolpasswordxx', role="user") + self.waitUserCreated(user_info['userId']) + + wa.users.update_user(user_info['username'], 'firstname2', 'lastname2', new_email='updated@bx.psu.edu') + + time.sleep(3) + res = wa.users.show_user('updated@bx.psu.edu') + + assert res['username'] == 'updated@bx.psu.edu' + assert res['firstName'] == 'firstname2' + assert res['lastName'] == 'lastname2' + assert res['role'] == 'USER' + + def setUp(self): + # Make sure the user is not already there + temp_user_info = wa.users.show_user('test_temp@bx.psu.edu') + if 'username' in temp_user_info: + wa.users.delete_user(temp_user_info['username']) + self.waitUserDeleted(temp_user_info['userId']) + + temp_user_info = wa.users.show_user('trash@bx.psu.edu') + if 'username' in temp_user_info: + wa.users.delete_user(temp_user_info['username']) + self.waitUserDeleted(temp_user_info['userId']) + + temp_user_info = wa.users.show_user('updated@bx.psu.edu') + if 'username' in temp_user_info: + wa.users.delete_user(temp_user_info['username']) + self.waitUserDeleted(temp_user_info['userId']) + + wa.users.create_user("test_temp@bx.psu.edu", 'Temp', 'orary', 'coolpassword', role="user") + + def tearDown(self): + temp_user_info = wa.users.show_user('test_temp@bx.psu.edu') + if temp_user_info and 'username' in temp_user_info: + wa.users.delete_user(temp_user_info['username']) + self.waitUserDeleted(temp_user_info['userId']) + + temp_user_info = wa.users.show_user('trash@bx.psu.edu') + if 'username' in temp_user_info: + wa.users.delete_user(temp_user_info['username']) + self.waitUserDeleted(temp_user_info['userId']) + + temp_user_info = wa.users.show_user('updated@bx.psu.edu') + if 'username' in temp_user_info: + wa.users.delete_user(temp_user_info['username']) + self.waitUserDeleted(temp_user_info['userId']) From 5871a787c54ec5d8b4578845306fcbf19db152e6 Mon Sep 17 00:00:00 2001 From: Anthony Bretaudeau Date: Wed, 22 Apr 2020 15:03:25 +0200 Subject: [PATCH 2/3] allow leaving password unchanged --- arrow/commands/users/update_user.py | 10 +++++++--- docs/commands/users.rst | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arrow/commands/users/update_user.py b/arrow/commands/users/update_user.py index 3d04cca0..8f33a403 100644 --- a/arrow/commands/users/update_user.py +++ b/arrow/commands/users/update_user.py @@ -7,7 +7,11 @@ @click.argument("email", type=str) @click.argument("first_name", type=str) @click.argument("last_name", type=str) -@click.argument("password", type=str) +@click.option( + "--password", + help="User's password (omit to keep untouched)", + type=str +) @click.option( "--metadata", help="User metadata", @@ -21,11 +25,11 @@ @pass_context @custom_exception @dict_output -def cli(ctx, email, first_name, last_name, password, metadata={}, new_email=""): +def cli(ctx, email, first_name, last_name, password="", metadata={}, new_email=""): """Update an existing user Output: a dictionary containing user information """ - return ctx.gi.users.update_user(email, first_name, last_name, password, metadata=metadata, new_email=new_email) + return ctx.gi.users.update_user(email, first_name, last_name, password=password, metadata=metadata, new_email=new_email) diff --git a/docs/commands/users.rst b/docs/commands/users.rst index ae0e99ea..db247bcf 100644 --- a/docs/commands/users.rst +++ b/docs/commands/users.rst @@ -271,7 +271,7 @@ Update the permissions of a user on a specified organism **Usage**:: - arrow users update_user [OPTIONS] EMAIL FIRST_NAME LAST_NAME PASSWORD + arrow users update_user [OPTIONS] EMAIL FIRST_NAME LAST_NAME **Help** @@ -286,6 +286,7 @@ Update an existing user **Options**:: + --password TEXT User's password (omit to keep untouched) --metadata TEXT User metadata --new_email TEXT User's new email (if you want to change it) -h, --help Show this message and exit. From 2e4e5498862cc31cd3878a71de30db1411f3a7df Mon Sep 17 00:00:00 2001 From: Anthony Bretaudeau Date: Wed, 22 Apr 2020 15:17:51 +0200 Subject: [PATCH 3/3] cachetools 4 drops support for python2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 30ead024..a0fb6ccb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ requests biopython -cachetools +cachetools<4 click>=6.7 wrapt pyyaml