Skip to content

Commit

Permalink
Merge pull request #74 from spotify/preserve-uri-order-37
Browse files Browse the repository at this point in the history
Fix for issue 37: preserve URI order
  • Loading branch information
econchick committed Dec 24, 2015
2 parents e819cfe + d14d0ff commit 4630ed9
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 27 deletions.
14 changes: 10 additions & 4 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ raml
.. py:attribute:: base_uri_params
``list`` of base :py:class:`.URIParameter` s for the base URI, or
``None``.
``None``. The order of ``base_uri_params`` will follow the order \
defined in the :py:obj:`.RootNode.base_uri`.

.. py:attribute:: uri_params
``list`` of :py:class:`.URIParameter` s that can apply to all
resources, or ``None``.
resources, or ``None``. The order of ``uri_params`` will follow the \
order defined in the :py:obj:`.RootNode.base_uri`.

.. py:attribute:: protocols
Expand Down Expand Up @@ -145,11 +147,15 @@ raml

.. py:attribute:: uri_params
``list`` of ``Node``’s :py:class:`.URIParameter` objects, or ``None``
``list`` of ``Node``’s :py:class:`.URIParameter` objects, or ``None``. \
The order of ``uri_params`` will follow the order defined in the \
:py:obj:`.ResourceNode.absolute_uri`.

.. py:attribute:: base_uri_params
``list`` of ``Node``’s base :py:class:`.URIParameter` objects, or ``None``
``list`` of ``Node``’s base :py:class:`.URIParameter` objects, or ``None``. \
The order of ``base_uri_params`` will follow the order defined in the \
:py:obj:`.ResourceNode.absolute_uri`.

.. py:attribute:: query_params
Expand Down
5 changes: 5 additions & 0 deletions docs/extendedusage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,11 @@ would be ``/foo/bar/{id}``, and the absolute URI path would be
>>> foo_bar.parent
<Resource(method='GET', path='/foo/bar/')>
.. note::

The ``uri_params`` and ``base_uri_params`` on the ``api`` object (``RootNode``) and a resource object (``ResourceNode``) will **always** preserve order according to the absolute URI.


Check out :doc:`api` for full definition of what is available for a ``resource`` object, and its associated attributes and objects.


Expand Down
2 changes: 1 addition & 1 deletion ramlfications/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from __future__ import absolute_import, division, print_function

__author__ = "Lynn Root"
__version__ = "0.1.8.dev0"
__version__ = "0.1.9.dev0"
__license__ = "Apache 2.0"

__email__ = "lynn@spotify.com"
Expand Down
31 changes: 15 additions & 16 deletions ramlfications/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
_get_inherited_attribute, _remove_duplicates, _create_uri_params,
_get, _create_base_param_obj, _get_res_type_attribute,
_get_inherited_type_params, _get_inherited_item, _get_attribute_dict,
get_inherited, set_param_object, set_params, _get_data_union
get_inherited, set_param_object, set_params, _get_data_union,
_preserve_uri_order
)


Expand Down Expand Up @@ -89,11 +90,15 @@ def base_uri():

def base_uri_params():
data = _get(raml, "baseUriParameters", {})
return _create_base_param_obj(data, URIParameter, config, errors)
params = _create_base_param_obj(data, URIParameter, config, errors)
uri = _get(raml, "baseUri", "")
return _preserve_uri_order(uri, params)

def uri_params():
data = _get(raml, "uriParameters", {})
return _create_base_param_obj(data, URIParameter, config, errors)
params = _create_base_param_obj(data, URIParameter, config, errors)
uri = _get(raml, "baseUri", "")
return _preserve_uri_order(uri, params)

def docs():
d = _get(raml, "documentation", [])
Expand Down Expand Up @@ -917,24 +922,18 @@ def uri_params():
unparsed_attr = "uriParameters"
parsed_attr = "uri_params"
root_params = root.uri_params
return _create_uri_params(unparsed_attr, parsed_attr, root_params,
root, type_(), is_(), method, raw_data,
parent)

# TODO: for some reason this doesn't work for uri
# kw = dict(type=type_(),
# is_=is_(),
# root_params=root_params,
# parent=parent)
# return set_params(raw_data, "uri_params", root, method,
# inherit=True, **kw)
params = _create_uri_params(unparsed_attr, parsed_attr, root_params,
root, type_(), is_(), method, raw_data,
parent)
return _preserve_uri_order(absolute_uri(), params)

def base_uri_params():
"""Set resource's base URI parameters."""
root_params = root.base_uri_params
kw = dict(type=type_(), is_=is_(), root_params=root_params)
return set_params(raw_data, "base_uri_params", root, method,
inherit=True, **kw)
params = set_params(raw_data, "base_uri_params", root, method,
inherit=True, **kw)
return _preserve_uri_order(absolute_uri(), params)

def query_params():
kw = dict(type_=type_(), is_=is_())
Expand Down
17 changes: 12 additions & 5 deletions ramlfications/raml.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ class RootNode(object):
:param dict raw: dict of loaded RAML data
:param str version: API version
:param str base_uri: API's base URI
:param list base_uri_params: parameters for base URI, or ``None``
:param list base_uri_params: parameters for base URI, or ``None``. \
The order of ``base_uri_params`` will follow the order \
defined in the :py:obj:`.RootNode.base_uri`.
:param list uri_params: URI parameters that can apply to all resources, \
or ``None``
or ``None``. The order of ``uri_params`` will follow the order \
defined in the :py:obj:`.RootNode.base_uri`.
:param list protocols: API-supported protocols, defaults to protocol \
in ``base_uri``
:param str title: API Title
Expand Down Expand Up @@ -83,11 +86,15 @@ class BaseNode(object):
:param list responses: List of node's :py:class:`parameters.Response`\
objects, or ``None``
:param list uri_params: List of node's :py:class:`parameters.URIParameter`\
objects, or ``None``
objects, or ``None``. The order of ``uri_params`` will follow the \
order defined in the \
:py:obj:`.ResourceNode.absolute_uri`.
:param list base_uri_params: List of node's base \
:py:obj:`parameters.URIParameter` objects, or ``None``
:py:obj:`parameters.URIParameter` objects, or ``None``. The order of \
``base_uri_params`` will follow the order defined in the \
:py:attribute:`.ResourceNode.absolute_uri`.
:param list query_params: List of node's \
:py:class:`parameters.QueryParameter` objects, or ``None``
:py:obj:`parameters.QueryParameter` objects, or ``None``
:param list form_params: List of node's \
:py:class:`parameters.FormParameter` objects, or ``None``
:param str media_type: Supported request MIME media type. Defaults to \
Expand Down
16 changes: 16 additions & 0 deletions ramlfications/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import json
import logging
import os
import re
import sys

try:
Expand Down Expand Up @@ -678,3 +679,18 @@ def set_params(data, param_str, root, method, inherit=False, **kw):

return __remove_duplicates(to_clean)
# <--[query, base uri, form]-->


# preserve order of URI and Base URI parameters
# used for RootNode, ResourceNode
def _preserve_uri_order(path, param_objs):
if not param_objs:
return param_objs
sorted_params = []
pattern = "\{(.*?)\}"
params = re.findall(pattern, path)
for p in params:
_param = [i for i in param_objs if i.name == p]
if _param:
sorted_params.append(_param[0])
return sorted_params
33 changes: 33 additions & 0 deletions tests/data/examples/preserve-uri-order.raml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#%RAML 0.8
title: Example API - Preserve URI Param order
version: v1
protocols: [ HTTPS ]
baseUri: https://{subHostName}.{bucketName}.example.com/{version}/{lang}
baseUriParameters:
subHostName:
description: The name of the sub domain host
bucketName:
description: The name of the bucket
uriParameters:
lang:
description: Language desired
mediaType: application/json
/users/{user_id}/playlists:
uriParameters:
user_id:
displayName: User ID
type: string
description: The user's Spotify user ID.
example: smedjan
displayName: playlists
get:
description: Get a List of a User's Playlists
/{playlist_id}:
get:
description: Get a Playlist's Details
uriParameters:
playlist_id:
displayName: Playlist Id
type: string
description: ID of playlist
example: 123456
31 changes: 30 additions & 1 deletion tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,15 @@ def test_resource_assigned_type(resources):
assert res.method == "get"
assert res.type == "item"

assert res.uri_params[0] == res.resource_type.uri_params[0]
res_type_uri = [r.name for r in res.resource_type.uri_params]
res_uri = [r.name for r in res.uri_params]

exp_res_type_uri = ["mediaTypeExtension"]
exp_res_uri = ["communityPath", "user_id", "thingy_id"]

assert res_type_uri == exp_res_type_uri
assert res_uri == exp_res_uri

assert res.headers[0] == res.resource_type.headers[0]
assert res.body[0] == res.resource_type.body[0]
assert res.responses[0] == res.resource_type.responses[0]
Expand Down Expand Up @@ -1111,6 +1119,27 @@ def test_overwrite_protocol(resource_protocol):
assert second.protocols == ["HTTP"]


@pytest.fixture(scope="session")
def uri_param_resources():
raml_file = os.path.join(EXAMPLES, "preserve-uri-order.raml")
loaded_raml = load_file(raml_file)
config = setup_config(EXAMPLES + "test-config.ini")
config['validate'] = False
return pw.parse_raml(loaded_raml, config)


def test_uri_params_order(uri_param_resources):
res = uri_param_resources.resources[1]
expected_uri = ["lang", "user_id", "playlist_id"]
expected_base = ["subHostName", "bucketName"]

uri = [u.name for u in res.uri_params]
base = [b.name for b in res.base_uri_params]

assert uri == expected_uri
assert base == expected_base


#####
# Test Includes parsing
#####
Expand Down

0 comments on commit 4630ed9

Please sign in to comment.