diff --git a/botocore/__init__.py b/botocore/__init__.py index 7f10ee428f..4c6912ea52 100644 --- a/botocore/__init__.py +++ b/botocore/__init__.py @@ -23,7 +23,7 @@ import re import logging -__version__ = '0.26.0' +__version__ = '0.27.0' class NullHandler(logging.Handler): diff --git a/botocore/data/aws/s3.json b/botocore/data/aws/s3.json index 4c5cf3fdfd..11f7b856e3 100644 --- a/botocore/data/aws/s3.json +++ b/botocore/data/aws/s3.json @@ -318,7 +318,8 @@ "type": "string", "location": "header", "location_name": "x-amz-website-redirect-location", - "documentation": "If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata." + "documentation": "If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.", + "no_paramfile": true } } }, @@ -604,7 +605,8 @@ "type": "string", "location": "header", "location_name": "x-amz-website-redirect-location", - "documentation": "If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata." + "documentation": "If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.", + "no_paramfile": true } } }, @@ -3816,7 +3818,8 @@ "type": "string", "location": "header", "location_name": "x-amz-website-redirect-location", - "documentation": "If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata." + "documentation": "If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.", + "no_paramfile": true } } }, diff --git a/botocore/data/aws/support.json b/botocore/data/aws/support.json index 5f21109256..3298677d41 100644 --- a/botocore/data/aws/support.json +++ b/botocore/data/aws/support.json @@ -99,8 +99,7 @@ "shape_name": "ServiceCode", "type": "string", "pattern": "[0-9a-z\\-_]+", - "documentation": "\n

Code for the AWS service returned by the call to DescribeServices.

\n ", - "required": true + "documentation": "\n

Code for the AWS service returned by the call to DescribeServices.

\n " }, "severityCode": { "shape_name": "SeverityCode", @@ -110,8 +109,7 @@ "categoryCode": { "shape_name": "CategoryCode", "type": "string", - "documentation": "\n

Specifies the category of problem for the AWS Support case.

\n ", - "required": true + "documentation": "\n

Specifies the category of problem for the AWS Support case.

\n " }, "communicationBody": { "shape_name": "CommunicationBody", diff --git a/botocore/parameters.py b/botocore/parameters.py index c060407686..e8f97d27d1 100644 --- a/botocore/parameters.py +++ b/botocore/parameters.py @@ -374,9 +374,17 @@ def build_parameter_query(self, value, built_params, label=''): else: label = self.get_label() label = '%s.%s' % (label, 'member') - for i, v in enumerate(value, 1): - member_type.build_parameter_query(v, built_params, - '%s.%d' % (label, i)) + if len(value) == 0 and self.required: + # If the parameter is required and an empty list is + # provided as a value, we should insert a parameter + # into the dictionary with the base name of the + # parameter and a value of the empty string. See + # ELB SetLoadBalancerPoliciesForBackendServer for example. + built_params[label.split('.')[0]] = '' + else: + for i, v in enumerate(value, 1): + member_type.build_parameter_query(v, built_params, + '%s.%d' % (label, i)) def build_parameter_json(self, value, built_params, label=''): value = self.validate(value) @@ -415,13 +423,21 @@ def _handle_subtypes(self): def build_parameter_query(self, value, built_params, label=''): label = self.get_label() + if not self.flattened: + label = '%s.entry' % label key_type = self.keys member_type = self.members for i, v in enumerate(value, 1): - built_params['%s.%d.%s' % (label, i, key_type.xmlname)] = v + key_name = key_type.xmlname + if not key_name: + key_name = 'key' + built_params['%s.%d.%s' % (label, i, key_name)] = v + member_name = member_type.xmlname + if not member_name: + member_name = 'value' member_type.build_parameter_query( value[v], built_params, - '%s.%d.%s' % (label, i, member_type.xmlname)) + '%s.%d.%s' % (label, i, member_name)) def build_parameter_json(self, value, built_params, label=''): label = self.get_label() diff --git a/services/s3.extra.json b/services/s3.extra.json index 78ed31abd0..86d3310942 100644 --- a/services/s3.extra.json +++ b/services/s3.extra.json @@ -40,6 +40,33 @@ } } } + }, + "PutObject": { + "input": { + "members": { + "WebsiteRedirectLocation": { + "no_paramfile": true + } + } + } + }, + "CopyObject": { + "input": { + "members": { + "WebsiteRedirectLocation": { + "no_paramfile": true + } + } + } + }, + "CreateMultipartUpload": { + "input": { + "members": { + "WebsiteRedirectLocation": { + "no_paramfile": true + } + } + } } }, "pagination": { diff --git a/services/support.json b/services/support.json index 323c5a203c..73bade39b1 100644 --- a/services/support.json +++ b/services/support.json @@ -99,8 +99,7 @@ "shape_name": "ServiceCode", "type": "string", "pattern": "[0-9a-z\\-_]+", - "documentation": "\n

Code for the AWS service returned by the call to DescribeServices.

\n ", - "required": true + "documentation": "\n

Code for the AWS service returned by the call to DescribeServices.

\n " }, "severityCode": { "shape_name": "SeverityCode", @@ -110,8 +109,7 @@ "categoryCode": { "shape_name": "CategoryCode", "type": "string", - "documentation": "\n

Specifies the category of problem for the AWS Support case.

\n ", - "required": true + "documentation": "\n

Specifies the category of problem for the AWS Support case.

\n " }, "communicationBody": { "shape_name": "CommunicationBody", diff --git a/setup.py b/setup.py index fa3146b912..d3eb98efb1 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ requires = ['six>=1.1.0', - 'jmespath==0.1.0', + 'jmespath==0.2.0', 'python-dateutil>=2.1'] diff --git a/tests/unit/test_elb_operations.py b/tests/unit/test_elb_operations.py index 2a53a414d0..43ae60cc14 100644 --- a/tests/unit/test_elb_operations.py +++ b/tests/unit/test_elb_operations.py @@ -76,6 +76,27 @@ def test_register_instances_with_load_balancer(self): 'Instances.member.2.InstanceId': 'i-87654321'} self.assertEqual(params, result) + def test_set_lb_policies_for_backend_server(self): + op = self.elb.get_operation('SetLoadBalancerPoliciesForBackendServer') + params = op.build_parameters(load_balancer_name='foobar', + instance_port=443, + policy_names=['fie', 'baz']) + result = {'LoadBalancerName': 'foobar', + 'InstancePort': '443', + 'PolicyNames.member.1': 'fie', + 'PolicyNames.member.2': 'baz'} + self.assertEqual(params, result) + + def test_clear_lb_policies_for_backend_server(self): + op = self.elb.get_operation('SetLoadBalancerPoliciesForBackendServer') + params = op.build_parameters(load_balancer_name='foobar', + instance_port=443, + policy_names=[]) + result = {'LoadBalancerName': 'foobar', + 'InstancePort': '443', + 'PolicyNames': ''} + self.assertEqual(params, result) + if __name__ == "__main__": unittest.main() diff --git a/tests/unit/test_sns_operations.py b/tests/unit/test_sns_operations.py index 50cae127ec..d7cf3b4135 100644 --- a/tests/unit/test_sns_operations.py +++ b/tests/unit/test_sns_operations.py @@ -22,8 +22,9 @@ # import unittest -from mock import Mock, sentinel +from mock import Mock +from botocore.compat import OrderedDict import botocore.session @@ -74,6 +75,21 @@ def test_sns_post_send_event_is_invoked(self): self.assertEqual(calls[0]['http_response'], self.http_response) self.assertEqual(calls[0]['parsed'], self.parsed_response) + def test_create_platform_application(self): + op = self.sns.get_operation('CreatePlatformApplication') + attributes = OrderedDict() + attributes['PlatformCredential'] = 'foo' + attributes['PlatformPrincipal'] = 'bar' + params = op.build_parameters(name='gcmpushapp', platform='GCM', + attributes=attributes) + result = {'Name': 'gcmpushapp', + 'Platform': 'GCM', + 'Attributes.entry.1.key': 'PlatformCredential', + 'Attributes.entry.1.value': 'foo', + 'Attributes.entry.2.key': 'PlatformPrincipal', + 'Attributes.entry.2.value': 'bar'} + self.assertEqual(params, result) + if __name__ == "__main__": unittest.main()