@@ -185,6 +185,14 @@ class AccessyUser(DataClassJsonMixin):
185185 msisdn : Optional [MSISDN ] = None
186186
187187
188+ @dataclass
189+ class AccessyUserMembership (DataClassJsonMixin ):
190+ id : UUID
191+ userId : UUID
192+ organizationId : UUID
193+ roles : list [str ]
194+
195+
188196class AccessySession :
189197 def __init__ (self ) -> None :
190198 self .session_token : str | None = None
@@ -232,10 +240,42 @@ def get_member_id_from_accessy_id(self, accessy_id: UUID) -> Optional[int]:
232240 def get_all_members (self ) -> list [AccessyMember ]:
233241 """Get a list of all Accessy members in the ORG with GROUPS (lab and special)"""
234242
235- org_member_ids = set (item ["id" ] for item in self ._get_users_org ())
243+ def fill_in_group_affiliation_and_membership_id (members : list [AccessyMember ], group : UUID ) -> None :
244+ group_members = self ._get_users_in_access_group (group )
245+ userId_to_group_member = {m .userId : m for m in group_members }
246+ for member in members :
247+ assert member .user_id is not None
248+ membership = userId_to_group_member .get (member .user_id , None )
249+
250+ if not membership :
251+ continue
252+
253+ member .groups .add (group )
254+ if member .membership_id is None :
255+ member .membership_id = membership .id
256+ elif member .membership_id != membership .id :
257+ raise AccessyError (
258+ f"User { member .name !r} { member .user_id = } has different membership_id for different groups"
259+ f" ({ member .membership_id } != { membership .id } ). This is a bug"
260+ )
261+
262+ def fill_in_membership_id_if_missing (member : AccessyMember ) -> None :
263+ if member .membership_id is None :
264+ member .membership_id = self ._get (
265+ f"/asset/admin/user/{ member .user_id } /organization/{ self .organization_id ()} /membership"
266+ )["id" ]
267+
268+ org_members = [AccessyUser .from_dict (item ) for item in self ._get_users_org ()]
269+ members = [
270+ AccessyMember (user_id = item .id , phone = item .msisdn , name = f"{ item .firstName } { item .lastName } " )
271+ for item in org_members
272+ ]
236273
237- members = self ._user_ids_to_accessy_members (org_member_ids )
238- self ._populate_user_groups (members )
274+ for group in [ACCESSY_LABACCESS_GROUP , ACCESSY_SPECIAL_LABACCESS_GROUP ]:
275+ fill_in_group_affiliation_and_membership_id (members , group )
276+
277+ for m in members :
278+ fill_in_membership_id_if_missing (m )
239279
240280 return members
241281
@@ -470,30 +510,20 @@ def _get_user_details(self, user_id: UUID) -> Any:
470510 return self ._get (f"/org/admin/user/{ user_id } " , err_msg = "Getting user details" )
471511
472512 def _get_users_org (self ) -> list [dict ]:
473- """Get all user ID:s"""
474-
475- def is_application (user : dict ) -> bool :
476- return user .get ("msisdn" , None ) is None
513+ """Get all users"""
477514
478515 return [
479516 v
480- for v in self ._get_json_paginated (f"/asset/admin /organization/{ self .organization_id ()} /user" )
481- if not is_application ( v )
517+ for v in self ._get_json_paginated (f"/org /organization/{ self .organization_id ()} /user" )
518+ if not v [ "application" ]
482519 ]
483- # {"items":[{"id":<uuid>,"msisdn":"+46...","firstName":str,"lastName":str}, ...],"totalItems":6,"pageSize":25,"pageNumber":0,"totalPages":1}
484520
485- def _get_users_in_access_group (self , access_group_id : UUID ) -> list [dict ]:
521+ def _get_users_in_access_group (self , access_group_id : UUID ) -> list [AccessyUserMembership ]:
486522 """Get all user ID:s in a specific access group"""
487- return self ._get_json_paginated (f"/asset/admin/access-permission-group/{ access_group_id } /membership" )
488- # {"items":[{"id":<uuid>,"userId":<uuid>,"organizationId":<uuid>,"roles":[<roles>]}, ...],"totalItems":3,"pageSize":25,"pageNumber":0,"totalPages":1}
489-
490- def _get_users_lab (self ) -> list [dict ]:
491- """Get all user ID:s with lab access"""
492- return self ._get_users_in_access_group (ACCESSY_LABACCESS_GROUP )
493-
494- def _get_users_special (self ) -> list [dict ]:
495- """Get all user ID:s with special access"""
496- return self ._get_users_in_access_group (ACCESSY_SPECIAL_LABACCESS_GROUP )
523+ return [
524+ AccessyUserMembership .from_dict (d )
525+ for d in self ._get_json_paginated (f"/asset/admin/access-permission-group/{ access_group_id } /membership" )
526+ ]
497527
498528 def _get_organization_groups (self ) -> list [dict ]:
499529 """Get information about all groups"""
@@ -512,14 +542,13 @@ def _get_group_description(self, group_id: UUID) -> str:
512542 assert isinstance (name , str )
513543 return name
514544
515- def _user_ids_to_accessy_members (self , user_ids : Iterable [ UUID ] ) -> list [ AccessyMember ] :
516- """Convert a list of User ID:s to AccessyMembers """
545+ def _user_id_to_accessy_member (self , user_id : UUID ) -> AccessyMember | None :
546+ """Convert an Accessy User ID to an AccessyMember object """
517547
518548 APPLICATION_PHONE_NUMBER = object () # Sentinel phone number for applications
519549
520550 def fill_user_details (user : AccessyMember ) -> None :
521- assert user .user_id is not None
522- data = self .get_user_details (user .user_id )
551+ data = self .get_user_details (user_id )
523552
524553 # API keys do not have phone numbers, set it to sentinel object so we can filter out API keys further down
525554 if data .application :
@@ -529,60 +558,28 @@ def fill_user_details(user: AccessyMember) -> None:
529558 if data .msisdn is not None :
530559 user .phone = data .msisdn
531560 else :
532- logger .warning (f"User { user . user_id } does not have a phone number in accessy. { data = } " )
561+ logger .warning (f"User { user_id = } does not have a phone number in accessy. { data = } " )
533562 user .name = f"{ data .firstName } { data .lastName } "
534563
535564 def fill_membership_id (user : AccessyMember ) -> None :
536- data = self ._get (f"/asset/admin/user/{ user . user_id } /organization/{ self .organization_id ()} /membership" )
565+ data = self ._get (f"/asset/admin/user/{ user_id } /organization/{ self .organization_id ()} /membership" )
537566 user .membership_id = data ["id" ]
538567
539- exception_during_fetch = None
540- threads = []
541- user_ids = list (user_ids )
542- thread_count = min (4 , len (user_ids ))
543- accessy_members : List [AccessyMember ] = []
544- for i in range (thread_count ):
545- # Chunk the user_ids into equal parts for each thread
546- slice = user_ids [i ::thread_count ]
547- member_slice = [AccessyMember (user_id = uid ) for uid in slice ]
548- accessy_members .extend (member_slice )
549-
550- # Start a thread for each chunk
551- def worker (member_slice : list [AccessyMember ]) -> None :
552- nonlocal exception_during_fetch
553- try :
554- for member in member_slice :
555- fill_user_details (member )
556- fill_membership_id (member )
557- except AccessyError as e :
558- exception_during_fetch = e
559-
560- t = threading .Thread (target = worker , args = (member_slice ,))
561- threads .append (t )
562- t .start ()
563-
564- for t in threads :
565- t .join ()
566-
567- # Filter out API keys
568- accessy_members = [m for m in accessy_members if m .phone is not APPLICATION_PHONE_NUMBER ]
569-
570- if exception_during_fetch :
571- raise RuntimeError (
572- f"One or more threads failed to fetch user details. The last exception as: { exception_during_fetch } "
573- )
568+ member = AccessyMember (user_id = user_id )
569+ fill_user_details (member )
570+ fill_membership_id (member )
574571
575- return accessy_members
572+ if member .phone is APPLICATION_PHONE_NUMBER :
573+ return None
576574
577- def _populate_user_groups (self , members : List [AccessyMember ]) -> None :
578- lab_ids = set (item ["userId" ] for item in self ._get_users_lab ())
579- special_ids = set (item ["userId" ] for item in self ._get_users_special ())
575+ return member
580576
581- for m in members :
582- if m .user_id in lab_ids :
583- m .groups .add (ACCESSY_LABACCESS_GROUP )
584- if m .user_id in special_ids :
585- m .groups .add (ACCESSY_SPECIAL_LABACCESS_GROUP )
577+ def _populate_user_groups (self , members : List [AccessyMember ]) -> None :
578+ for group in [ACCESSY_LABACCESS_GROUP , ACCESSY_SPECIAL_LABACCESS_GROUP ]:
579+ user_ids_in_group = set (item .userId for item in self ._get_users_in_access_group (group ))
580+ for m in members :
581+ if m .user_id in user_ids_in_group :
582+ m .groups .add (group )
586583
587584 def get_org_user_from_phone (
588585 self , phone_number : MSISDN , users_in_org : list [dict ] | None = None
@@ -603,7 +600,7 @@ def _get_org_user_from_phone(
603600 for item in users_in_org :
604601 if item .get ("msisdn" , None ) == phone_number :
605602 user_id = item ["id" ]
606- return self ._user_ids_to_accessy_members ([ user_id ])[ 0 ]
603+ return self ._user_id_to_accessy_member ( user_id )
607604 else :
608605 return None
609606
0 commit comments