From 71eb84511f38e5a3a364fbc9c45b0c7a9dcd2f55 Mon Sep 17 00:00:00 2001 From: Garrett Birkel Date: Tue, 3 Feb 2026 15:20:01 -0800 Subject: [PATCH 1/3] Assuming default of ORCID type, for id supplied to get_user. Attempting to get ORCID from returned user record if not already supplied. Commenting out warning about missing LBNLID. Support functions get_user_proposals and get_user_esafs now use ORCID instead of LBNLID. --- alshub/service.py | 81 +++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/alshub/service.py b/alshub/service.py index 150afc1..62464f9 100644 --- a/alshub/service.py +++ b/alshub/service.py @@ -57,7 +57,7 @@ class ALSHubService(UserService): def __init__(self) -> None: super().__init__() - async def get_user(self, id: str, id_type: IDType, fetch_groups=True) -> User: + async def get_user(self, id: str, id_type: IDType=IDType.orcid, fetch_groups=True) -> User: """Return a user object from ALSHub. Makes several calls to ALSHub to assemble user info, beamline membership and proposal info, which is used to populate group names. @@ -100,18 +100,26 @@ async def get_user(self, id: str, id_type: IDType, fetch_groups=True) -> User: return None user_response_obj = response.json() - user_lb_id = user_response_obj.get('LBNLID') - if not user_lb_id: - raise UserNotFound(f'user {id} not found in ALSHub or could not communicate') - info('get_user userinfo for orcid: %s lbid: %s', - id, - user_lb_id) + #user_lb_id = user_response_obj.get('LBNLID') + #if not user_lb_id: + # # This does not even warrant a warning at this point. + # logging.warning(f"no LBNLID for user {id} returned from ALSHub") + #info('get_user userinfo for orcid: %s lbid: %s', + # id, + # user_lb_id) + + orcid = None + if id_type == IDType.orcid: + orcid = id + else: + orcid = user_response_obj.get('orcid') # add staff beamlines to groups list - # if id_type == IDType.email: - beamlines = await get_staff_beamlines(alsusweb_client, id, user_response_obj['OrgEmail']) - if beamlines: - groups.update(beamlines) + if orcid: + beamlines = await get_staff_beamlines(alsusweb_client, orcid, user_response_obj['OrgEmail']) + if beamlines: + groups.update(beamlines) + if not fetch_groups: return User(**{ "uid": user_response_obj.get('LBNLID'), @@ -121,14 +129,19 @@ async def get_user(self, id: str, id_type: IDType, fetch_groups=True) -> User: "current_email": user_response_obj.get('OrgEmail'), "orcid": user_response_obj.get('orcid') }) - proposals = await get_user_proposals(alsusweb_client, user_lb_id) - if proposals: - groups.update(proposals) - - async with AsyncClient(base_url=ESAF_BASE, timeout=10.0) as esaf_client: - esafs = await get_user_esafs(esaf_client, user_lb_id) - if esafs: - groups.update(esafs) + + if not orcid: + logging.warning(f"Asked to fetch groups but could not find ORCID for user {id} returned from ALSHub") + + if orcid: + proposals = await get_user_proposals(alsusweb_client, orcid) + if proposals: + groups.update(proposals) + + async with AsyncClient(base_url=ESAF_BASE, timeout=10.0) as esaf_client: + esafs = await get_user_esafs(esaf_client, orcid) + if esafs: + groups.update(esafs) return User(**{ "uid": user_response_obj.get('LBNLID'), @@ -141,15 +154,15 @@ async def get_user(self, id: str, id_type: IDType, fetch_groups=True) -> User: }) -async def get_user_proposals(client, lbl_id): - url = f"{ALSHUB_PROPOSALBY}/?lb={lbl_id}" +async def get_user_proposals(client, orcid): + url = f"{ALSHUB_PROPOSALBY}/?or={orcid}" full_url = f"{ALSHUB_BASE}/{url}" debug('Requesting: %s', full_url) response = await client.get(url) debug('Response status: %s', response.status_code) if response.is_error: info('error getting user proposals: %s status code: %s message: %s', - lbl_id, + orcid, response.status_code, response.json()) return {} @@ -158,35 +171,35 @@ async def get_user_proposals(client, lbl_id): debug('Response body: %s', proposal_response_obj) proposals = proposal_response_obj.get('Proposals') if not proposals: - info('no proposals for lbnlid: %s', lbl_id) + info('no proposals for orcid: %s', orcid) return [] else: - info('get_user userinfo for lblid: %s proposals: %s', - lbl_id, + info('get_user userinfo for orcid: %s proposals: %s', + orcid, str(proposals)) return {proposal_id for proposal_id in proposals} -async def get_user_esafs(client, lbl_id): - url = f"{ESAF_INFO}/?lb={lbl_id}" +async def get_user_esafs(client, orcid): + url = f"{ESAF_INFO}/?or={orcid}" full_url = f"{ESAF_BASE}/{url}" debug('Requesting: %s', full_url) response = await client.get(url) debug('Response status: %s', response.status_code) if response.is_error: info('error getting user esafs: %s status code: %s message: %s', - lbl_id, + orcid, response.status_code, response.json()) else: esafs = response.json() debug('Response body: %s', esafs) if not esafs or len(esafs) == 0: - info('no proposals for lbnlid: %s', lbl_id) + info('no proposals for orcid: %s', orcid) else: - debug('get_user userinfo for lblid: %s proposals: %s', - lbl_id, + debug('get_user userinfo for orcid: %s proposals: %s', + orcid, str(esafs)) return {esaf["ProposalFriendlyId"] for esaf in esafs} @@ -201,10 +214,8 @@ async def get_staff_beamlines(ac: AsyncClient, orcid: str, email: str) -> List[s # ADMINS are a list maintained in a python to add users to groups even if they're not maintained in # ALSHub beamlines = set() - if ADMINS: - admin = ADMINS.get(email) - if admin: - beamlines.update(ADMINS.get(email)) + if email and ADMINS and (email in ADMINS): + beamlines.update(ADMINS.get(email)) if response.is_error: info(f"error asking ALHub for staff roles {orcid}") return beamlines From 34ac384997793153a4b34473c62ae931df09a1ff Mon Sep 17 00:00:00 2001 From: Garrett Birkel Date: Wed, 4 Feb 2026 12:28:56 -0800 Subject: [PATCH 2/3] Removing comment and dead code. --- alshub/service.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/alshub/service.py b/alshub/service.py index 62464f9..4fc26c4 100644 --- a/alshub/service.py +++ b/alshub/service.py @@ -72,7 +72,6 @@ async def get_user(self, id: str, id_type: IDType=IDType.orcid, fetch_groups=Tru User instance populate with info from ALSHub requests """ - user_lb_id = None groups = set() async with AsyncClient(base_url=ALSHUB_BASE, timeout=10.0) as alsusweb_client: # query for user information @@ -100,13 +99,6 @@ async def get_user(self, id: str, id_type: IDType=IDType.orcid, fetch_groups=Tru return None user_response_obj = response.json() - #user_lb_id = user_response_obj.get('LBNLID') - #if not user_lb_id: - # # This does not even warrant a warning at this point. - # logging.warning(f"no LBNLID for user {id} returned from ALSHub") - #info('get_user userinfo for orcid: %s lbid: %s', - # id, - # user_lb_id) orcid = None if id_type == IDType.orcid: From a01c1e63a8fea6662770b6d348c6a22081497e40 Mon Sep 17 00:00:00 2001 From: Garrett Birkel Date: Wed, 4 Feb 2026 12:29:29 -0800 Subject: [PATCH 3/3] 'None' is not a valid response from this method; better to throw an exception since we got an error. --- alshub/service.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/alshub/service.py b/alshub/service.py index 4fc26c4..d2a76a9 100644 --- a/alshub/service.py +++ b/alshub/service.py @@ -93,10 +93,9 @@ async def get_user(self, id: str, id_type: IDType=IDType.orcid, fetch_groups=Tru if response.status_code == 404: raise UserNotFound(f'user {id} not found in ALSHub') if response.is_error: - info('error getting user: %s status code: %s message: %s', - id, - response.status_code, response.json()) - return None + error = f"error getting user: {id} status code: {response.status_code} message: {response.json()}" + logger.error(error) + raise CommunicationError(error) user_response_obj = response.json()