def test_multiple_structures_list_returns_struture(self): # This is to handle the scenario when something is modeled # as a structure and instead a list of structures is returned. # For this case, a single element from the list should be parsed # For ibm_botocore, this will be the first element. # Currently, this logic may happen in s3's GetBucketLifecycle # operation. headers = {} parser = parsers.RestXMLParser() body = ( '<?xml version="1.0" ?>' '<OperationName xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' ' <Foo><Bar>first_value</Bar></Foo>' ' <Foo><Bar>middle_value</Bar></Foo>' ' <Foo><Bar>last_value</Bar></Foo>' '</OperationName>') builder = model.DenormalizedStructureBuilder() output_shape = builder.with_members({ 'Foo': { 'type': 'structure', 'members': { 'Bar': { 'type': 'string', } } } }).build_model() parsed = parser.parse( { 'body': body, 'headers': headers, 'status_code': 200 }, output_shape) # Ensure the first element is used out of the list. self.assertEqual(parsed['Foo'], {'Bar': 'first_value'})
def test_s3_error_response(self): body = ('<Error>' ' <Code>NoSuchBucket</Code>' ' <Message>error message</Message>' ' <BucketName>asdf</BucketName>' ' <RequestId>EF1EF43A74415102</RequestId>' ' <HostId>hostid</HostId>' '</Error>').encode('utf-8') headers = {'x-amz-id-2': 'second-id', 'x-amz-request-id': 'request-id'} parser = parsers.RestXMLParser() parsed = parser.parse( { 'body': body, 'headers': headers, 'status_code': 400 }, None) self.assertIn('Error', parsed) self.assertEqual( parsed['Error'], { 'Code': 'NoSuchBucket', 'Message': 'error message', 'BucketName': 'asdf', # We don't want the RequestId/HostId because they're already # present in the ResponseMetadata key. }) self.assertEqual( parsed['ResponseMetadata'], { 'RequestId': 'request-id', 'HostId': 'second-id', 'HTTPStatusCode': 400, 'HTTPHeaders': headers })
def test_s3_error_response_with_no_body(self): # If you try to HeadObject a key that does not exist, # you will get an empty body. When this happens # we expect that we will use Code/Message from the # HTTP status code. body = '' headers = {'x-amz-id-2': 'second-id', 'x-amz-request-id': 'request-id'} parser = parsers.RestXMLParser() parsed = parser.parse( { 'body': body, 'headers': headers, 'status_code': 404 }, None) self.assertIn('Error', parsed) self.assertEqual(parsed['Error'], { 'Code': '404', 'Message': 'Not Found', }) self.assertEqual( parsed['ResponseMetadata'], { 'RequestId': 'request-id', 'HostId': 'second-id', 'HTTPStatusCode': 404, 'HTTPHeaders': headers })
def test_empty_rest_xml_response(self): # This is the format used by cloudfront, route53. headers = {'x-amzn-requestid': 'request-id'} parser = parsers.RestXMLParser() output_shape = None parsed = parser.parse( {'body': b'', 'headers': headers, 'status_code': 200}, output_shape) self.assertEqual( parsed, {'ResponseMetadata': {'RequestId': 'request-id', 'HTTPStatusCode': 200, 'HTTPHeaders': headers}})
def test_metadata_always_exists_on_rest_xml_response(self): # ResponseMetadata is used for more than just the request id. It # should always get populated, even if the request doesn't seem to # have an id. headers = {} parser = parsers.RestXMLParser() parsed = parser.parse( {'body': '', 'headers': headers, 'status_code': 200}, None) expected = { 'ResponseMetadata': { 'HTTPStatusCode': 200, 'HTTPHeaders': headers } } self.assertEqual(parsed, expected)
def test_response_metadata_from_s3_response(self): # Even though s3 is a rest-xml service, it's response metadata # is slightly different. It has two request ids, both come from # the response headers, are both are named differently from other # rest-xml responses. headers = { 'x-amz-id-2': 'second-id', 'x-amz-request-id': 'request-id' } parser = parsers.RestXMLParser() parsed = parser.parse( {'body': '', 'headers': headers, 'status_code': 200}, None) self.assertEqual( parsed, {'ResponseMetadata': {'RequestId': 'request-id', 'HostId': 'second-id', 'HTTPStatusCode': 200, 'HTTPHeaders': headers}})
def test_can_parse_rest_xml_errors(self): body = ( '<ErrorResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">' ' <Error>' ' <Type>Sender</Type>' ' <Code>NoSuchHostedZone</Code>' ' <Message>No hosted zone found with ID: foobar</Message>' ' </Error>' ' <RequestId>bc269cf3-d44f-11e5-8779-2d21c30eb3f1</RequestId>' '</ErrorResponse>' ).encode('utf-8') parser = parsers.RestXMLParser() parsed = parser.parse({ 'body': body, 'headers': {}, 'status_code': 400}, None) self.assertIn('Error', parsed) self.assertEqual(parsed['Error'], { 'Code': 'NoSuchHostedZone', 'Message': 'No hosted zone found with ID: foobar', 'Type': 'Sender', })
def test_can_parse_route53_with_missing_message(self): # The message isn't always in the XML response (or even the headers). # We should be able to handle this gracefully and still at least # populate a "Message" key so that consumers don't have to # conditionally check for this. body = ( '<ErrorResponse>' ' <Error>' ' <Type>Sender</Type>' ' <Code>InvalidInput</Code>' ' </Error>' ' <RequestId>id</RequestId>' '</ErrorResponse>' ).encode('utf-8') parser = parsers.RestXMLParser() parsed = parser.parse({ 'body': body, 'headers': {}, 'status_code': 400}, None) error = parsed['Error'] self.assertEqual(error['Code'], 'InvalidInput') # Even though there's no <Message /> we should # still populate an empty string. self.assertEqual(error['Message'], '')