Skip to content

Commit

Permalink
validation for no query params (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
edaniszewski authored Apr 16, 2018
1 parent 45ec2c1 commit e73dc60
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 9 deletions.
4 changes: 3 additions & 1 deletion synse/routes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

from sanic import Blueprint

from synse import commands
from synse import commands, validate

bp = Blueprint(__name__, url_prefix='/synse')


@bp.route('/test')
@validate.no_query_params()
async def test_route(request):
""" Endpoint to test whether the service is up and reachable.
Expand All @@ -24,6 +25,7 @@ async def test_route(request):


@bp.route('/version')
@validate.no_query_params()
async def version_route(request):
""" Endpoint to get the API version of the service.
Expand Down
7 changes: 7 additions & 0 deletions synse/routes/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ async def scan_route(request, rack=None, board=None):


@bp.route('/read/<rack>/<board>/<device>')
@validate.no_query_params()
async def read_route(request, rack, board, device):
"""Read data from a known device.
Expand All @@ -71,6 +72,7 @@ async def read_route(request, rack, board, device):


@bp.route('/write/<rack>/<board>/<device>', methods=['POST'])
@validate.no_query_params()
async def write_route(request, rack, board, device):
"""Write data to a known device.
Expand Down Expand Up @@ -106,6 +108,7 @@ async def write_route(request, rack, board, device):

@bp.route('/transaction')
@bp.route('/transaction/<transaction_id>')
@validate.no_query_params()
async def transaction_route(request, transaction_id=None):
"""Check the status of a write transaction.
Expand All @@ -123,6 +126,7 @@ async def transaction_route(request, transaction_id=None):
@bp.route('/info/<rack>')
@bp.route('/info/<rack>/<board>')
@bp.route('/info/<rack>/<board>/<device>')
@validate.no_query_params()
async def info_route(request, rack, board=None, device=None):
"""Get any known information on the specified resource.
Expand All @@ -140,6 +144,7 @@ async def info_route(request, rack, board=None, device=None):


@bp.route('/config')
@validate.no_query_params()
async def config_route(request):
"""Get the current Synse Server configuration.
Expand All @@ -154,6 +159,7 @@ async def config_route(request):


@bp.route('/plugins')
@validate.no_query_params()
async def plugins_route(request):
"""Get the plugins that are currently configured with Synse Server.
Expand All @@ -172,6 +178,7 @@ async def plugins_route(request):
# should be removed. this will only stay in for a short period of time, so use at
# your own risk!
@bp.route('/fan_sensors')
@validate.no_query_params()
async def fan_sensors(request):
"""Get fan sensor data for autofan.
Expand Down
25 changes: 25 additions & 0 deletions synse/validate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Synse Server utility and convenience methods.
"""

from functools import wraps

from synse import cache, errors
from synse.i18n import gettext

Expand Down Expand Up @@ -51,3 +53,26 @@ def validate_query_params(raw_args, *valid_params):
)
params[k] = v
return params


def no_query_params():
"""Decorator to validate that the incoming request has no query parameters.
This check is largely to ensure that the API is being used correctly
and params aren't accidentally being passed where they don't belong.
Raises:
errors.InvalidArgumentsError: Query parameters were found with
the request.
"""
def decorator(f): # pylint: disable=missing-docstring
@wraps(f)
async def inner(request, *args, **kwargs): # pylint: disable=missing-docstring
if len(request.raw_args) != 0:
raise errors.InvalidArgumentsError(
gettext('Endpoint does not support query parameters but got: {}').format(
request.raw_args)
)
return await f(request, *args, **kwargs)
return inner
return decorator
3 changes: 2 additions & 1 deletion tests/unit/routes/base/test_test_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
from sanic.response import HTTPResponse

from synse.routes.base import test_route as synse_test_route
from tests import utils


@pytest.mark.asyncio
async def test_synse_test_route():
"""Test successfully hitting the test route."""

result = await synse_test_route(None)
result = await synse_test_route(utils.make_request('/synse/test'))

assert isinstance(result, HTTPResponse)
assert result.status == 200
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/routes/base/test_version_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@

from synse import version
from synse.routes.base import version_route
from tests import utils


@pytest.mark.asyncio
async def test_synse_version_route():
"""Test successfully hitting the version route."""

result = await version_route(None)
result = await version_route(utils.make_request('/synse/version'))

assert isinstance(result, HTTPResponse)
assert result.status == 200
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/routes/core/test_config_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import synse.commands
from synse.routes.core import config_route
from synse.scheme.base_response import SynseResponse
from tests import utils


def mockreturn():
Expand All @@ -29,7 +30,7 @@ def mock_config(monkeypatch):
async def test_synse_config_route(mock_config, no_pretty_json):
"""Test successfully getting the config."""

result = await config_route(None)
result = await config_route(utils.make_request('/synse/config'))

assert isinstance(result, HTTPResponse)
assert result.body == b'{"test":"config"}'
Expand Down
11 changes: 9 additions & 2 deletions tests/unit/routes/core/test_info_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import synse.commands
from synse.routes.core import info_route
from synse.scheme.base_response import SynseResponse
from tests import utils


def mockreturn(rack, board, device):
Expand All @@ -29,7 +30,10 @@ def mock_info(monkeypatch):
async def test_synse_info_route(mock_info, no_pretty_json):
"""Test successfully getting the info."""

result = await info_route(None, 'rack1', 'board1', 'device1')
result = await info_route(
utils.make_request('/synse/info'),
'rack1', 'board1', 'device1'
)

assert isinstance(result, HTTPResponse)
assert result.body == b'{"r":"rack1","b":"board1","d":"device1"}'
Expand All @@ -40,7 +44,10 @@ async def test_synse_info_route(mock_info, no_pretty_json):
async def test_synse_info_route_no_optional(mock_info, no_pretty_json):
"""Test successfully getting the info without optional params specified."""

result = await info_route(None, 'rack1')
result = await info_route(
utils.make_request('/synse/info'),
'rack1'
)

assert isinstance(result, HTTPResponse)
assert result.body == b'{"r":"rack1","b":null,"d":null}'
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/routes/core/test_plugins_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import synse.commands
from synse.routes.core import plugins_route
from synse.scheme.base_response import SynseResponse
from tests import utils


def mockreturn():
Expand All @@ -29,7 +30,7 @@ def mock_plugins(monkeypatch):
async def test_synse_config_route(mock_plugins, no_pretty_json):
"""Test successfully getting the plugins."""

result = await plugins_route(None)
result = await plugins_route(utils.make_request('/synse/plugins'))

assert isinstance(result, HTTPResponse)
assert result.body == b'[]'
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/routes/core/test_read_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import synse.commands
from synse.routes.core import read_route
from synse.scheme.base_response import SynseResponse
from tests import utils


def mockreturn(rack, board, device):
Expand All @@ -29,7 +30,10 @@ def mock_read(monkeypatch):
async def test_synse_read_route(mock_read, no_pretty_json):
"""Test a successful read."""

result = await read_route(None, 'rack-1', 'vec', '123456')
result = await read_route(
utils.make_request('/synse/read'),
'rack-1', 'vec', '123456'
)

assert isinstance(result, HTTPResponse)
assert result.body == b'{"value":1}'
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/routes/core/test_transaction_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import synse.commands
from synse.routes.core import transaction_route
from synse.scheme.base_response import SynseResponse
from tests import utils


def mockreturn(transaction):
Expand Down Expand Up @@ -38,7 +39,7 @@ async def test_synse_transaction_route(mock_transaction, no_pretty_json):

for case in cases:

result = await transaction_route(None, case)
result = await transaction_route(utils.make_request('/synse/transaction'), case)
expected = '{{"id":"{0}"}}'.format(case)

assert isinstance(result, HTTPResponse)
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from synse_plugin import api

from synse import cache, errors, validate
from tests import utils


async def make_metainfo_response(rack, board, device):
Expand Down Expand Up @@ -108,3 +109,32 @@ def test_validate_query_params4():
"""Test validating query parameters are valid when an invalid param is given."""
with pytest.raises(errors.InvalidArgumentsError):
validate.validate_query_params({'other': 'something'}, 'test')


@pytest.mark.asyncio
async def test_validate_no_query_params():
"""Test validating that an incoming request has no query params, when there
are no query params.
"""

@validate.no_query_params()
async def test_fn(request, *args, **kwargs):
"""Dummy function for testing the decorator."""
return request

await test_fn(utils.make_request('/synse/endpoint'))


@pytest.mark.asyncio
async def test_validate_no_query_params2():
"""Test validating that an incoming request has no query params, when there
are query params. In this case, we expect an error.
"""

@validate.no_query_params()
async def test_fn(request, *args, **kwargs):
"""Dummy function for testing the decorator."""
return request

with pytest.raises(errors.InvalidArgumentsError):
await test_fn(utils.make_request('/synse/endpoint?test=param'))

0 comments on commit e73dc60

Please sign in to comment.