3
>^:                 @   s6  d Z ddl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 ddlmZ dd	lmZ dd
lmZ ddlmZmZmZmZmZ ddlmZ ddlmZ ddlmZ ddlm Z  dd Z!dd Z"dd Z#dZ$G dd deZ%dd Z&dd Z'dd Z(d d! Z)ej*d"Z+G d#d$ d$e,Z-G d%d& d&e,Z.dS )'z]
generators.py   # Top-down schema generation

See schemas.__init__.py for package overview.
    N)CounterOrderedDict)import_module)settings)simplify_regex)PermissionDenied)Http404)six)
exceptions)
URLPatternURLResolvercoreapi
coreschemaget_original_route)clone_request)api_settings)_get_pk   )is_list_viewc             C   s`   dd | D }t |}t|}|}x.t|D ]"\}}||| kr,|d | }P q,W ddj| S )Nc             S   s   g | ]}|j d jd qS )/)stripsplit).0path r   P/tmp/pip-build-8app2_gc/djangorestframework/rest_framework/schemas/generators.py
<listcomp>   s    zcommon_path.<locals>.<listcomp>r   )minmax	enumeratejoin)pathssplit_pathss1s2commonicr   r   r   common_path   s    r(   c             C   s   | j jj }t|jS )N)Z_metaZconcrete_modelr   name)modelmetar   r   r   get_pk_name'   s    
r,   c             C   s*   ddl m} t| dd}|dk	o(t||S )zT
    Return `True` if the given view callback is a REST framework view/viewset.
    r   )APIViewclsN)Zrest_framework.viewsr-   getattr
issubclass)callbackr-   r.   r   r   r   is_api_view,   s    r2   a=  
Schema Naming Collision.

coreapi.Link for URL path {value_url} cannot be inserted into schema.
Position conflicts with coreapi.Link for URL path {target_url}.

Attempted to insert link with keys: {keys}.

Adjust URLs to avoid naming collision or override `SchemaGenerator.get_keys()`
to customise schema structure.
c                   s$   e Zd Z fddZdd Z  ZS )LinkNodec                s    g | _ t | _tt| j  d S )N)linksr   methods_countersuperr3   __init__)self)	__class__r   r   r7   D   s    zLinkNode.__init__c             C   sJ   || kr|S x8| j | }| j |  d7  < dj||}|| kr|S qW d S )Nr   z{}_{})r5   format)r8   preferred_keyZcurrent_valkeyr   r   r   get_available_keyI   s    
zLinkNode.get_available_key)__name__
__module____qualname__r7   r=   __classcell__r   r   )r9   r   r3   C   s   r3   c             C   s~   x.|dd D ]}|| kr$t  | |< | | } qW y| jj|d |f W n0 tk
rx   tj|j| j|d}t|Y nX dS )z
    Nested dictionary insertion.

    >>> example = {}
    >>> insert_into(example, ['a', 'b', 'c'], 123)
    >>> example
    LinkNode({'a': LinkNode({'b': LinkNode({'c': LinkNode(links=[123])}}})))
    Nr   )Z	value_url
target_urlkeysrD   )r3   r4   append	TypeErrorINSERT_INTO_COLLISION_FMTr:   url
ValueError)targetrC   valuer<   msgr   r   r   insert_intoV   s    	
rM   c             C   sH   x| j  D ]\}}t| q
W x$| jD ]\}}| j|}|| |< q&W d S )N)itemsdistribute_linksr4   r=   )objr<   rK   r;   linkr   r   r   rO   o   s
    
rO   c             C   s   | dkS )Nretrievelistcreateupdatepartial_updatedestroy>   rR   rU   rS   rW   rV   rT   r   )actionr   r   r   is_custom_actionx   s    rY   c             C   s*   | \}}}ddddddj |d}||fS )Nr   r            )GETPOSTPUTPATCHDELETE   )get)endpointr   methodr1   Zmethod_priorityr   r   r   endpoint_ordering~   s    
rf   z/<(?:(?P<converter>[^>:]+):)?(?P<parameter>\w+)>c               @   s<   e Zd ZdZdddZdddZdd	 Zd
d Zdd ZdS )EndpointEnumeratorzR
    A class to determine the available API endpoints that a project exposes.
    Nc             C   s@   |d kr6|d krt j}t|tjr,t|}n|}|j}|| _d S )N)r   ZROOT_URLCONF
isinstancer	   string_typesr   Zurlpatternspatterns)r8   rj   urlconfurlsr   r   r   r7      s    
zEndpointEnumerator.__init__ c             C   s   |dkr| j }g }x|D ]}|t| }t|trx| j|}|j}| j||rxL| j|D ]}|||f}	|j|	 qZW qt|t	r| j
|j|d}
|j|
 qW t|td}|S )zZ
        Return a list of all available API endpoints by inspecting the URL conf.
        N)rj   prefix)r<   )rj   r   rh   r   get_path_from_regexr1   should_include_endpointget_allowed_methodsrE   r   get_api_endpointsZurl_patternsextendsortedrf   )r8   rj   rn   Zapi_endpointspattern
path_regexr   r1   re   rd   Znested_endpointsr   r   r   rr      s&    




z$EndpointEnumerator.get_api_endpointsc             C   s   t |}tjtd|}|S )zG
        Given a URL conf regex, return a URI template string.
        z{\g<parameter>})r   resub_PATH_PARAMETER_COMPONENT_RE)r8   rv   r   r   r   r   ro      s    z&EndpointEnumerator.get_path_from_regexc             C   sT   t |sdS |jjdkrdS d|jkr8|jd dkr8dS |jdsL|jdrPdS dS )zI
        Return `True` if the given endpoint should be included.
        FNschemaz	.{format}z
.{format}/T)r2   r.   rz   
initkwargsendswith)r8   r   r1   r   r   r   rp      s    
z*EndpointEnumerator.should_include_endpointc             C   sL   t |dr4t|j}t|jj}dd ||@ D }n
|j j}dd |D S )zL
        Return a list of the valid HTTP methods for this endpoint.
        actionsc             S   s   g | ]}|j  qS r   )upper)r   re   r   r   r   r      s    z:EndpointEnumerator.get_allowed_methods.<locals>.<listcomp>c             S   s   g | ]}|dkr|qS )OPTIONSHEAD)r   r   r   )r   re   r   r   r   r      s    )hasattrsetr}   r.   http_method_namesZallowed_methods)r8   r1   r}   r   methodsr   r   r   rq      s    


z&EndpointEnumerator.get_allowed_methods)NN)Nrm   )	r>   r?   r@   __doc__r7   rr   ro   rp   rq   r   r   r   r   rg      s   


rg   c               @   sp   e Zd ZddddddZeZdZdZddd	ZdddZ	dddZ
dd ZdddZdd Zdd Zdd ZdS )SchemaGeneratorrR   rT   rU   rV   rW   )rc   postputpatchdeleteNc             C   sh   t stdtstd|r0|jd r0|d7 }tj| _tj| _|| _	|| _
|| _|| _|| _d | _d S )Nz/`coreapi` must be installed for schema support.z2`coreschema` must be installed for schema support.r   )r   AssertionErrorr   r|   r   ZSCHEMA_COERCE_METHOD_NAMEScoerce_method_namesZSCHEMA_COERCE_PATH_PKcoerce_path_pkrj   rk   titledescriptionrH   	endpoints)r8   r   rH   r   rj   rk   r   r   r   r7      s    zSchemaGenerator.__init__Fc             C   sx   | j dkr$| j| j| j}|j | _ | j|r0dn|}|s>dS | j}| rZ|dk	rZ|j }t| t	j
| j| j||dS )zL
        Generate a `coreapi.Document` representing the API schema.
        N)r   r   rH   content)r   endpoint_inspector_clsrj   rk   rr   	get_linksrH   Zbuild_absolute_urirO   r   Documentr   r   )r8   requestpublicZ	inspectorr4   rH   r   r   r   
get_schema  s    

zSchemaGenerator.get_schemac             C   s   t  }g }g }xJ| jD ]@\}}}| j|||}| j|||}|j| |j|||f qW |sbdS | j|}	x`|D ]X\}}}| j|||sqr|jj||| j	d}
|t
|	d }| j|||}t|||
 qrW |S )zq
        Return a dictionary containing all the links that should be
        included in the API schema.
        N)base_url)r3   r   create_viewcoerce_pathrE   determine_path_prefixhas_view_permissionsrz   Zget_linkrH   lenget_keysrM   )r8   r   r4   r!   Zview_endpointsr   re   r1   viewrn   rQ   subpathrC   r   r   r   r   '  s&    

zSchemaGenerator.get_linksc             C   s|   g }xn|D ]f}|j djd}g }x |D ]}d|kr6P |j| q(W dj|dd }|s^dS |jd| d  q
W t|S )a  
        Given a list of all paths, return the common prefix which should be
        discounted when generating a schema structure.

        This will be the longest common string that does not include that last
        component of the URL, or the last component before a path parameter.

        For example:

        /api/v1/users/
        /api/v1/users/{pk}/

        The path prefix is '/api/v1/'
        r   {Nr   rD   )r   r   rE   r    r(   )r8   r!   prefixesr   
componentsZinitial_components	componentrn   r   r   r   r   H  s    

z%SchemaGenerator.determine_path_prefixc             C   s   |j f t|di }f |_i |_d|_d|_t|dd|_t|dd}|dk	rn|dkr^d|_n|j|j	 |_|dk	rt
|||_|S )zC
        Given a callback, return an actual view instance.
        r{   Nr}   r   metadata)r.   r/   argskwargsZformat_kwargr   
action_maprX   rc   lowerr   )r8   r1   re   r   r   r}   r   r   r   r   g  s    zSchemaGenerator.create_viewc             C   s@   |j dkrdS y|j|j  W n tjttfk
r:   dS X dS )zY
        Return `True` if the incoming request has the correct view permissions.
        NTF)r   Zcheck_permissionsr
   ZAPIExceptionr   r   )r8   r   re   r   r   r   r   r   ~  s    
z$SchemaGenerator.has_view_permissionsc             C   sJ   | j  sd|kr|S tt|dddd}|r6t|}nd}|jdd| S )z
        Coerce {pk} path arguments into the name of the model field,
        where possible. This is cleaner for an external representation.
        (Ie. "this is an identifier", not "this is a database primary key")
        z{pk}ZquerysetNr*   idz{%s})r   r/   r,   replace)r8   r   re   r   r*   
field_namer   r   r   r     s    
zSchemaGenerator.coerce_pathc             C   s   t |dr|j}n t|||r$d}n| j|j  }dd |jdjdD }t|rt|j	dkr| j|j  }|| j
kr| j
| }||g S |dd |g S || j
kr| j
| }||g S )	a  
        Return a list of keys that should be used to layout a link within
        the schema document.

        /users/                   ("users", "list"), ("users", "create")
        /users/{pk}/              ("users", "read"), ("users", "update"), ("users", "delete")
        /users/enabled/           ("users", "enabled")  # custom viewset list action
        /users/{pk}/star/         ("users", "star")     # custom viewset detail action
        /users/{pk}/groups/       ("users", "groups", "list"), ("users", "groups", "create")
        /users/{pk}/groups/{pk}/  ("users", "groups", "read"), ("users", "groups", "update"), ("users", "groups", "delete")
        rX   rS   c             S   s   g | ]}d |kr|qS )r   r   )r   r   r   r   r   r     s    z,SchemaGenerator.get_keys.<locals>.<listcomp>r   r   NrD   )r   rX   r   default_mappingr   r   r   rY   r   r   r   )r8   r   re   r   rX   Znamed_path_componentsr   r   r   r     s"    





zSchemaGenerator.get_keys)NNNNN)NF)N)N)r>   r?   r@   r   rg   r   r   r   r7   r   r   r   r   r   r   r   r   r   r   r   r      s    


!
r   )/r   rw   collectionsr   r   	importlibr   Zdjango.confr   Zdjango.contrib.admindocs.viewsr   Zdjango.core.exceptionsr   Zdjango.httpr   Zdjango.utilsr	   Zrest_frameworkr
   Zrest_framework.compatr   r   r   r   r   Zrest_framework.requestr   Zrest_framework.settingsr   Zrest_framework.utils.model_metar   utilsr   r(   r,   r2   rG   r3   rM   rO   rY   rf   compilery   objectrg   r   r   r   r   r   <module>   s6   	\