
    K.h8                    *   d dl mZ d dlZd dlZd dlZd dlZd dlZd dlmZ d dl	m
Z
 d dlmZmZ d dlmZ d dlmZ d dlmZ d d	lmZ  ej        e          Z G d
 dej                  Z G d de          ZddZddZddZ G d de          ZdS )    )annotationsN)
HTTPStatus)Any)ResponseSession)ConnectionError)CaseInsensitiveDict)	TypeGuard)	JIRAErrorc                  4    e Zd ZdZej        dd            ZdS )PrepareRequestForRetryzThis class allows for the manipulation of the Request keyword arguments before a retry.

    The :py:meth:`.prepare` handles the processing of the Request keyword arguments.
    original_request_kwargsr	   returnc                    |S )a)  Process the Request's keyword arguments before retrying the Request.

        Args:
            original_request_kwargs (CaseInsensitiveDict): The keyword arguments of the Request.

        Returns:
            CaseInsensitiveDict: The new keyword arguments to use in the retried Request.
         )selfr   s     g/Users/user/workspace/sujinbaek/cqa-test-app/venv/lib/python3.11/site-packages/jira/resilientsession.pypreparezPrepareRequestForRetry.prepare   s
     '&    Nr   r	   r   r	   )__name__
__module____qualname____doc__abcabstractmethodr   r   r   r   r   r      sB         
 	' ' ' ' ' 'r   r   )	metaclassc                  $     e Zd ZdZd fdZ xZS )PassthroughRetryPreparezbReturns the Request's keyword arguments unchanged, when no change needs to be made before a retry.r   r	   r   c                F    t                                          |          S )N)superr   )r   r   	__class__s     r   r   zPassthroughRetryPrepare.prepare-   s     ww6777r   r   )r   r   r   r   r   __classcell__r"   s   @r   r   r   *   sC        ll8 8 8 8 8 8 8 8 8 8r   r   respResponse | Noner   TypeGuard[Response]c                    |                     dd          }| t          dd| i|| j        s,t          |           }t          |f| j        | j        || d|dS )	a.  Handle errors from a Jira Request.

    Args:
        resp (Optional[Response]): Response from Jira request

    Raises:
        JIRAError: If Response is None
        JIRAError: for unhandled 400 status codes.

    Returns:
        TypeGuard[Response]: True if the passed in Response is all good.
    requestNEmpty Response!response)r%   )status_codeurlr)   r+   T)r*   )getr   okparse_error_msgr,   r-   )r%   kwargsr)   errors       r   raise_on_errorr3   3   s     jjD))G|CCDCFCCC7 

T***
(
 
 
 
 	
 4r   r   	list[str]c                2   i }g }| j         dk    rd| j        v r| j        d         gS | j        r.	 |                                 }n# t          $ r | j        gcY S w xY wd|v r	|d         g}d|v r	|d         g}d|v rK|d         }t          |          dk    r0t          |t          t          z            rt          |          }n|g}d|v rN|d         }t          |          dk    r3t          |t                    rd |
                                D             }|S )	a;  Parse a Jira Error messages from the Response.

    https://developer.atlassian.com/cloud/jira/platform/rest/v2/intro/#status-codes

    Args:
        resp (Response): The Jira API request's response.

    Returns:
        List[str]: The error messages list parsed from the Response. An empty list if no error.
    i  zx-authentication-denied-reasonmessageerrorMessageerrorMessagesr   errorsc                ,    g | ]}t          |          S r   )str).0errs     r   
<listcomp>z parse_errors.<locals>.<listcomp>~   s    FFF#SXXFFFr   )r,   headerstextjson
ValueErrorlen
isinstancelisttupledictvalues)r%   	resp_dataparsed_errorserror_messagesresp_errorss        r   parse_errorsrM   T   sm    !#I!M3#Ct|#S#S=>??	 			II 	 	 	I;	 I"9-."" #>23)## #?3~"".$,77 1 $^ 4 4!/ 09){aJ{D$A$A GF1C1C1E1EFFFMs   A AAr;   c                J    t          |           }d                    |          S )aE  Parse a Jira Error messages from the Response and join them by comma.

    https://developer.atlassian.com/cloud/jira/platform/rest/v2/intro/#status-codes

    Args:
        resp (Response): The Jira API request's response.

    Returns:
        str: The error message parsed from the Response. An empty str if no error.
    z, )rM   join)r%   r9   s     r   r0   r0      s#     $F99Vr   c                  j     e Zd ZdZdd fdZddZ e            fd fdZd dZ	 d!d"dZ	d dZ
 xZS )#ResilientSessionzThis class is supposed to retry requests that do return temporary errors.

    :py:meth:`__recoverable` handles all retry-able errors.
    N   <   max_retriesintmax_retry_delayc                    || _         || _        || _        t                                                       | j                            ddi           t                              ddt           dz              dS )a  A Session subclass catered for the Jira API with exponential delaying retry.

        Args:
            timeout (Optional[Union[Union[float, int], Tuple[float, float]]]): Connection/read timeout delay. Defaults to None.
            max_retries (int): Max number of times to retry a request. Defaults to 3.
            max_retry_delay (int): Max delay allowed between retries. Defaults to 60.
        Acceptzapplication/json,*/*;q=0.9z@WARNING: On error, will dump Response headers and body to logs. zLog level debug in 'z"' is not safe for production code!N)
timeoutrT   rV   r!   __init__r?   updateLOGdebugr   )r   rY   rT   rV   r"   s       r   rZ   zResilientSession.__init__   s     &. 	X'CDEEE 			NQXQQQR	
 	
 	
 	
 	
r   r   rG   c                d   |                                 }|  | j                                         }|                    |                    di                      ||d<   |                    dd          }t	          |t
                    r|rt          j        |          |d<   d|vr
| j        |d<   |S )z?Do any pre-processing of our own and return the updated kwargs.r?   dataNverify)	copyr?   r[   r.   rD   rG   rA   dumpsr`   )r   original_kwargsprepared_kwargsrequest_headersr_   s        r   _jira_preparezResilientSession._jira_prepare   s    )..00),++--229bAABBB%4	"""6400dD!! 	7d 	7 '+j&6&6OF#?**(,OH%r   methodr;   r-   str | bytes_prepare_retry_classr   r   c                $    dd}d}  j         d	i |}d
 fd} |            rd}d}	  t                      j        ||fd j        i|}|j        r                     |           |S n# t          $ r}	|	}Y d}	~	nd}	~	ww xY w||n|}
dz   |            r@                     |
||                                          r|	                    |           n
 j
        dz    |            ||t          |fi |r|S t          d          )a	  This is an intentional override of `Session.request()` to inject some error handling and retry logic.

        Raises:
            Exception: Various exceptions as defined in py:method:`raise_on_error`.

        Returns:
            Response: The response.
        r   Nr   boolc                      j         k    S )z4Helper method to say if we should still be retrying.)rT   )retry_numberr   s   r   is_allowed_to_retryz5ResilientSession.request.<locals>.is_allowed_to_retry   s    4#333r   rY      z*Expected a Response or Exception to raise!r   )r   rk   )rf   r!   r)   rY   r/   2_ResilientSession__handle_known_ok_response_errorsr   _ResilientSession__recoverableupperr   rT   r3   RuntimeError)r   rg   r-   ri   r1   	exceptionr+   processed_kwargsrn   eresponse_or_exceptionrm   r"   s   `          @r   r)   zResilientSession.request   s    &*	$( .4-7777	4 	4 	4 	4 	4 	4 	4 "!## 	4HI	*577?C )-9I  ; $::8DDD#O$ #   						 190DHH)!AL""$$ 4););%sFLLNNL* * 4 %,,-=>>>>#/!3/ "!## 	42  OH99(899 	MO KLLLs   ?A. .
B8A??Br+   c                    |j         sdS t          |j                  dk    r4d|j        v r-d|j        d         v r t                              d           dS dS dS dS )zResponses that report ok may also have errors.

        We can either log the error or raise the error as appropriate here.

        Args:
            response (Response): The response.
        Nr   zX-Seraph-LoginReasonAUTHENTICATED_FAILEDz;Atlassian's bug https://jira.atlassian.com/browse/JRA-41559)r/   rC   contentr?   r\   warning)r   r+   s     r   !__handle_known_ok_response_errorsz2ResilientSession.__handle_known_ok_response_errors   sx     { 	F !!Q&&&(*:::&(*:;Q*RRRKKUVVVVV	 '&::RRr   ro   !ConnectionError | Response | Nonerequest_methodcounterc                   d}t          |          }t          |t                    rldd|z  z  }t                              d| d|j         d| d| dz              t          j        t          j        k    rt                              d	           nt          |t                    rt          j        t          j        g}|j        |v r|j                            d
          }|r!dt!          t#          |          d          z  }n|j        t          j        k    rdd|z  z  }|j        t          j        k    r&|j         d|j         }|                     |           |dk    }	|	rt)          | j        |          t-          j        dd          z  }
t                              d| d| d| d| j         d|
 d|            t          |t                    rat                              dt5          j        t9          |j                  d                     t                              d|j                   t=          j        |
           |	S )a  Return whether the request is recoverable and hence should be retried.

        Exponentially delays if recoverable.

        At this moment it supports: 429, 503

        Args:
            response (Optional[Union[ConnectionError, Response]]): The response or exception.
              Note: the response here is expected to be ``not response.ok``.
            url (Union[str, bytes]): The URL.
            request_method (str): The request method.
            counter (int, optional): The retry counter to use when calculating the exponential delay. Defaults to 1.

        Returns:
            bool: True if the request should be retried.
        
      zGot ConnectionError [z] errno:z on  
zJResponse headers for ConnectionError are only printed for log level DEBUG.Retry-Afterro   r   g      ?g      ?zGot recoverable error from z, will retry [/z] in zs. Err: zresponse.headers:
%s   )indentzresponse.body:
%s) r;   rD   r   r\   r{   errnolevelloggingDEBUGr   r   TOO_MANY_REQUESTSSERVICE_UNAVAILABLEr,   r?   r.   maxrU   reason(_ResilientSession__log_http_429_responseminrV   randomuniformrT   r]   rA   rb   rG   rz   timesleep)r   r+   r-   r~   r   suggested_delaymsgrecoverable_error_codesretry_afteris_recoverabledelays              r   __recoverablezResilientSession.__recoverable  s   . (mmh00 	; 1g:oOKK___(.__n___***   y7=((`   (++ 	;,.'#
 #'>>>&.22=AA 6&'#K((!+ + 'OO )Z-III&(1g:oO':+GGG%1EEHOEEC00:::(1, 	,o>>SB B E KK Hn  H  Hs  H  HRY  H  H\`\l  H  Hsx  H  H  CF  H  H   (H-- B		+JtH$455a@@@   		.0@AAAJur   c                l   |j                             d          }|j                             d          }|j                             d          }|j                             d          }d}||d| dndz  }|||| d	| dz  }|	|d
| dz  }|dz   dz   }t                              |           d S )Nr   zX-RateLimit-FillRatezX-RateLimit-Interval-SecondszX-RateLimit-LimitzRequest rate limited by Jira.z! Request should be retried after z
 seconds.
r   z tokens are issued every zYou can accumulate up to z	 tokens.
z;Consider adding an exemption for the user as explained in: zohttps://confluence.atlassian.com/adminjiraserver/improving-instance-stability-with-rate-limiting-983794911.html)r?   r.   r\   r{   )r   r+   r   $number_of_tokens_issued_per_interval#token_issuing_rate_interval_secondsmaximum_number_of_tokenswarning_msgs          r   __log_http_429_responsez(ResilientSession.__log_http_429_response[  s.   &**=99/7/?/C/C"0
 0
, /7.>.B.B*/
 /
+ $,#3#7#78K#L#L 5& IHHHH	
 1<3?B  N  N  ^A  N  N  N  NK#/P,DPPPK
 KL@ 	 	K     r   )NrR   rS   )rT   rU   rV   rU   )r   rG   )rg   r;   r-   rh   ri   r   r   r   )r+   r   )ro   )r+   r}   r-   rh   r~   r;   r   rU   )r   r   r   r   rZ   rf   r   r)   rp   rq   r   r#   r$   s   @r   rQ   rQ      s         

 
 
 
 
 
 
,   . 8O7N7P7P	;M ;M ;M ;M ;M ;M ;MzW W W W, J J J J JX"! "! "! "! "! "! "! "!r   rQ   )r%   r&   r   r'   )r%   r   r   r4   )r%   r   r   r;   ) 
__future__r   r   rA   r   r   r   httpr   typingr   requestsr   r   requests.exceptionsr   requests.structuresr	   typing_extensionsr
   jira.exceptionsr   	getLoggerr   r\   ABCMetar   r   r3   rM   r0   rQ   r   r   r   <module>r      s   " " " " " " 



                 & & & & & & & & / / / / / / 3 3 3 3 3 3 ' ' ' ' ' ' % % % % % %g!!' ' ' ' 's{ ' ' ' '*8 8 8 8 84 8 8 8   B, , , ,^   k! k! k! k! k!w k! k! k! k! k!r   