From 1348d7cba89f55a636c5936066b3935366be51c8 Mon Sep 17 00:00:00 2001 From: Hannah Eslinger Date: Wed, 6 Mar 2024 12:48:50 -0700 Subject: [PATCH] Allow change of ali (#4552) * Allow change of ali * update tests * fix typo * chain user updates * fix test * precommit * update tests --------- Co-authored-by: Ross Perry Co-authored-by: Katherine Fleming <2205659+kflemin@users.noreply.github.com> --- .../seed/js/controllers/members_controller.js | 102 +++++++++++++++--- .../seed/js/services/organization_service.js | 12 +++ seed/static/seed/partials/members.html | 59 ++++++++-- .../seed/tests/members_controller.spec.js | 2 +- seed/tests/test_account_views.py | 26 +++++ seed/views/v3/organization_users.py | 1 + seed/views/v3/users.py | 38 +++++++ 7 files changed, 217 insertions(+), 23 deletions(-) diff --git a/seed/static/seed/js/controllers/members_controller.js b/seed/static/seed/js/controllers/members_controller.js index 33cdf42159..ef60ef3415 100644 --- a/seed/static/seed/js/controllers/members_controller.js +++ b/seed/static/seed/js/controllers/members_controller.js @@ -13,8 +13,9 @@ angular.module('BE.seed.controller.members', []).controller('members_controller' 'user_profile_payload', 'access_level_tree', 'urls', + 'Notification', // eslint-disable-next-line func-names - function ($scope, $uibModal, users_payload, organization_payload, auth_payload, auth_service, organization_service, user_profile_payload, access_level_tree, urls) { + function ($scope, $uibModal, users_payload, organization_payload, auth_payload, auth_service, organization_service, user_profile_payload, access_level_tree, urls, Notification) { $scope.ownerRoles = ['owner', 'member', 'viewer']; $scope.memberRoles = ['member', 'viewer']; $scope.users = users_payload.users; @@ -22,6 +23,91 @@ angular.module('BE.seed.controller.members', []).controller('members_controller' $scope.filter_params = {}; $scope.auth = auth_payload.auth; $scope.user_profile = user_profile_payload; + $scope.access_level_names = access_level_tree.access_level_names; + $scope.user_id_being_edited = null; + $scope.user_edits = {}; + + /* Build out access_level_instances_by_depth recursively */ + const access_level_instances_by_depth = {}; + const calculate_access_level_instances_by_depth = (tree, depth = 1) => { + if (tree === undefined) return; + if (access_level_instances_by_depth[depth] === undefined) access_level_instances_by_depth[depth] = []; + for (const ali of tree) { + access_level_instances_by_depth[depth].push({ id: ali.id, name: ali.data.name }); + calculate_access_level_instances_by_depth(ali.children, depth + 1); + } + }; + calculate_access_level_instances_by_depth(access_level_tree.access_level_tree, 0); + + // Result of clicking "edit" + $scope.begin_user_edits = (user) => { + // set edited user and default edits + $scope.user_id_being_edited = user.user_id; + $scope.user_edits = { + "access_level_instance": { + "id": user.access_level_instance_id, + "name": user.access_level_instance_name + }, + "access_level": user.access_level, + "role": user.role, + }; + + // the the possible access_level_instances at that level + access_level_idx = $scope.access_level_names.findIndex(x => x == $scope.user_edits["access_level"]); + $scope.access_level_instances = access_level_instances_by_depth[access_level_idx]; + } + + // Result of pick a new al. sets ali options. + $scope.change_access_level_instance_options = () => { + access_level_idx = $scope.access_level_names.findIndex(x => x == $scope.user_edits["access_level"]); + $scope.access_level_instances = access_level_instances_by_depth[access_level_idx]; + $scope.user_edits["access_level_instance"] = null; + } + + // user user edits + $scope.save_user_edits = () => { + if ($scope.user_edits["access_level_instance"] === null){ + Notification.error('Must select an ali.'); + return; + } + if($scope.user_edits.role == "owner" && $scope.user_edits.access_level != $scope.access_level_names[0]){ + Notification.error('Owners must be in the root.'); + return; + } + + // update user + user = $scope.users.find(x => x.user_id == $scope.user_id_being_edited); + $scope.update_user(user, $scope.user_edits) + + user.role = $scope.user_edits.role + user.access_level_instance_id = $scope.user_edits.access_level_instance.id + user.access_level_instance_name = $scope.user_edits.access_level_instance.name + user.access_level = $scope.user_edits.access_level + + // reset user edits + $scope.user_id_being_edited = null; + $scope.user_edits = {}; + } + /** + * update_user: updates a users role and access level instance + */ + $scope.update_user = (user, user_edits) => { + // 1. update role + // 2. update ali + organization_service.update_role(user.user_id, $scope.org.id, user_edits.role) + .then(() => { + refreshRoleStatus(); + return organization_service.update_ali(user.user_id, $scope.org.id, user_edits.access_level_instance.id) + }) + .catch((data) => { + $scope.$emit('app_error', data); + }) + } + + $scope.cancel_user_edits = () => { + $scope.user_id_being_edited = null; + $scope.user_edits = {}; + } /** * remove_member: removes a user from the org @@ -42,20 +128,6 @@ angular.module('BE.seed.controller.members', []).controller('members_controller' }); }; - /** - * saves the changed role for the user - * @param {obj} user - */ - $scope.update_role = (user) => { - organization_service - .update_role(user.user_id, $scope.org.id, user.role) - .then((data) => { - refreshRoleStatus(); - }) - .catch((data) => { - $scope.$emit('app_error', data); - }); - }; /** * new_member_modal open an AngularUI modal to add/invite a new member diff --git a/seed/static/seed/js/services/organization_service.js b/seed/static/seed/js/services/organization_service.js index c8608290e0..93528c4d8a 100644 --- a/seed/static/seed/js/services/organization_service.js +++ b/seed/static/seed/js/services/organization_service.js @@ -91,6 +91,18 @@ angular.module('BE.seed.service.organization', []).factory('organization_service ) .then((response) => response.data); + organization_factory.update_ali = (user_id, org_id, ali_id) => $http + .put( + `/api/v3/users/${user_id}/access_level_instance/`, + { + "access_level_instance_id": ali_id, + }, + { + params: { organization_id: org_id } + } + ) + .then((response) => response.data); + /** * saves the organization settings * @param {obj} org an organization with fields to share between sub-orgs diff --git a/seed/static/seed/partials/members.html b/seed/static/seed/partials/members.html index 257b8ec06d..8064e13eb8 100644 --- a/seed/static/seed/partials/members.html +++ b/seed/static/seed/partials/members.html @@ -67,21 +67,66 @@

{$:: 'Members' | translate $}

{$:: u.name $} {$:: u.email $} - {$:: u.access_level $} - {$:: u.access_level_instance_name $} -
+
{$:: u.access_level $}
+ + + +
{$:: u.access_level_instance_name $}
+ + +
{$:: u.role | translate $}
+
- - - {$:: u.role | translate $} + + + {$:: u.role | translate $}
- Remove + + + + diff --git a/seed/static/seed/tests/members_controller.spec.js b/seed/static/seed/tests/members_controller.spec.js index 005ff0a1cb..d6bb386ae8 100644 --- a/seed/static/seed/tests/members_controller.spec.js +++ b/seed/static/seed/tests/members_controller.spec.js @@ -108,7 +108,7 @@ describe('controller: members_controller', () => { // act ctrl_scope.$digest(); - ctrl_scope.update_role({ user_id: 2, role: 'viewer' }); + ctrl_scope.update_user({ user_id: 2 }, { role: 'viewer' }); // assertions expect(mock_organization_service.update_role).toHaveBeenCalledWith(2, 4, 'viewer'); diff --git a/seed/tests/test_account_views.py b/seed/tests/test_account_views.py index 6631b68e50..2480b30811 100644 --- a/seed/tests/test_account_views.py +++ b/seed/tests/test_account_views.py @@ -455,6 +455,32 @@ def test_update_role(self): }) self.assertEqual(ou.role_level, ROLE_MEMBER) + def test_update_access_level_instance(self): + # Setup + u = User.objects.create(username='b@b.com', email='b@be.com') + self.org.add_member(u, role=ROLE_VIEWER, access_level_instance_id=self.org.root.id) + org_user = OrganizationUser.objects.get(user=u) + + self.org.access_level_names = ["root", "child"] + self.org.save() + child_ali = self.org.add_new_access_level_instance(self.org.root.id, "child") + + # Action + resp = self.client.put( + reverse_lazy("api:v3:user-access-level-instance", args=[u.id]) + '?organization_id=' + str( + self.org.id), + data=json.dumps( + { + 'access_level_instance_id': child_ali.id + } + ), + content_type='application/json', + ) + + # Assertion + assert resp.status_code == 200 + assert OrganizationUser.objects.get(pk=org_user.pk).access_level_instance_id == child_ali.id + def test_allowed_to_update_role_if_not_last_owner(self): u = User.objects.create(username='b@b.com', email='b@be.com') self.org.add_member(u, role=ROLE_OWNER, access_level_instance_id=self.org.root.id) diff --git a/seed/views/v3/organization_users.py b/seed/views/v3/organization_users.py index 8ad5aea5ce..472344408e 100644 --- a/seed/views/v3/organization_users.py +++ b/seed/views/v3/organization_users.py @@ -52,6 +52,7 @@ def list(self, request, organization_pk): 'number_of_orgs': user_orgs, 'user_id': user.pk, 'role': get_js_role(u.role_level), + 'access_level_instance_id': u.access_level_instance.id, 'access_level_instance_name': u.access_level_instance.name, 'access_level': org.access_level_names[u.access_level_instance.depth - 1], }) diff --git a/seed/views/v3/users.py b/seed/views/v3/users.py index df1c9a390c..b303ff15b0 100644 --- a/seed/views/v3/users.py +++ b/seed/views/v3/users.py @@ -322,6 +322,44 @@ def role(self, request, pk=None): return JsonResponse({'status': 'success'}) + @api_endpoint_class + @ajax_request_class + @has_perm_class('requires_owner') + @action(detail=True, methods=['PUT']) + def access_level_instance(self, request, pk=None): + user_id = int(pk) + organization_id = self.get_organization(request) + + # get user + try: + user = OrganizationUser.objects.get(user_id=user_id, organization_id=organization_id) + except OrganizationUser.DoesNotExist: + return JsonResponse({ + 'status': 'error', + 'message': 'No such user' + }, status=status.HTTP_400_BAD_REQUEST) + + # get ali + access_level_instance_id = request.data.get("access_level_instance_id") + if access_level_instance_id is None: + return JsonResponse({ + 'status': 'error', + 'message': 'Must be an `access_level_instance_id`' + }, status=status.HTTP_400_BAD_REQUEST) + try: + access_level_instance = AccessLevelInstance.objects.get(organization_id=organization_id, id=access_level_instance_id) + except AccessLevelInstance.DoesNotExist: + return JsonResponse({ + 'status': 'error', + 'message': 'No such access_level_instance' + }, status=status.HTTP_400_BAD_REQUEST) + + # set user ali + user.access_level_instance = access_level_instance + user.save() + + return JsonResponse({'status': 'success'}) + @swagger_auto_schema( responses={ 200: user_response_schema,