Skip to content

Commit

Permalink
implement feedback and move test_only_s3_targets_expires_header() to …
Browse files Browse the repository at this point in the history
…test_s3.py
  • Loading branch information
alexgromero committed Sep 4, 2024
1 parent 5741723 commit 0f0861d
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 87 deletions.
21 changes: 10 additions & 11 deletions botocore/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,17 +536,6 @@ def _handle_float(self, shape, text):
def _handle_timestamp(self, shape, text):
return self._timestamp_parser(text)

@_text_content
def _handle_expires_timestamp(self, shape, text):
try:
return self._handle_timestamp(shape, text)
except (ValueError, RuntimeError):
LOG.warning(
f'Failed to parse the "Expires" member as a timestamp: {text}. '
f'The unparsed value is available in the response under "ExpiresString".'
)
return None

@_text_content
def _handle_integer(self, shape, text):
return int(text)
Expand Down Expand Up @@ -1023,6 +1012,16 @@ def _handle_list(self, shape, node):
node = [e.strip() for e in node.split(',')]
return super()._handle_list(shape, node)

def _handle_expires_timestamp(self, shape, timestamp):
try:
return self._handle_timestamp(shape, timestamp)
except (ValueError, RuntimeError):
LOG.warning(
f'Failed to parse the "Expires" member as a timestamp: {timestamp}. '
f'The unparsed value is available in the response under "ExpiresString".'
)
return None


class RestJSONParser(BaseRestParser, BaseJSONParser):
EVENT_STREAM_PARSER_CLS = EventStreamJSONParser
Expand Down
40 changes: 40 additions & 0 deletions tests/functional/test_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -3718,3 +3718,43 @@ def _verify_bucket_and_key_in_context(self, request, **kwargs):
request.context['input_params']['Bucket'], self.BUCKET
)
self.assertEqual(request.context['input_params']['Key'], self.KEY)


@pytest.mark.validates_models
def test_only_s3_targets_expires_header():
"""
This test verifies that S3 is the only service that uses the 'expires' header
in their response. If any other service is found to target this header in any
of their output shapes, the test will fail and alert us of the change.
"""
session = botocore.session.get_session()
loader = session.get_component('data_loader')
services = loader.list_available_services('service-2')
services_that_target_expires_header = set()

for service in services:
service_model = session.get_service_model(service)
for operation in service_model.operation_names:
operation_model = service_model.operation_model(operation)
if output_shape := operation_model.output_shape:
if _shape_targets_expires_header(output_shape):
services_that_target_expires_header.add(service)
assert services_that_target_expires_header == {'s3'}, (
f"Expected only 's3' to target the 'Expires' header.\n"
f"Actual services that target the 'Expires' header: {services_that_target_expires_header}\n"
f"Please review the service models to verify this change."
)


def _shape_targets_expires_header(shape):
for member_shape in shape.members.values():
location = member_shape.serialization.get('location')
location_name = member_shape.serialization.get('name')
if (
location
and location.lower() == 'header'
and location_name
and location_name.lower() == 'expires'
):
return True
return False
54 changes: 0 additions & 54 deletions tests/functional/test_service_headers.py

This file was deleted.

31 changes: 9 additions & 22 deletions tests/unit/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1784,29 +1784,16 @@ def test_remove_bucket_from_url_paths_from_model(

@pytest.fixture()
def document_s3_expires_mocks():
section = mock.Mock()
parent = mock.Mock()
param_line = mock.Mock()
param_section = mock.Mock()
doc_section = mock.Mock()
new_param_line = mock.Mock()
new_param_section = mock.Mock()
response_example_event = (
'docs.response-example.s3.TestOperation.complete-section'
)
response_params_event = (
'docs.response-params.s3.TestOperation.complete-section'
)
return {
'section': section,
'parent': parent,
'param_line': param_line,
'param_section': param_section,
'doc_section': doc_section,
'new_param_line': new_param_line,
'new_param_section': new_param_section,
'response_example_event': response_example_event,
'response_params_event': response_params_event,
'section': mock.Mock(),
'parent': mock.Mock(),
'param_line': mock.Mock(),
'param_section': mock.Mock(),
'doc_section': mock.Mock(),
'new_param_line': mock.Mock(),
'new_param_section': mock.Mock(),
'response_example_event': 'docs.response-example.s3.TestOperation.complete-section',
'response_params_event': 'docs.response-params.s3.TestOperation.complete-section',
}


Expand Down

0 comments on commit 0f0861d

Please sign in to comment.