Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fix pagination with string params #689

Merged
merged 4 commits into from

2 participants

@danielgtaylor

Fixes boto/botocore#243 by making sure the max items pagination param uses the same type as the parameter it is replacing.

$ aws route53 list-resource-record-sets --hosted-zone-id /hostedzone/ABCD --max-items 1

A client error (NoSuchHostedZone) occurred when calling the ListResourceRecordSets operation: No hosted zone found with ID: ABCD

@jamesls please review.

awscli/customizations/paginate.py
@@ -66,8 +66,19 @@ def unify_paging_params(argument_table, operation, event_name, **kwargs):
STARTING_TOKEN_HELP,
operation,
parse_type='string')
+ # Try to get the pagination parameter type
+ type_ = 'integer'
+ if 'limit_key' in operation.pagination:
+ for param in operation.params:
+ if param.name == operation.pagination['limit_key']:
@jamesls Owner
jamesls added a note

I don't think it's a safe assumption that there's always limit_key, based on the code below that does a if 'limit_key' in operation.pagination. It would be worth double checking that this is the case. Either way we should be consistent in this module.

@danielgtaylor Owner

Is line 71 not the same as 107? I'm not sure what you mean here.

@jamesls Owner
jamesls added a note

You're right, sorry I misread the code. Disregard my previous comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jamesls jamesls commented on the diff
tests/unit/customizations/test_paginate.py
((5 lines not shown))
self.bar_param = mock.Mock()
self.bar_param.cli_name = 'bar'
+ self.bar_param.type = 'string'
@jamesls Owner
jamesls added a note

Can we add a test for:

1) integer
2) Unknown type (the raising TypeError is not tested).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
awscli/customizations/paginate.py
@@ -66,8 +66,19 @@ def unify_paging_params(argument_table, operation, event_name, **kwargs):
STARTING_TOKEN_HELP,
operation,
parse_type='string')
+ # Try to get the pagination parameter type
+ type_ = 'integer'
+ if 'limit_key' in operation.pagination:
+ for param in operation.params:
+ if param.name == operation.pagination['limit_key']:
+ type_ = param.type
+ break
+
+ if type_ not in PageArgument.type_map:
+ raise TypeError('Unsupported pagination type {0}'.format(type_))
@jamesls Owner
jamesls added a note

Let's include more context in the error message (the operation/parameter name would be helpful).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jamesls
Owner

Please add a test for this specific case you mention in the PR descriptions:

$ aws route53 list-resource-record-sets --hosted-zone-id /hostedzone/ABCD --max-items 1
@danielgtaylor

I believe my latest changes fix all the comments above. Please take another look :smile:

tests/unit/customizations/test_paginate.py
((5 lines not shown))
+
+class TestStringLimitKey(TestPaginateBase):
+
+ def setUp(self):
+ super(TestStringLimitKey, self).setUp()
+ self.bar_param.type = 'string'
+
+ def test_integer_limit_key(self):
+ argument_table = {
+ 'foo': mock.Mock(),
+ 'bar': mock.Mock(),
+ }
+ paginate.unify_paging_params(argument_table, self.operation,
+ 'building-argument-table.foo.bar')
+ # Max items should be the same type as bar, which may not be an int
+ self.assertEqual('string', argument_table['max-items']._parse_type)
@jamesls Owner
jamesls added a note

In general, we should not be testing through any internal APIs in a unittest unless there's really no other way to do so (just a general rule of thumb to keep in mind). You can use the .cli_type_name property to get this information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jamesls
Owner

Also, I'd say just make the s/._parse_type/.cli_type_name/ change and feel free to merge.

:shipit:

@danielgtaylor danielgtaylor merged commit bf5e9b3 into from
@danielgtaylor danielgtaylor deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
16 awscli/customizations/paginate.py
@@ -66,8 +66,22 @@ def unify_paging_params(argument_table, operation, event_name, **kwargs):
STARTING_TOKEN_HELP,
operation,
parse_type='string')
+ # Try to get the pagination parameter type
+ limit_param = None
+ if 'limit_key' in operation.pagination:
+ for param in operation.params:
+ if param.name == operation.pagination['limit_key']:
+ limit_param = param
+ break
+
+ type_ = limit_param and limit_param.type or 'integer'
+ if limit_param and limit_param.type not in PageArgument.type_map:
+ raise TypeError(('Unsupported pagination type {0} for operation {1}'
+ ' and parameter {2}').format(type_, operation.name,
+ limit_param.name))
+
argument_table['max-items'] = PageArgument('max-items', MAX_ITEMS_HELP,
- operation, parse_type='integer')
+ operation, parse_type=type_)
def check_should_enable_pagination(input_tokens, parsed_args, parsed_globals, **kwargs):
View
57 tests/unit/customizations/test_paginate.py
@@ -17,7 +17,7 @@
from awscli.customizations import paginate
-class TestArgumentTableModifications(unittest.TestCase):
+class TestPaginateBase(unittest.TestCase):
def setUp(self):
self.operation = mock.Mock()
@@ -25,8 +25,10 @@ def setUp(self):
self.foo_param = mock.Mock()
self.foo_param.cli_name = 'foo'
self.foo_param.name = 'Foo'
+ self.foo_param.type = 'string'
self.bar_param = mock.Mock()
self.bar_param.cli_name = 'bar'
+ self.bar_param.type = 'string'
@jamesls Owner
jamesls added a note

Can we add a test for:

1) integer
2) Unknown type (the raising TypeError is not tested).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
self.bar_param.name = 'Bar'
self.params = [self.foo_param, self.bar_param]
self.operation.pagination = {
@@ -35,6 +37,9 @@ def setUp(self):
}
self.operation.params = self.params
+
+class TestArgumentTableModifications(TestPaginateBase):
+
def test_customize_arg_table(self):
argument_table = {
'foo': mock.Mock(),
@@ -66,3 +71,53 @@ def test_operation_with_no_paginate(self):
paginate.unify_paging_params(argument_table, self.operation,
'building-argument-table.foo.bar')
self.assertEqual(starting_table, argument_table)
+
+
+class TestStringLimitKey(TestPaginateBase):
+
+ def setUp(self):
+ super(TestStringLimitKey, self).setUp()
+ self.bar_param.type = 'string'
+
+ def test_integer_limit_key(self):
+ argument_table = {
+ 'foo': mock.Mock(),
+ 'bar': mock.Mock(),
+ }
+ paginate.unify_paging_params(argument_table, self.operation,
+ 'building-argument-table.foo.bar')
+ # Max items should be the same type as bar, which may not be an int
+ self.assertEqual('string', argument_table['max-items'].cli_type_name)
+
+
+class TestIntegerLimitKey(TestPaginateBase):
+
+ def setUp(self):
+ super(TestIntegerLimitKey, self).setUp()
+ self.bar_param.type = 'integer'
+
+ def test_integer_limit_key(self):
+ argument_table = {
+ 'foo': mock.Mock(),
+ 'bar': mock.Mock(),
+ }
+ paginate.unify_paging_params(argument_table, self.operation,
+ 'building-argument-table.foo.bar')
+ # Max items should be the same type as bar, which may not be an int
+ self.assertEqual('integer', argument_table['max-items'].cli_type_name)
+
+
+class TestBadLimitKey(TestPaginateBase):
+
+ def setUp(self):
+ super(TestBadLimitKey, self).setUp()
+ self.bar_param.type = 'bad'
+
+ def test_integer_limit_key(self):
+ argument_table = {
+ 'foo': mock.Mock(),
+ 'bar': mock.Mock(),
+ }
+ with self.assertRaises(TypeError):
+ paginate.unify_paging_params(argument_table, self.operation,
+ 'building-argument-table.foo.bar')
View
18 tests/unit/route53/test_resource_id.py
@@ -102,4 +102,20 @@ def test_short_resource_id(self):
self.assert_params_for_cmd(cmdline, result, expected_rc=0,
ignore_params=['payload'])[0]
-
+
+class TestMaxItems(BaseAWSCommandParamsTest):
+
+ prefix = 'route53 list-resource-record-sets'
+
+ def test_full_resource_id(self):
+ args = ' --hosted-zone-id /hostedzone/ABCD --max-items 1'
+ cmdline = self.prefix + args
+ result = {
+ 'uri_params': {
+ 'HostedZoneId': 'ABCD',
+ 'MaxItems': '1'
+ },
+ 'headers': {}
+ }
+ self.assert_params_for_cmd(cmdline, result, expected_rc=0,
+ ignore_params=['payload'])[0]
Something went wrong with that request. Please try again.