11import logging
2- from contextlib import contextmanager
32from typing import Final
43
5- from aiohttp import ClientResponseError , web
4+ from aiohttp import web
65from models_library .api_schemas_invitations .invitations import (
76 ApiInvitationContent ,
87 ApiInvitationContentAndLink ,
98 ApiInvitationInputs ,
109)
1110from models_library .emails import LowerCaseEmailStr
1211from pydantic import AnyHttpUrl , TypeAdapter , ValidationError
13- from servicelib .aiohttp import status
1412
1513from ..groups .api import is_user_by_email_in_group
1614from ..products .api import Product
17- from ._client import InvitationsServiceApi , get_invitations_service_api
15+ from ._client import get_invitations_service_api
1816from .errors import (
1917 MSG_INVALID_INVITATION_URL ,
2018 MSG_INVITATION_ALREADY_USED ,
2119 InvalidInvitationError ,
22- InvitationsError ,
2320 InvitationsServiceUnavailableError ,
2421)
2522
2623_logger = logging .getLogger (__name__ )
2724
2825
29- @contextmanager
30- def _handle_exceptions_as_invitations_errors ():
31- try :
32- yield # API function calls happen
33-
34- except ClientResponseError as err :
35- # check possible errors
36- if err .status == status .HTTP_422_UNPROCESSABLE_ENTITY :
37- raise InvalidInvitationError (
38- invitations_api_response = {
39- "err" : err ,
40- "status" : err .status ,
41- "message" : err .message ,
42- "url" : err .request_info .real_url ,
43- },
44- ) from err
45-
46- assert err .status >= status .HTTP_400_BAD_REQUEST # nosec
47-
48- # any other error status code
49- raise InvitationsServiceUnavailableError (
50- client_response_error = err ,
51- ) from err
52-
53- except InvitationsError :
54- # bypass: prevents that the Exceptions handler catches this exception
55- raise
56-
57- except Exception as err :
58- raise InvitationsServiceUnavailableError (
59- unexpected_error = err ,
60- ) from err
61-
62-
6326#
6427# API plugin CALLS
6528#
@@ -81,53 +44,50 @@ async def validate_invitation_url(
8144) -> ApiInvitationContent :
8245 """Validates invitation and associated email/user and returns content upon success
8346
84- raises InvitationsError
47+ Raises:
48+ InvitationsError
49+ InvalidInvitationError:
50+ InvitationsServiceUnavailableError:
8551 """
8652 if current_product .group_id is None :
8753 raise InvitationsServiceUnavailableError (
8854 reason = "Current product is not configured for invitations"
8955 )
9056
91- invitations_service : InvitationsServiceApi = get_invitations_service_api (app = app )
92-
93- with _handle_exceptions_as_invitations_errors ():
94- try :
95- valid_url = TypeAdapter (AnyHttpUrl ).validate_python (invitation_url )
96- except ValidationError as err :
97- raise InvalidInvitationError (reason = MSG_INVALID_INVITATION_URL ) from err
98-
99- # check with service
100- invitation = await invitations_service .extract_invitation (
101- invitation_url = valid_url
57+ try :
58+ valid_url = TypeAdapter (AnyHttpUrl ).validate_python (invitation_url )
59+ except ValidationError as err :
60+ raise InvalidInvitationError (reason = MSG_INVALID_INVITATION_URL ) from err
61+
62+ # check with service
63+ invitation = await get_invitations_service_api (app = app ).extract_invitation (
64+ invitation_url = valid_url
65+ )
66+
67+ # check email
68+ if invitation .guest .lower () != guest_email .lower ():
69+ raise InvalidInvitationError (
70+ reason = "This invitation was issued for a different email"
10271 )
10372
104- # check email
105- if invitation .guest .lower () != guest_email .lower ():
106- raise InvalidInvitationError (
107- reason = "This invitation was issued for a different email"
108- )
109-
110- # check product
111- assert current_product .group_id is not None # nosec
112- if (
113- invitation .product is not None
114- and invitation .product != current_product .name
115- ):
116- raise InvalidInvitationError (
117- reason = "This invitation was issued for a different product. "
118- f"Got '{ invitation .product } ', expected '{ current_product .name } '"
119- )
120-
121- # check invitation used
122- assert invitation .product == current_product .name # nosec
123- is_user_registered_in_product : bool = await is_user_by_email_in_group (
124- app ,
125- user_email = LowerCaseEmailStr (invitation .guest ),
126- group_id = current_product .group_id ,
73+ # check product
74+ assert current_product .group_id is not None # nosec
75+ if invitation .product is not None and invitation .product != current_product .name :
76+ raise InvalidInvitationError (
77+ reason = "This invitation was issued for a different product. "
78+ f"Got '{ invitation .product } ', expected '{ current_product .name } '"
12779 )
128- if is_user_registered_in_product :
129- # NOTE: a user might be already registered but the invitation is for another product
130- raise InvalidInvitationError (reason = MSG_INVITATION_ALREADY_USED )
80+
81+ # check invitation used
82+ assert invitation .product == current_product .name # nosec
83+ is_user_registered_in_product : bool = await is_user_by_email_in_group (
84+ app ,
85+ user_email = LowerCaseEmailStr (invitation .guest ),
86+ group_id = current_product .group_id ,
87+ )
88+ if is_user_registered_in_product :
89+ # NOTE: a user might be already registered but the invitation is for another product
90+ raise InvalidInvitationError (reason = MSG_INVITATION_ALREADY_USED )
13191
13292 return invitation
13393
@@ -137,25 +97,29 @@ async def extract_invitation(
13797) -> ApiInvitationContent :
13898 """Validates invitation and returns content without checking associated user
13999
140- raises InvitationsError
100+ Raises:
101+ InvitationsError
102+ InvalidInvitationError:
103+ InvitationsServiceUnavailableError:
141104 """
142- invitations_service : InvitationsServiceApi = get_invitations_service_api (app = app )
143-
144- with _handle_exceptions_as_invitations_errors ():
145- try :
146- valid_url = TypeAdapter (AnyHttpUrl ).validate_python (invitation_url )
147- except ValidationError as err :
148- raise InvalidInvitationError (reason = MSG_INVALID_INVITATION_URL ) from err
105+ try :
106+ valid_url = TypeAdapter (AnyHttpUrl ).validate_python (invitation_url )
107+ except ValidationError as err :
108+ raise InvalidInvitationError (reason = MSG_INVALID_INVITATION_URL ) from err
149109
150- # check with service
151- return await invitations_service .extract_invitation (invitation_url = valid_url )
110+ # check with service
111+ return await get_invitations_service_api (app = app ).extract_invitation (
112+ invitation_url = valid_url
113+ )
152114
153115
154116async def generate_invitation (
155117 app : web .Application , params : ApiInvitationInputs
156118) -> ApiInvitationContentAndLink :
157- invitations_service : InvitationsServiceApi = get_invitations_service_api (app = app )
158-
159- with _handle_exceptions_as_invitations_errors ():
160- # check with service
161- return await invitations_service .generate_invitation (params )
119+ """
120+ Raises:
121+ InvitationsError
122+ InvalidInvitationError:
123+ InvitationsServiceUnavailableError:
124+ """
125+ return await get_invitations_service_api (app = app ).generate_invitation (params )
0 commit comments