o
    ^                     @   sN  d Z ddlmZ ddlmZ 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
ZddlmZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlZdZdZdZdZ dZ!dZ"dZ#dZ$dd Z%dd Z&G dd de'Z(G dd de	j)Z*G dd de*Z+G d d! d!ej,Z-d$d"d#Z.dS )%z9A module that converts API exceptions to core exceptions.    )absolute_import)division)unicode_literalsN)
exceptions)resource)log)
properties)resource_lex)resource_printer)resource_property)encoding~CELRzgoogle.rpc.ErrorInfozgoogle.rpc.LocalizedMessagezgoogle.rpc.Helpc                 C   s   g }d}| D ][}|t kr|t t t   q|dkr$|t t t   q|dkr@|dkr6|t t t   n|d |d7 }q|dkr\|d8 }|dkrV|t t t   q|d q|| qd|S )z0Return s with format special characters escaped.r   :{   } )_ESCAPEappend_ESCAPED_ESCAPE_ESCAPED_COLON_ESCAPED_LEFT_CURLY_ESCAPED_RIGHT_CURLYjoin)srnc r"   C/tmp/google-cloud-sdk/lib/googlecloudsdk/api_lib/util/exceptions.py_Escape7   s&   


r$   c                 C   s0  g }d}d}|t | k r| | }|d7 }|tkr|d t | k r| |d  tkr| | }|d7 }|tkrJ|dkr@|tt  n|d |d7 }nC|tkrd|d8 }|dkr^|tt  n/|d n)|dkrt|| |d |  n|tkr~|d n|tkr|t n|| |t | k sd|S )	z9Return s with escaped format special characters expanded.r   r      r   r      r   r   )lenr   r   r   r   r   r   r   )r   r   r    ir!   r"   r"   r#   _ExpandQ   s:   (




r)   c                   @   s   e Zd ZdZdd ZdS )_JsonSortedDictz0A dict with a sorted JSON string representation.c                 C   s   t j| ddS )NT)	sort_keys)jsondumpsselfr"   r"   r#   __str__v   s   z_JsonSortedDict.__str__N)__name__
__module____qualname____doc__r0   r"   r"   r"   r#   r*   s   s    r*   c                       s@   e Zd ZdZ fddZdd Zdd Zdd	 Zd
d Z  Z	S )FormattableErrorPayloada>  Generic payload for an HTTP error that supports format strings.

  Attributes:
    content: The dumped JSON content.
    message: The human readable error message.
    status_code: The HTTP status code number.
    status_description: The status_code description.
    status_message: Context specific status message.
  c                    sP   t t|   d| _i | _d| _d| _d| _t|t	j
r!|| _dS |  | _dS )zInitialize a FormattableErrorPayload instance.

    Args:
      http_error: An Exception that subclasses can use to populate class
        attributes, or a string to use as the error message.
    z{?}r   r   N)superr5   __init___valuecontentstatus_codestatus_descriptionstatus_message
isinstancesixstring_typesmessage_MakeGenericMessager/   
http_error	__class__r"   r#   r7      s   
z FormattableErrorPayload.__init__c                 C   s   t |}|dkr| j|fS |dd}|ddd}|d}|dr,t|dks0|dkr6|dd }|r=|dnd}|rF|dnd}| |\}}	|	s\t|	tt	fs\d|fS |skt|	t
jt
jt	ft
j st }
tj|	|pud|
d	d
 |
  }	|r|	| _| t |}	|	|fS )a  Returns the value of field_name for string.Formatter.format().

    Args:
      field_name: The format string field name to get in the form
        name - the value of name in the payload, '' if undefined
        name?FORMAT - if name is non-empty then re-formats with FORMAT, where
          {?} is the value of name. For example, if name=NAME then
          {name?\nname is "{?}".} expands to '\nname is "NAME".'.
        .a.b.c - the value of a.b.c in the JSON decoded payload contents.
          For example, '{.errors.reason?[{?}]}' expands to [REASON] if
          .errors.reason is defined.
      unused_args: Ignored.
      unused_kwargs: Ignored.

    Returns:
      The value of field_name for string.Formatter.format().
    ?r   r   r   z0.Nr   defaultT)outsingle)r)   r8   splitpop
startswithr'   	_GetFieldr=   intfloatr>   	text_typebinary_typeinteger_typesioStringIOr
   Printgetvaluestripformat)r/   
field_nameunused_argsunused_kwargspartssubpartsnameprinter_formatrecursive_formatvaluebufr"   r"   r#   	get_field   s2   

z!FormattableErrorPayload.get_fieldc                 C   s   d|v r>| drd}|dd }nd}t| }| j}|r3|r3| j|d d}|r3|d |i}t||d}||fS |rK| j|d}||fS d}||fS )a8  Gets the value corresponding to name in self.content or class attributes.

    If `name` starts with a period, treat it as a key in self.content and get
    the corresponding value. Otherwise get the value of the class attribute
    named `name` first and fall back to checking keys in self.content.

    Args:
      name (str): The name of the attribute to return the value of.

    Returns:
      A tuple where the first value is `name` with any leading periods dropped,
      and the second value is the value of a class attribute or key in
      self.content.
    .Fr   NTr   )	rL   r	   LexerKeyr9   __dict__getr   Get)r/   r^   check_payload_attributeskeyr9   ra   r"   r"   r#   rM      s$   
z!FormattableErrorPayload._GetFieldc                 C   s    |   }| jrd|| jS |S )z:Makes a generic human readable message from the HttpError.z{0}: {1})_MakeDescriptionr<   rX   r/   descriptionr"   r"   r#   rA      s   z+FormattableErrorPayload._MakeGenericMessagec                 C   s0   | j }|r|dr|dd }|S d| jS )CMakes description for error by checking which fields are filled in.rd   NzHTTPError {0})r;   endswithrX   r:   rm   r"   r"   r#   rl      s   
z(FormattableErrorPayload._MakeDescription)
r1   r2   r3   r4   r7   rc   rM   rA   rl   __classcell__r"   r"   rD   r#   r5   z   s    
,%r5   c                       s   e Zd ZdZ fddZ fddZdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd Zdd Z fddZdd Zdd Z  ZS )HttpErrorPayloada  Converts apitools HttpError payload to an object.

  Attributes:
    api_name: The url api name.
    api_version: The url version.
    content: The dumped JSON content.
    details: A list of {'@type': TYPE, 'detail': STRING} typed details.
    domain_details: ErrorInfo metadata Indexed by domain.
    violations: map of subject to error message for that subject.
    field_violations: map of field name to error message for that field.
    error_info: content['error'].
    instance_name: The url instance name.
    message: The human readable error message.
    resource_item: The resource type.
    resource_name: The url resource name.
    resource_version: The resource version.
    status_code: The HTTP status code number.
    status_description: The status_code description.
    status_message: Context specific status message.
    unparsed_details: The unparsed details.
    type_details: ErrorDetails Indexed by type.
    url: The HTTP url. .<a>.<b>...: The <a>.<b>... attribute in the JSON content
      (synthesized in get_field()).

  Grammar:
    Format strings inherit from python's string.formatter. where we pass tokens
    obtained by the resource projection framework format strings.

  Examples:
    error_format values and resulting output:

    'Error: [{status_code}] {status_message}{url?\n{?}}{.debugInfo?\n{?}}'

      Error: [404] Not found
      http://dotcom/foo/bar
      <content.debugInfo in yaml print format>

    'Error: {status_code} {details?\n\ndetails:\n{?}}'

      Error: 404

      details:
      - foo
      - bar

     'Error [{status_code}] {status_message}\n'
     '{.:value(details.detail.list(separator="\n"))}'

       Error [400] Invalid request.
       foo
       bar
  c                    s   t t| | d| _d| _g | _i | _i | _d | _d| _	d| _
d| _d| _d| _d | _t|tjsC| | | | |  | _d S d S Nr   )r6   rs   r7   api_nameapi_versiondetails
violationsfield_violations
error_infoinstance_nameresource_itemresource_nameresource_versionurl
_cred_infor=   r>   r?   _ExtractResponseAndJsonContent#_ExtractUrlResourceAndInstanceNamesrA   r@   rB   rD   r"   r#   r7   0  s$   

zHttpErrorPayload.__init__c                    sx   | dr|dd\}}| j|}||fS | dr.|dd\}}| j|}||fS tt| |\}}||fS )Nzfield_violations.rd   r   zviolations.)rL   rJ   ry   rh   rx   r6   rs   rM   )r/   r^   _fieldra   subjectrD   r"   r#   rM   C  s   

zHttpErrorPayload._GetFieldc                 C   s,   d|v r
| ddS d|v r| ddS dS )zGenerates mTLS endpoint override for a given endpoint override.

    Args:
      endpoint_override: The endpoint to generate the mTLS endpoint override
        for.

    Returns:
      The mTLS endpoint override, if one exists. Otherwise, None.
    zsandbox.googleapis.comzmtls.sandbox.googleapis.comzgoogleapis.comzmtls.googleapis.comN)replace)r/   endpoint_overrider"   r"   r#   _GetMTLSEndpointOverrideN  s   
z)HttpErrorPayload._GetMTLSEndpointOverridec                 C   s"   t jj ot jjj ot  S )zfReturns whether mTLS is enabled and an endpoint override is present for the current gcloud invocation.)r   VALUESapi_endpoint_overrides	AllValuescontext_awareuse_client_certificateGetBoolIsInternalUserCheckr.   r"   r"   r#   ,_IsMTLSEnabledAndApiEndpointOverridesPresenta  s
   z=HttpErrorPayload._IsMTLSEnabledAndApiEndpointOverridesPresentc                 C   s2   t j|}|j}|sdS |dd }|dS )zEReturns whether the existing endpoint override is using mTLS already.Nrd   r   mtls)urllibparseurlparsenetlocrJ   rL   )r/   r   	urlparseddomain_partsdomainr"   r"   r#   _IsExistingOverrideMTLSi  s   
z(HttpErrorPayload._IsExistingOverrideMTLSc              
   C   sn  t |dd}|rt|dd| _t|dd| _t|j}ztt	
|| _t| jd | _| js?t| jdd| _| jd	v r| jd
drddlm} |j | _| jr| j }| jd d
 }|d dkr||d |dd  | jd d
< n|d | | jd d
< | jd d
 | jd
< | js| jdd| _|  rtjj }| D ]0\}}	| |	}
| |	s|j|	r|
rd||	g|
| jd
< dd|	|
ddg| jd<  nq| jd
d| _| jdg | _| | j| _ | !| j| _"| #| j| _$| %| j| _&tjj'j() r| *| j| _+W dS W dS  t,t-t.fy,   || _Y dS  t/y6   Y dS w )z:Extracts the response and JSON content from the HttpError.responseNstatusr   reasonr   errorcode)i      r@   )storerp   rd   z.  zCertificate-based access is enabled, but the endpoint override for the following API is not using mTLS: {}. Please update the endpoint override to use mTLS endpoint: {} z(type.googleapis.com/google.rpc.ErrorInfoACCESS_DENIED)r   mtls_endpoint_override)@typer   metadatarw   )0getattrrN   rh   r:   r   Decoder;   r9   r*   r,   loadsrz   googlecloudsdk.core.credentialsr   CredentialInfoGetCredentialInfor   GetInfoStringr   r   r   r   r   itemsr   r   r   rL   rX   r<   rw   _ExtractViolationsrx   _ExtractFieldViolationsry   _IndexErrorDetailsByTypetype_details_IndexErrorInfoByDomaindomain_detailscoreparse_error_detailsr   RedactParsedTypesunparsed_detailsKeyError	TypeError
ValueErrorAttributeError)r/   rC   r   r9   c_storecred_info_messageexisting_messageendpoint_overridesru   r   mtls_endpointr"   r"   r#   r   r  s   




z/HttpErrorPayload._ExtractResponseAndJsonContentc                 C   sB   g }|D ]}| dd}|dd }|ttfvr|| q|S )z/Redacts the parsed types from the details list.r   N/rp   )rh   rJ   LOCALIZED_MESSAGE_SUFFIXHELP_SUFFIXr   )r/   rw   r   item
error_typeerror_suffixr"   r"   r#   r     s   
z"HttpErrorPayload.RedactParsedTypesc                 C   sD   t t}|D ]}|dd}|r|dd }|| | q|S )z>Extracts and indexes error details list by the type attribute.r   Nrd   rp   )collectionsdefaultdictlistrh   rJ   r   )r/   rw   type_mapr   r   error_type_suffixr"   r"   r#   r     s   
z)HttpErrorPayload._IndexErrorDetailsByTypec                 C   sL   t t}|D ]}|dd}|tr#|dd}|r#|| | q|S )z=Extracts and indexes error info list by the domain attribute.r   Nr   )r   r   r   rh   rq   ERROR_INFO_SUFFIXr   )r/   rw   
domain_mapr   r   r   r"   r"   r#   r     s   

z(HttpErrorPayload._IndexErrorInfoByDomainc                 C   s   |j | _ | j s	dS zt| j \}}}W n tjy   Y dS w |r%|| _|r*|| _|d}dt|  k r<dk s?dS  dS |d | _|d }|dd | _	d
| j| _dS )zEExtracts the url resource type and instance names from the HttpError.Nr   r      r   rF   z{} instance)r   resource_utilSplitEndpointUrlInvalidEndpointExceptionru   rv   rJ   r'   r}   r{   rX   r|   )r/   rC   r^   versionresource_pathresource_partsr{   r"   r"   r#   r     s.   

z4HttpErrorPayload._ExtractUrlResourceAndInstanceNamesc                    s   | j rS| jrS| jrS| j dkr*| jr| jjp| jj}ntjjj	 }d
|| j| jS | j dkr9d
| j | jS | j dkrS| jdkrId
| jS d
| j | jS tt|  S )	ro   r   zH[{0}] does not have permission to access {1} [{2}] (or it may not exist)r   z{0} [{1}] not foundi  projectsz7Resource in projects [{0}] is the subject of a conflictz&{0} [{1}] is the subject of a conflict)r:   r|   r{   r   impersonated_accountaccountr   r   r   ri   rX   
capitalizer}   r6   rs   rl   )r/   r   rD   r"   r#   rl     s,   



z!HttpErrorPayload._MakeDescriptionc           	      C      t  }|D ]J}d|vrq|d }t|tsq|d}|D ]1}z$|d}|p)|}|rC||v r=||  d|d  7  < n|d ||< W q ttfyO   Y qw q|S )a&  Extracts a map of violations from the given error's details.

    Args:
      details: JSON-parsed details field from parsed json of error.

    Returns:
      Map[str, str] sub -> error description. The iterator of it is ordered by
      the order the subjects first appear in the errror.
    rx   r   
rn   r   OrderedDictr=   r   rh   r   r   )	r/   rw   resultsdetailrx   sub	violation	local_subr   r"   r"   r#   r     s,   



z#HttpErrorPayload._ExtractViolationsc           	      C   r   )aD  Extracts a map of field violations from the given error's details.

    Args:
      details: JSON-parsed details field from parsed json of error.

    Returns:
      Map[str, str] field (in dotted format) -> error description.
      The iterator of it is ordered by the order the fields first
      appear in the error.
    fieldViolationsr   r   rn   r   )	r/   rw   r   deetrx   fviollocal_fr   r"   r"   r#   r   :  s,   


z(HttpErrorPayload._ExtractFieldViolations)r1   r2   r3   r4   r7   rM   r   r   r   r   r   r   r   r   rl   r   r   rr   r"   r"   rD   r#   rs      s    5	R

 rs   c                       sJ   e Zd ZdZdef fdd	Zdd Zedd Zd	d
 Z	dd Z
  ZS )HttpExceptionzTransforms apitools HttpError to api_lib HttpException.

  Attributes:
    error: The original HttpError.
    error_format: An HttpErrorPayload format string.
    payload: The HttpErrorPayload object.
  Nc                    s*   t t| d || _|| _||| _d S rt   )r6   r   r7   r   error_formatpayload)r/   r   r   payload_classrD   r"   r#   r7   e  s   zHttpException.__init__c                 C   sp   | j }|d u r.d}tjjj rd}d}d}|| | | }n|d }t tj	kr.|d7 }t
| jt|S )Nz
{message?}zG{type_details.LocalizedMessage:value(message.list(separator="
"))?
{?}}zJ{type_details.Help:value(links.flatten(show="values",separator="
"))?
{?}}z{unparsed_details?
{?}}z{details?
{?}}z{.debugInfo?
{?}})r   r   r   r   r   r   r   GetVerbosityloggingDEBUGr)   r   rX   r$   )r/   r   error_prefixparsed_localized_messagesparsed_help_messagesr   r"   r"   r#   r0   k  s*   zHttpException.__str__c                 C   s
   t | S N)r>   rP   r.   r"   r"   r#   r@     s   
zHttpException.messagec                 C   s
   t | jS r   )hashr@   r.   r"   r"   r#   __hash__  s   
zHttpException.__hash__c                 C   s   t |tr| j|jkS dS )NF)r=   r   r@   )r/   otherr"   r"   r#   __eq__  s   
zHttpException.__eq__)r1   r2   r3   r4   rs   r7   r0   propertyr@   r   r   rr   r"   r"   rD   r#   r   \  s    
r   c                    s    fdd}|S )a*  Decorator that catches an HttpError and returns a custom error message.

  It catches the raw Http Error and runs it through the given format string to
  get the desired message.

  Args:
    format_str: An HttpErrorPayload format string. Note that any properties that
    are accessed here are on the HTTPErrorPayload object, and not the raw
    object returned from the server.

  Returns:
    A custom error message.

  Example:
    @CatchHTTPErrorRaiseHTTPException('Error [{status_code}]')
    def some_func_that_might_throw_an_error():
      ...
  c                    s    fdd}|S )Nc               
      sN   z| i |W S  t jy& } zt| }t| W Y d }~d S d }~ww r   )apitools_exceptions	HttpErrorr   core_exceptionsreraise)argskwargsr   exc)
format_strrun_funcr"   r#   Wrapper  s   
zdCatchHTTPErrorRaiseHTTPException.<locals>.CatchHTTPErrorRaiseHTTPExceptionDecorator.<locals>.Wrapperr"   )r   r  r   )r   r#   )CatchHTTPErrorRaiseHTTPExceptionDecorator  s   zSCatchHTTPErrorRaiseHTTPException.<locals>.CatchHTTPErrorRaiseHTTPExceptionDecoratorr"   )r   r  r"   r  r#    CatchHTTPErrorRaiseHTTPException  s   r  r   )/r4   
__future__r   r   r   r   rS   r,   r   stringurllib.parser   apitools.base.pyr   r   googlecloudsdk.api_lib.utilr   r   googlecloudsdk.corer   r   r   googlecloudsdk.core.resourcer	   r
   r   googlecloudsdk.core.utilr   r>   r   r   r   r   r   r   r   r   r$   r)   dictr*   	Formatterr5   rs   Errorr   r  r"   r"   r"   r#   <module>   sL   "   d6