33from datetime import datetime , timedelta , UTC
44from typing import Final , Iterator , Optional , Dict
55
6+ from jose .exceptions import ExpiredSignatureError , JWTError
7+
68from app_config import AppConfig
79from bss .adapters import BSSAdapter
810from bss .dbs import TiedKeyValue
2426 UserEventGroup ,
2527 UserEventType ,
2628)
27- from jose .exceptions import ExpiredSignatureError , JWTError
2829from localization import get_translation_func
2930from report_error import WebTritErrorException
30-
3131from .api import AccountAPI , AdminAPI
3232from .config import Settings
3333from .serializer import Serializer
@@ -171,7 +171,7 @@ def generate_otp(self, user: UserInfo) -> OTPCreateResponse:
171171 raise WebTritErrorException (500 , "Unknown error" , code = "external_api_issue" )
172172
173173 otp_id : str = generate_otp_id ()
174- self ._cached_otp_ids [otp_id ] = i_account
174+ self ._cached_otp_ids [otp_id ] = i_account , user . user_id
175175
176176 env_info = self ._admin_api .get_env_info ()
177177
@@ -204,12 +204,12 @@ def validate_otp(self, otp: OTPVerifyRequest) -> SessionInfo:
204204 # We need the otp_id only for storing the i_account.
205205 otp_id = safely_extract_scalar_value (otp .otp_id )
206206
207- i_account : int = self ._cached_otp_ids .get (otp_id )
207+ ( i_account , user_ref ) = self ._cached_otp_ids .get (otp_id , ( None , None ) )
208208 if not i_account :
209209 raise WebTritErrorException (status_code = 404 , error_message = f"Incorrect OTP code: { otp .code } " )
210210
211211 data : dict = self ._admin_api .verify_otp (otp_token = otp .code )
212- if str ( i_account ) not in self ._otp_settings .IGNORE_ACCOUNTS and not data ["success" ]:
212+ if user_ref not in self ._otp_settings .IGNORE_ACCOUNTS and not data ["success" ]:
213213 raise WebTritErrorException (status_code = 404 , error_message = f"Incorrect OTP code: { otp .code } " )
214214
215215 self ._cached_otp_ids .pop (otp_id )
@@ -407,7 +407,7 @@ def retrieve_contacts(self, session: SessionInfo, user: UserInfo) -> list[Contac
407407 type .value for type in
408408 self ._portaswitch_settings .CONTACTS_SELECTING_EXTENSION_TYPES
409409 }
410- accounts = self ._admin_api . get_account_list (i_customer )[ "account_list" ]
410+ accounts = self ._get_all_accounts_by_customer (i_customer )
411411 account_to_aliases = {account ["i_account" ]: account .get ("alias_list" , []) for account in accounts }
412412 extensions = self ._admin_api .get_extensions_list (i_customer )["extensions_list" ]
413413
@@ -416,7 +416,7 @@ def retrieve_contacts(self, session: SessionInfo, user: UserInfo) -> list[Contac
416416 aliases = [str (alias ) for alias in account_to_aliases .get (ext .get ("i_account" ), [])]
417417 contacts .append (Serializer .get_contact_info_by_extension (ext , aliases , i_account ))
418418 case PortaSwitchContactsSelectingMode .ACCOUNTS :
419- accounts = self ._admin_api . get_account_list (i_customer )[ "account_list" ]
419+ accounts = self ._get_all_accounts_by_customer (i_customer )
420420
421421 for account in accounts :
422422 if (status := account .get ("status" )) and status == "blocked" :
@@ -833,7 +833,7 @@ def _get_number_to_customer_accounts_map(self) -> dict[str, dict]:
833833 # Retrieve accounts for a pre-defined list of customers to map them to phonebook records
834834 with ThreadPoolExecutor (max_workers = 10 ) as executor :
835835 future_to_customer_id = {
836- executor .submit (lambda cid : self ._admin_api . get_account_list (int (cid ))[ "account_list" ] ,
836+ executor .submit (lambda cid : self ._get_all_accounts_by_customer (int (cid )),
837837 cid ): cid
838838 for cid in self ._portaswitch_settings .CONTACTS_SELECTING_CUSTOMER_IDS
839839 }
@@ -848,3 +848,26 @@ def _get_number_to_customer_accounts_map(self) -> dict[str, dict]:
848848 f"Error fetching accounts for customer { future_to_customer_id [future ]} : { e } " )
849849
850850 return number_to_accounts
851+
852+ def _get_all_accounts_by_customer (self , i_customer : int ) -> list [dict ]:
853+ """Fetch all accounts for a customer using pagination with offset until total is reached."""
854+ all_accounts : list [dict ] = []
855+ offset = 0
856+ limit = 1000
857+
858+ while True :
859+ resp = self ._admin_api .get_account_list (i_customer , limit , offset )
860+ page = resp .get ("account_list" , []) if isinstance (resp , dict ) else []
861+ total = resp .get ("total" ) if isinstance (resp , dict ) else None
862+
863+ all_accounts .extend (page )
864+
865+ # Stop if we've reached the total or the page is shorter than limit
866+ if total is not None and len (all_accounts ) >= int (total ):
867+ break
868+ if len (page ) < limit :
869+ break
870+
871+ offset += limit
872+
873+ return all_accounts
0 commit comments