from requests.exceptions import ConnectTimeout, ReadTimeout from tenacity import retry, stop_after_attempt from tenacity.wait import wait_exponential from src.config.objects import LastFetchDatetime, TargetObject from src.error.exceptions import SalesforceAPIException from src.salesforce.salesforce_api import SalesforceApiClient from src.salesforce.soql_builder import SOQLBuilder from src.system_var.constants import FETCH_JP_NAME from src.system_var.environments import ( CRM_AUTH_TIMEOUT, CRM_FETCH_RECORD_MAX_RETRY_ATTEMPT, CRM_FETCH_RECORD_RETRY_INTERVAL, CRM_FETCH_RECORD_RETRY_MAX_INTERVAL, CRM_FETCH_RECORD_RETRY_MIN_INTERVAL, CRM_FETCH_RECORD_TIMEOUT, CRM_GET_RECORD_COUNT_MAX_RETRY_ATTEMPT, CRM_GET_RECORD_COUNT_RETRY_INTERVAL, CRM_GET_RECORD_COUNT_RETRY_MAX_INTERVAL, CRM_GET_RECORD_COUNT_RETRY_MIN_INTERVAL, CRM_GET_RECORD_COUNT_TIMEOUT) from src.util.counter_object import CounterObject from src.util.logger import logger_instance as logger def fetch_crm_data_process(target_object: TargetObject, last_fetch_datetime: LastFetchDatetime): """CRMデータ取得処理 Args: target_object (TargetObject): 取得対象オブジェクト情報インスタンス last_fetch_datetime (LastFetchDatetime): データ取得期間設定インスタンス Raises: SalesforceAPIException: SalesforceのAPI実行失敗が発生した場合 Returns: crm_data_response: Salesforceオブジェクトデータ """ # ① CRMデータ取得処理開始ログを出力する logger.info( f'I-FETCH-01 [{target_object.object_name}] のCRMからのデータ取得処理を開始します') target_object_name = target_object.object_name # リトライ回数判定用のカウンタオブジェクトを生成 # @retryデコレータを利用した関数のリトライ処理で、基本データ型だとリトライ回数をカウントすることができないため、オブジェクト化する count_counter = CounterObject(1) data_counter = CounterObject(1) try: # ② 取得対象オブジェクトの取得期間内のレコード件数を取得する logger.info(f'I-FETCH-02 [{target_object_name}] の件数取得を開始します') soql_builder = SOQLBuilder(target_object, last_fetch_datetime) count_soql = soql_builder.create_count_soql() record_count = fetch_record_count_retry(count_soql, target_object_name, count_counter) logger.info(f'I-FETCH-03 [{target_object_name}] の件数:[{record_count}]') except Exception as e: raise SalesforceAPIException( 'E-FETCH-01', FETCH_JP_NAME, f'[{target_object_name}] の件数取得に失敗しました エラー内容:[{e}]') try: # ③ 取得対象オブジェクトのレコードを取得する logger.info(f'I-FETCH-04 [{target_object_name}] のレコード取得を開始します') fetch_soql = soql_builder.create_fetch_soql() crm_data_response = fetch_sf_data_retry(fetch_soql, target_object_name, data_counter) logger.info(f'I-FETCH-05 [{target_object_name}] のレコード取得が成功しました') except Exception as e: raise SalesforceAPIException( 'E-FETCH-02', FETCH_JP_NAME, f'[{target_object_name}] のレコード取得に失敗しました エラー内容:[{e}]') # ④ CRMデータ取得処理終了ログを出力する logger.info(f'I-FETCH-06 [{target_object_name}] のCRMからのデータ取得処理を終了します') # ⑤ 次の処理へ移行する return crm_data_response @retry( wait=wait_exponential(multiplier=CRM_GET_RECORD_COUNT_RETRY_INTERVAL, min=CRM_GET_RECORD_COUNT_RETRY_MIN_INTERVAL, max=CRM_GET_RECORD_COUNT_RETRY_MAX_INTERVAL), stop=stop_after_attempt(CRM_GET_RECORD_COUNT_MAX_RETRY_ATTEMPT)) def fetch_record_count_retry(soql: str, target_object_name: str, count_counter: CounterObject): try: salesforce_api_client = SalesforceApiClient() return salesforce_api_client.fetch_sf_count(soql) except ConnectTimeout as e: # 「リトライします」のメッセージ出力後、リトライせず例外終了になってしまうことを防ぐため、カウンタによる回数の判定を行う if count_counter.describe() < CRM_GET_RECORD_COUNT_MAX_RETRY_ATTEMPT: count_counter.increment(1) logger.warning(f'W-FETCH-01 CRMの接続処理がタイムアウトしため、リトライします:[{CRM_AUTH_TIMEOUT}] エラー内容:[{e}]') raise e except ReadTimeout as e: # 「リトライします」のメッセージ出力後、リトライせず例外終了になってしまうことを防ぐため、カウンタによる回数の判定を行う if count_counter.describe() < CRM_GET_RECORD_COUNT_MAX_RETRY_ATTEMPT: count_counter.increment(1) logger.warning( f'W-FETCH-02 [{target_object_name}] の件数取得処理がタイムアウトしたため、リトライします:[{CRM_GET_RECORD_COUNT_TIMEOUT}] エラー内容:[{e}]') raise e except Exception as e: # 「リトライします」のメッセージ出力後、リトライせず例外終了になってしまうことを防ぐため、カウンタによる回数の判定を行う if count_counter.describe() < CRM_GET_RECORD_COUNT_MAX_RETRY_ATTEMPT: count_counter.increment(1) logger.warning( f'W-FETCH-03 [{target_object_name}] の件数取得に失敗したため、リトライします エラー内容:[{e}]') raise e @retry( wait=wait_exponential(multiplier=CRM_FETCH_RECORD_RETRY_INTERVAL, min=CRM_FETCH_RECORD_RETRY_MIN_INTERVAL, max=CRM_FETCH_RECORD_RETRY_MAX_INTERVAL), stop=stop_after_attempt(CRM_FETCH_RECORD_MAX_RETRY_ATTEMPT)) def fetch_sf_data_retry(soql: str, target_object_name: str, data_counter: CounterObject): try: salesforce_api_client = SalesforceApiClient() return salesforce_api_client.fetch_sf_data(soql) except ConnectTimeout as e: # 「リトライします」のメッセージ出力後、リトライせず例外終了になってしまうことを防ぐため、カウンタによる回数の判定を行う if data_counter.describe() < CRM_FETCH_RECORD_MAX_RETRY_ATTEMPT: data_counter.increment(1) logger.warning(f'W-FETCH-04 CRMの接続処理がタイムアウトしため、リトライします:[{CRM_AUTH_TIMEOUT}] エラー内容:[{e}]') raise e except ReadTimeout as e: # 「リトライします」のメッセージ出力後、リトライせず例外終了になってしまうことを防ぐため、カウンタによる回数の判定を行う if data_counter.describe() < CRM_FETCH_RECORD_MAX_RETRY_ATTEMPT: data_counter.increment(1) logger.warning( f'W-FETCH-05 [{target_object_name}] のレコード取得処理がタイムアウトしたため、リトライします:[{CRM_FETCH_RECORD_TIMEOUT}] エラー内容:[{e}]') raise e except Exception as e: # 「リトライします」のメッセージ出力後、リトライせず例外終了になってしまうことを防ぐため、カウンタによる回数の判定を行う if data_counter.describe() < CRM_FETCH_RECORD_MAX_RETRY_ATTEMPT: data_counter.increment(1) logger.warning( f'W-FETCH-06 [{target_object_name}] のレコード取得に失敗したため、リトライします エラー内容:[{e}]') raise e