1# -*- coding: utf-8 -*-
2# Copyright (c) 2015 Spotify AB
3
4from __future__ import absolute_import, division, print_function
5
6import attr
7from six.moves import BaseHTTPServer as httpserver  # NOQA
8
9from .parameters import Content
10from .validate import *  # NOQA
11
12HTTP_RESP_CODES = httpserver.BaseHTTPRequestHandler.responses.keys()
13AVAILABLE_METHODS = [
14    "get", "post", "put", "delete", "patch", "head", "options",
15    "trace", "connect"
16]
17
18METHOD_PROPERTIES = [
19    "headers", "body", "responses", "query_params", "form_params"
20]
21
22
23@attr.s
24class RootNode(object):
25    """
26    API Root Node
27
28    :param dict raw: dict of loaded RAML data
29    :param str version: API version
30    :param str base_uri: API's base URI
31    :param list base_uri_params: parameters for base URI, or ``None``. \
32        The order of ``base_uri_params`` will follow the order \
33        defined in the :py:obj:`.RootNode.base_uri`.
34    :param list uri_params: URI parameters that can apply to all resources, \
35        or ``None``. The order of ``uri_params`` will follow the order \
36        defined in the :py:obj:`.RootNode.base_uri`.
37    :param list protocols: API-supported protocols, defaults to protocol \
38        in ``base_uri``
39    :param str title: API Title
40    :param list docs: list of :py:class:`parameters.Documentation` objects, \
41        or ``None``
42    :param list schemas: list of dictionaries, or ``None``
43    :param str media_type: default accepted request/response media type, \
44        or ``None``
45    :param list resource_types: list of :py:class:`ResourceTypeNode`, \
46        or ``None``
47    :param list traits: list of :py:class:`TraitNode`, or ``None``
48    :param list security_schemes: list of \
49        :py:class:`parameters.SecurityScheme` objects, or ``None``
50    :param list resources: list of :py:class:`ResourceNode` objects, \
51        or ``None``
52    :param raml_obj: loaded :py:class:`raml.RAMLDict` object
53    """
54    raw              = attr.ib(repr=False)
55    version          = attr.ib(repr=False, validator=root_version)
56    base_uri         = attr.ib(repr=False, validator=root_base_uri)
57    base_uri_params  = attr.ib(repr=False,
58                               validator=root_base_uri_params)
59    uri_params       = attr.ib(repr=False, validator=root_uri_params)
60    protocols        = attr.ib(repr=False, validator=root_protocols)
61    title            = attr.ib(validator=root_title)
62    documentation    = attr.ib(repr=False, validator=root_docs)
63    schemas          = attr.ib(repr=False, validator=root_schemas)
64    media_type       = attr.ib(repr=False, validator=root_media_type)
65    secured_by       = attr.ib(repr=False, validator=root_secured_by)
66    resource_types   = attr.ib(repr=False, init=False)
67    traits           = attr.ib(repr=False, init=False)
68    security_schemes = attr.ib(repr=False, init=False)
69    resources        = attr.ib(repr=False, init=False,
70                               validator=root_resources)
71    raml_obj         = attr.ib(repr=False)
72    config           = attr.ib(repr=False,
73                               validator=attr.validators.instance_of(dict))
74    errors           = attr.ib(repr=False)
75
76
77@attr.s
78class BaseNode(object):
79    """
80    :param dict raw: The raw data parsed from the RAML file
81    :param RootNode root: Back reference to the node's API root
82    :param list headers: List of node's :py:class:`parameters.Header` \
83        objects, or ``None``
84    :param list body: List of node's :py:class:`parameters.Body` objects, \
85        or ``None``
86    :param list responses: List of node's :py:class:`parameters.Response`\
87        objects, or ``None``
88    :param list uri_params: List of node's :py:class:`parameters.URIParameter`\
89        objects, or ``None``. The order of ``uri_params`` will follow the \
90        order defined in the \
91        :py:obj:`.ResourceNode.absolute_uri`.
92    :param list base_uri_params: List of node's base \
93        :py:obj:`parameters.URIParameter` objects, or ``None``. The order of \
94        ``base_uri_params`` will follow the order defined in the \
95        :py:attribute:`.ResourceNode.absolute_uri`.
96    :param list query_params: List of node's \
97        :py:obj:`parameters.QueryParameter` objects, or ``None``
98    :param list form_params: List of node's \
99        :py:class:`parameters.FormParameter` objects, or ``None``
100    :param str media_type: Supported request MIME media type. Defaults to \
101        :py:class:`RootNode`'s ``media_type``.
102    :param str description: Description of node.
103    :param list protocols: List of ``str`` 's of supported protocols. \
104        Defaults to :py:class:`RootNode`'s ``protocols``.
105    """
106    root            = attr.ib(repr=False)
107    headers         = attr.ib(repr=False)
108    body            = attr.ib(repr=False)
109    responses       = attr.ib(repr=False)
110    uri_params      = attr.ib(repr=False)
111    base_uri_params = attr.ib(repr=False)
112    query_params    = attr.ib(repr=False)
113    form_params     = attr.ib(repr=False)
114    media_type      = attr.ib(repr=False)
115    desc            = attr.ib(repr=False)
116    protocols       = attr.ib(repr=False)
117    errors          = attr.ib(repr=False)
118
119    @property
120    def description(self):
121        return Content(self.desc)
122
123
124@attr.s
125class TraitNode(BaseNode):
126    """
127    RAML Trait object
128
129    :param str name: Name of trait
130    :param str usage: Usage of trait
131    """
132    name  = attr.ib()
133    raw   = attr.ib(repr=False, validator=defined_trait)
134    usage = attr.ib(repr=False)
135
136
137@attr.s
138class ResourceTypeNode(BaseNode):
139    """
140    RAML Resource Type object
141
142    :param str name: Name of resource type
143    :param str type: Name of inherited :py:class:`ResourceTypeNode` object,
144        or ``None``.
145    :param str method: Supported method. If ends in ``?``, parameters will \
146        only be applied to assigned resource if resource implements this \
147        method. Else, resource must implement the method.
148    :param str usage: Usage of resource type, or ``None``
149    :param bool optional: Inherited if resource defines method.
150    :param list is\_: List of assigned trait names, or ``None``
151    :param list traits: List of assigned :py:class:`TraitNode` objects, \
152        or ``None``
153    :param str secured_by: List of ``str`` s or ``dict`` s of assigned \
154        security scheme, or ``None``. If a ``str``, the name of the security \
155        scheme.  If a ``dict``, the key is the name of the scheme, the values \
156        are the parameters assigned (e.g. relevant OAuth 2 scopes).
157    :param list security_schemes: A list of assigned \
158        :py:class:`parameters.SecurityScheme` objects, or ``None``.
159    :param str display_name: User-friendly name of resource; \
160        defaults to ``name``
161
162    """
163    name             = attr.ib()
164    raw              = attr.ib(repr=False, validator=defined_resource_type)
165    type             = attr.ib(repr=False, validator=assigned_res_type)
166    method           = attr.ib(repr=False)
167    usage            = attr.ib(repr=False)
168    optional         = attr.ib(repr=False)
169    is_              = attr.ib(repr=False, validator=assigned_traits)
170    traits           = attr.ib(repr=False)
171    secured_by       = attr.ib(repr=False)
172    security_schemes = attr.ib(repr=False)
173    display_name     = attr.ib(repr=False)
174
175
176@attr.s
177class ResourceNode(BaseNode):
178    """
179    Supported API-endpoint (“resource”)
180
181    :param str name: Resource name
182    :param ResourceNode parent: parent node object if any, or ``None``
183    :param str method: HTTP method for resource, or ``None``
184    :param str display_name: User-friendly name of resource; \
185        defaults to ``name``
186    :param str path: relative path of resource
187    :param str absolute_uri: Absolute URI of resource: \
188        :py:class:`RootNode`'s ``base_uri`` + ``path``
189    :param list is\_: A list of ``str`` s or ``dict`` s of resource-assigned \
190        traits, or ``None``
191    :param list traits: A list of assigned :py:class:`TraitNode` objects, \
192        or ``None``
193    :param str type: The name of the assigned resource type, or ``None``
194    :param ResourceTypeNode resource_type: The assigned \
195        :py:class:`ResourceTypeNode` object
196    :param list secured_by: A list of ``str`` s or ``dict`` s of resource-\
197        assigned security schemes, or ``None``. If a ``str``, the name of the \
198        security scheme.  If a ``dict``, the key is the name of the scheme, \
199        the values are the parameters assigned (e.g. relevant OAuth 2 scopes).
200    :param list security_schemes: A list of assigned \
201        :py:class:`parameters.SecurityScheme` objects, or ``None``.
202    """
203    name             = attr.ib(repr=False)
204    raw              = attr.ib(repr=False)
205    parent           = attr.ib(repr=False)
206    method           = attr.ib()
207    display_name     = attr.ib(repr=False)
208    path             = attr.ib()
209    absolute_uri     = attr.ib(repr=False)
210    is_              = attr.ib(repr=False, validator=assigned_traits)
211    traits           = attr.ib(repr=False)
212    type             = attr.ib(repr=False, validator=assigned_res_type)
213    resource_type    = attr.ib(repr=False)
214    secured_by       = attr.ib(repr=False)
215    security_schemes = attr.ib(repr=False)
216
217    def _inherit_type(self):
218        for p in METHOD_PROPERTIES:
219            inherited_prop = getattr(self.resource_type, p)
220            resource_prop = getattr(self, p)
221            if resource_prop and inherited_prop:
222                for r in resource_prop:
223                    r._inherit_type_properties(inherited_prop)
224