U
    ~4ic                     @   s   d dl mZmZ d dlT 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 ed	ZG d
d deZdS )    )urlparse	parse_qsl)*N)contextmanager)HTTPAdapter)Retry)
Confluence)ApiNotFoundErrorzatlassian.rest_clientc                	       s  e Zd ZdZddejde ddf fdd	Zdd Z	e
dUd	d
Zdd Zdd Zdd Z fddZdVddZdW fdd	ZdXddZdd Zd d! Zd"d# Z fd$d%Z fd&d'ZdYeeeeed)d*d+ZdZeeed,d-d.Zd[ed/d0d1Zd\d2d3Zd]d4d5Z d^d6d7Z!d_d8d9Z"ddd: fd;d<
Z#d`ee$d=d>d?Z% fd@dAZ&dBdC Z'daeeeedDdEdFZ(dbeeeeeeedIdJdKZ)dLdM Z*edNdOdPZ+dQdR Z,dSdT Z-  Z.S )cConfluenceApiuK    Подключение по API к учетной записи Confluence NTFc	           
   
      s   | dr|nd| }|ds*|d7 }|r4|}nd }t j|f|||||||d|	 |   tjj| _dt|j	 | _
d | _|   d S )Nhttphttps:///)usernamepasswordtimeout
verify_sslsessioncloudtokenzCONFLUENCE:COOKIE:)
startswithendswithsuper__init___set_session_retriesZAPPZREDIS_DBredisr   netloc	redis_keycookies_dict_init_cookies)
selfurlr   r   r   r   r   r   r   kwargs	__class__ ./modules/confluence/api.pyr      s0    
	
zConfluenceApi.__init__c                 C   s@   t tjtjtjdd}t|d}| jd| | jd| dS )u   
        Конфигурирует стратегию повторных попыток
        и ассоциирует ее с сессией
           )totalstatusZstatus_forcelistZbackoff_factorZmax_retrieshttp://r   N)r   configZIMPORT_REQUEST_RETRY_TOTALZ!IMPORT_REQUEST_RETRY_STATUS_CODESr   _sessionmount)r   ZretriesZhttp_adapterr$   r$   r%   r   :   s    
z"ConfluenceApi._set_session_retriesc                 c   s   i }t dd}dD ]$}| jj|||< | j|| q| j}|rH|| _z
| V  W 5 | D ],\}}|rx| j|| q\| jj|d q\|r|| _X dS )u<   
        Временно отключает Retry.
        r   r)   )r*   r   N)r   r,   Zadaptersgetr-   r   itemspop)r   r   Zold_adaptersZno_retry_adapterprefixZold_timeoutZadapterr$   r$   r%   without_retryI   s     

zConfluenceApi.without_retryc                 C   sX   | j | j}|rTz*t|}| jj| | jj | _	W n tj
k
rR   Y nX dS )u  
        Добавляет cookie в сессию при инициализации,
        что бы не выполнять повторную авторизацию, если cookie еще действуют.
        При недействительных cookie, выполнится авторизация,
        а cookie обновятся в сессии и кеше
        N)r   r.   r   pickleloadsr,   cookiesupdateget_dictr   ZPickleError)r   cookies_pickler5   r$   r$   r%   r   i   s    
zConfluenceApi._init_cookiesc                 C   s&   t | jj}| jj| j|dd dS )u   
        Сохраняет cookie в кеш (Redis) для последующего использования
        без выполнения повторной авторизации
        i`T  )exN)r3   dumpsr,   r5   r   setr   )r   r8   r$   r$   r%   _save_cookiesy   s    zConfluenceApi._save_cookiesc                 C   s(   | j j }| j|kr$|| _|   dS )u   
        Проверяет изменение cookie и обновляет их в кеше при необходимости
        N)r,   r5   r7   r   r<   )r   Zcurrent_cookies_dictr$   r$   r%   _check_cookies   s    
zConfluenceApi._check_cookiesc                    s   t  j||}|   |S )N)r   requestr=   )r   argsr!   Zresponser"   r$   r%   r>      s    zConfluenceApi.request   c           	   
   C   s   | j }|p| j}| jd||d | jj|d||| j| jd`}td| d|j	 d|j
  |  t|d"}|j|d	D ]}|| q~W 5 Q R X W 5 Q R X d
S )us  
        Скачивает файл

        Args:
            download_url (_type_): url файла для скачивания
            save_path (_type_): путь для сохранения файла
            timeout (_type_, optional): Таймаут. Defaults to None.
            chunk_size (_type_, optional): _description_. Defaults to 64*1024.
        ZGET)methodr    headersT)streamrB   r   Zverifyproxiesz
HTTP: GET z ->  zwb+)
chunk_sizeN)Zno_check_headersr   Zlog_curl_debugr,   r.   r   rD   loggerdebugZstatus_codereasonZraise_for_statusopenZiter_contentwrite)	r   Zdownload_urlZ	save_pathr   rF   rB   resfchunkr$   r$   r%   download_file   s"    

 zConfluenceApi.download_filec                 #   sp   |r|d }|d |d  }nd}d}t  j|||d}|rl|dsHql|d D ]
}|V  qP|rbql||7 }q&dS )u   
        Получим все пространства из Confluence

        :return: Список всех пространств
        r   r&   d   )
space_typestartlimitresultsN)r   get_all_spacesr.   )r   rQ   slicerR   rS   rL   rowr"   r$   r%   rU      s    zConfluenceApi.get_all_spacesr   rP   archivedpagec                 c   s2   | j |||||d}|sq.|E dH  ||7 }q dS )ud   
        Получает архивные документы из пространства
        )content_typeN)Zget_all_pages_from_space)r   spacerR   rS   r(   rZ   rL   r$   r$   r%    get_all_pages_from_space_archive   s
    
z.ConfluenceApi.get_all_pages_from_space_archivec              
   c   s   t ddd}|||< d}zD| j||d}|ds8W d S |d E d H  |d  |d 7  < W q tjk
r } z td	| d
|  W Y d S d }~X Y qX qd S )Nr      rR   rS   zrest/api/user/memberofparamsrT   rR   rS   uL   Не удалось получить группы пользователя "". )dictr.   requestsZ	HTTPErrorrG   warning)r   keyvaluer`   r    rL   Zhttp_errr$   r$   r%   user_member_of   s    
zConfluenceApi.user_member_ofc              
   C   sR   z|  |}W n> tk
rL } z td| d|  t }W 5 d }~X Y nX |S )NuL   Не удалось получить данные пользователя "ra   )Zget_mobile_parameters	ExceptionrG   rd   rb   )r   r   infoerrorr$   r$   r%   get_user_detail   s    zConfluenceApi.get_user_detailc                 #   sv  | j rtddddd}| jd|d}|d s0d S |d D ]\}|d	  | j d
 d}  fdd| D  dd | d
 d
 D  d<  V  q8|d  |d 7  < qntddddd}| jf |}|d sd S |d D ]|}|d	 d dkrt	d q|d	  | j|d	 d d}  fdd| D  dd | d d D  d<  V  q|d  |d 7  < qd S )Nr   r]   zuser=""zoperations,personalSpace)rR   rS   cqlexpandzrest/api/search/userr_   rT   user	accountIdr   c                 3   s*   | ]"\}}| ks|d kr||fV  qdS  Nr$   .0kvrn   r$   r%   	<genexpr>   s     z.ConfluenceApi.get_all_users.<locals>.<genexpr>c                 S   s   g | ]}|qS r$   r$   rt   gr$   r$   r%   
<listcomp>   s     z/ConfluenceApi.get_all_users.<locals>.<listcomp>groupsrR   rS   z	type=userz&user.operations,user.status,user.emailr   Zrecovery_adminu   В вашем Confluence включен режим восстановления! Не оставляйте Confluence в режиме восстановления и не используйте учетную запись recovery_admin!c                 3   s*   | ]"\}}| ks|d kr||fV  qdS rq   r$   rs   rw   r$   r%   rx     s     c                 S   s   g | ]}|qS r$   r$   ry   r$   r$   r%   r{     s     )
r   rb   r.   rk   r6   r/   rg   rl   rG   rd   )r   r`   rL   rW   Z	user_infor$   rw   r%   get_all_users   s4    
zConfluenceApi.get_all_usersc                 #   sF   t ddd}t jf |}|s"d S |E d H  |d  |d 7  < qd S )Nr   r]   r^   rR   rS   )rb   r   get_all_groups)r   r`   rL   r"   r$   r%   r~     s    
zConfluenceApi.get_all_groupsc                 #   sP   t dd|dd}t jf |}|s&d S |D ]
}|V  q*|d  |d 7  < qd S )Nr   r]   zoperations,status)rR   rS   
group_namerm   rR   rS   )rb   r   get_group_members)r   r   r`   rL   rW   r"   r$   r%   r     s    zConfluenceApi.get_group_membersr]   )
content_idrR   rS   rm   returnc                 c   s  d| d}i }|dk	r$t ||d< |dk	r8t ||d< |dk	rH||d< |sTd|d< | j||d	}|d
 D ]<}	|r|	d dkrdd | j|	d ||dD |	d< |	V  qj|d }
|
d}|sqt|}tt|j}|d |d< |dr|d|d< |drT|d|d< qTdS )u  
        Получает все комментарии к документу

        Args:
            content_id (str): ID страницы(документа) в Confluence
            expand (str): Cвойства элемента, которые нужно расширить, например, expand='body,version,history'
            children (bool): Создать иерархическую структуру с дочерними элементами

        Yields:
            Iterator[list]: элемент
        rest/api/content/z/child/commentNrR   rS   rm   alldepthr_   rT   r(   currentc                 S   s   g | ]}|qS r$   r$   )rt   objr$   r$   r%   r{   ;  s     z3ConfluenceApi.get_page_comments.<locals>.<listcomp>id)rm   childrenr   _linksnextcursor)intr.   get_page_commentsr   rb   r   query)r   r   rR   rS   rm   r   r    r`   rL   resultlinksnext_
next_parser   r$   r$   r%   r     s:    


zConfluenceApi.get_page_comments)	key_spacerm   r   c           	      c   s   d}d}| j |d|||dd}|d D ]}|d |d |d	 dg dd
id}|drdd | j|d ddD }|jdd d ||d d d< t||d d d< |V  q&|d sq||7 }qdS )ui  
        Получим все документы из корня пространства.

        :param key_space: Идентификатор пространства.
        :param expand: Дополнительные данные по документам.

        :return: Соответствие id документа и его данных.
        r   2   rootrY   )r   rm   rR   rS   rZ   rT   r   titletyperT   size)r   r   r   r   r   c                 S   s   g | ]}|qS r$   r$   rt   childr$   r$   r%   r{   j  s     z;ConfluenceApi.get_root_pages_from_space.<locals>.<listcomp>Fdetailedc                 S   s   | d S Npositionr$   pr$   r$   r%   <lambda>k      z9ConfluenceApi.get_root_pages_from_space.<locals>.<lambda>re   r   N)Zget_space_contentr.   get_childrensortlen)	r   r   rm   rR   rS   rL   rW   datar   r$   r$   r%   get_root_pages_from_spaceO  s0    	
z'ConfluenceApi.get_root_pages_from_spacerm   c                 c   sZ   t |||d}| j|f|}|rV|ds,qV|d D ]
}|V  q4|d  |d 7  < qd S )N)rR   rS   rm   rT   rR   rS   )rb   Zget_attachments_from_contentr.   )r   page_idrR   rS   rm   r`   rL   rW   r$   r$   r%   get_all_attachments  s    z ConfluenceApi.get_all_attachmentc                 C   s,   z| j ||dW S  tk
r&   Y d S X d S )N)userkeyrm   )Zget_user_details_by_userkeyr	   )r   r   rm   r$   r$   r%   get_user_info_by_key~  s    z"ConfluenceApi.get_user_info_by_keyc                 C   s   | j r| j||dS d S )Nr   )r   get_user_details_by_accountid)r   ro   rm   r$   r$   r%   get_user_info  s    zConfluenceApi.get_user_infoc                 C   sR   z6| j r(| j|d}| j|d |dW S | j||dW S  tk
rL   Y d S X d S )Nrp   ro   )Z	accountidrm   )r   rm   )r   get_account_idr   Zget_user_details_by_usernamer	   )r   r   rm   rn   r$   r$   r%   get_user_info_by_username  s    z'ConfluenceApi.get_user_info_by_usernamec                 C   s4   d}|rd|i}nd|i}| j ||d}|d d S )Nzrest/api/user/bulk/migrationr   r   r_   rT   r   r.   )r   r   r   r    r`   rL   r$   r$   r%   r     s    
zConfluenceApi.get_account_id)r   with_childrenc                   s   t  j|f||}|s:|d |d |d |d d d}|r|dd | j||d	D }|jd
d d d|t|di|d< |S )u  
        Возвращает данные контента

        Николай1: expand='children.page' имеет лимит 25 элементов, запрашиваем детей вручную
        https://jira.atlassian.com/browse/CONFSERVER-52558?src=confmacro
        get_page_child_by_type - без лимита падает, т.к. в коде думает что урлы не относительные
        поэтому лимиты считаем руками
        Bспользуется в двух местах, поэтому вынес в глобальный API

        Args:
            page_id (str, int): ID контента
            detailed (bool, optional): Вернуть подробные данные. Defaults to True.
            with_children (bool, optional): Запросить дочерние объекты. Defaults to False.

        Returns:
            page (dict): данные контента
        r   r   r   
extensionsr   r   r   r   r   c                 S   s   g | ]}|qS r$   r$   r   r$   r$   r%   r{     s     z0ConfluenceApi.get_page_by_id.<locals>.<listcomp>r   c                 S   s   | d S r   r$   r   r$   r$   r%   r     r   z.ConfluenceApi.get_page_by_id.<locals>.<lambda>r   rY   r   r   )r   get_page_by_idr   r   r   )r   r   r   r   r?   r!   rY   r   r"   r$   r%   r     s     
zConfluenceApi.get_page_by_id)typesr   c           
      c   s   |dkr| j sdgnddg}|D ]p}d}d}| j||||d}|sDq |rT|E dH  n2|D ],}|d |d |d	 |d
 d d}	|	V  qX||7 }q,q dS )u  
        Возвращает дочерние объекты(контент)

        Args:
            parent_id (_type_): ID родительского контента
            types (list, optional): Список типов дочерних объектов. Defaults to None.
            detailed (bool, optional): Вернуть подробные данные. Defaults to True.

        Yields:
            dict: данные дочернего объекта(контента)
        NrY   Zfolderr   r]   )r   rR   rS   r   r   r   r   r   r   )r   Zget_page_child_by_type)
r   Z	parent_idr   r   Ztype_rR   rS   rT   r   r   r$   r$   r%   r     s.    
zConfluenceApi.get_childrenc                 #   s&   t t | d d E d H  d S )N)listr    get_all_restrictions_for_contentvalues)r   r   r"   r$   r%   r     s    z.ConfluenceApi.get_all_restrictions_for_contentc                 c   sT   t dd|d}| jf |}|d E d H  |d  |d 7  < |d |d k rqPqd S )Nr   rP   )rR   rS   r   rT   rR   rS   r   )rb   Zget_page_labels)r   r   r`   rL   r$   r$   r%   get_all_page_labels  s    z!ConfluenceApi.get_all_page_labels)r   version_numberrm   r   c                 C   sJ   | j rd| d| }nd| d| }i }|dk	r<||d< | j||dS )u  
        Получает определенную версию документа

        Args:
            content_id (str): ID страницы(документа) в Confluence
            version_number (int): номер версии страницы(документа)
            expand (str): Cвойства элемента, которые нужно расширить, например, expand='body,version,history'

        Return:
            dict: данные версии документа
        r   z	/version/rest/experimental/content/Nrm   r_   )r   r.   )r   r   r   rm   r    r`   r$   r$   r%   get_page_version  s    zConfluenceApi.get_page_version   r   )r   current_versionrR   rS   last_versionsrm   r   c                 c   s  |sdS | j rd| d}nd| d}i }|dk	r@t||d< |dk	rTt||d< |dk	rd||d< d}	zl| j||d	}
|
d
 }|d}|
d D ]>}|	d7 }	d|  kr|	k rn nd} q|d d }|V  qW n   t|ddD ]~}|	d7 }	d|  kr|	k rn n qhz| |||V  W q tk
rd } ztd| d| V  W 5 d}~X Y qX qd}Y nX |s|qt|}tt|j	}|d |d< |dr|d|d< |drh|d|d< qhdS )u  
        Получает версии страницы (историю документа)
        При большом количестве данных в версиях, limit > 5 может не работать,
        зависает, либо падает с ошибкой по таймауту.
        Получение версий списком может упасть с ошибкой 500 "Internal Server Error", если в ответе будет кривая версия.
        При получении ошибки при запросе списков, пытаемся получить версии по одной.

        Args:
            content_id (str): ID страницы(документа) в Confluence
            current_version (int): номер текущй версии страницы(документа)
            last_versions (int): колчество послених версий для получения, при "-1" - получение всех версий
            expand (str): Cвойства элемента, которые нужно расширить, например, expand='body,version,history'

        Yields:
            Iterator[list]: элемент
        Nr   z/versionr   rR   rS   rm   r   r_   r   r   rT   r&   Fnumberr   u   версия z. r   )
r   r   r.   ranger   rh   r   rb   r   r   )r   r   r   rR   rS   r   rm   r    r`   Zversion_countrL   r   r   versionr   excr   r   r$   r$   r%   get_page_versions  sV    
*

zConfluenceApi.get_page_versionsc                 C   sl   | j r | j|dd}|dg S |dkr>d| d}| |S d}ddd	|gd
}| j||ddg S d S )NZpermissions)	space_keyrm   
   zrest/api/space/z/permissionsz!rpc/json-rpc/confluenceservice-v2z2.0ZgetSpacePermissionSets   )ZjsonrpcrA   r   r`   )r   r   )r   Z	get_spacer.   Zpost)r   r   r   Z
space_datar    r   r$   r$   r%   get_space_permissionsb  s    
z#ConfluenceApi.get_space_permissions)r   c                 C   s   d}|  |S )u   
        Получает связи с другими системами

        Return:
            applinks (dict): данные связей с другими системами
        zrest/applinks/3.0/applinksr   r   r    r$   r$   r%   get_applinkst  s    zConfluenceApi.get_applinksc                 C   s   d}|  |S )u7   Получить информацию о системеzrest/applinks/1.0/manifestr   r   r$   r$   r%   get_system_info  s    zConfluenceApi.get_system_infoc                 C   s   d}|  |S )u^   
        Возвращает данные о текущем пользователе
        zrest/api/user/currentr   r   r$   r$   r%   get_current_user  s    zConfluenceApi.get_current_user)N)Nr@   )NN)r   rP   rX   rY   )r   r]   NT)N)r   r]   N)N)N)N)NN)NT)N)r   r   r   N)/__name__
__module____qualname____doc__r+   Z!IMPORT_CONFLUENCE_REQUEST_TIMEOUTZcmfutilZrequests_sessionr   r   r   r2   r   r<   r=   r>   rO   rU   r\   rg   rk   r}   r~   r   strr   r   r   rb   r   r   r   r   r   r   r   boolr   r   r   r   r   r   r   r   r   __classcell__r$   r$   r"   r%   r
      s|   )	

	#	        1$


	

')
    Sr
   )urllib.parser   r   Zcmf.includer3   rc   
contextlibr   Zrequests.adaptersr   Zurllib3.util.retryr   Z	atlassianr   Zatlassian.errorsr	   ZloggingZ	getLoggerrG   r
   r$   r$   r$   r%   <module>   s   
