diff --git a/api/utils.py b/api/utils.py index ae65df5d3d..39bf5e2a83 100644 --- a/api/utils.py +++ b/api/utils.py @@ -160,7 +160,8 @@ def formatWorkOutput( showAll, dbClient, formats=formats, - reader=reader + reader=reader, + request=request ) cls.addWorkMeta(outWork, highlights=highlights) @@ -171,30 +172,28 @@ def formatWorkOutput( #Formatted work with a specific format given elif formats != None and identifiers == None: formattedWork = cls.formatWork( - works, None, showAll, dbClient, formats=formats, reader=reader + works, None, showAll, dbClient, formats=formats, reader=reader, request=request ) formattedWork['editions'].sort( key=lambda x: x['publication_date'] if x['publication_date'] else 9999 ) - return formattedWork #Formatted work with no format specified else: formattedWork = cls.formatWork( - works, None, showAll, dbClient, reader=reader + works, None, showAll, dbClient, reader=reader, request=request ) formattedWork['editions'].sort( key=lambda x: x['publication_date'] if x['publication_date'] else 9999 ) - return formattedWork @classmethod - def formatWork(cls, work, editionIds, showAll, dbClient=None, formats=None, reader=None): + def formatWork(cls, work, editionIds, showAll, dbClient=None, formats=None, reader=None, request=None): workDict = dict(work) workDict['edition_count'] = len(work.editions) workDict['inCollections'] = cls.checkEditionInCollection(work, None, dbClient=dbClient) @@ -224,6 +223,11 @@ def formatWork(cls, work, editionIds, showAll, dbClient=None, formats=None, read None, [e for _, e in orderedEds.items()]) ) + for edition in workDict['editions']: + for item in edition['items']: + if item.get("links"): + # Map over item links and patch with pre-signed URL where necessary + item['links']= list(map(APIUtils.replacePrivateLinkUrl, item['links'], repeat(request))) return workDict @classmethod @@ -247,8 +251,9 @@ def formatEditionOutput( if formattedEdition.get("instances"): for instance in formattedEdition['instances']: for item in instance['items']: - # Map over item links and patch with pre-signed URL where necessary - item['links']= list(map(APIUtils.replacePrivateLinkUrl, item['links'], repeat(request))) + if item.get("links"): + # Map over item links and patch with pre-signed URL where necessary + item['links']= list(map(APIUtils.replacePrivateLinkUrl, item['links'], repeat(request))) return formattedEdition diff --git a/tests/unit/test_api_utils.py b/tests/unit/test_api_utils.py index e79e7268f6..d535ba2306 100644 --- a/tests/unit/test_api_utils.py +++ b/tests/unit/test_api_utils.py @@ -276,7 +276,7 @@ def test_formatWorkOutput_single_work(self, mocker, testApp): assert outWork['uuid'] == 1 assert outWork['editions'][0]['id'] == 'ed3' assert outWork['editions'][2]['id'] == 'ed1' - mockFormat.assert_called_once_with('testWork', None, True, mocker.sentinel.dbClient, reader=None) + mockFormat.assert_called_once_with('testWork', None, True, mocker.sentinel.dbClient, reader=None, request=request) def test_formatWorkOutput_multiple_works(self, mocker, testApp): mockFormat = mocker.patch.object(APIUtils, 'formatWork') @@ -302,8 +302,8 @@ def test_formatWorkOutput_multiple_works(self, mocker, testApp): assert outWorks == ['formattedWork1', 'formattedWork2'] mockFormat.assert_has_calls([ - mocker.call(testWorks[0], 1, True, mocker.sentinel.dbClient, formats=None, reader=None), - mocker.call(testWorks[1], 2, True, mocker.sentinel.dbClient, formats=None, reader=None), + mocker.call(testWorks[0], 1, True, mocker.sentinel.dbClient, formats=None, reader=None, request=request), + mocker.call(testWorks[1], 2, True, mocker.sentinel.dbClient, formats=None, reader=None, request=request), ]) mockAddMeta.assert_has_calls([ @@ -311,10 +311,10 @@ def test_formatWorkOutput_multiple_works(self, mocker, testApp): mocker.call('formattedWork2', highlights='highlight2') ]) - def test_formatWork_showAll(self, testWork, mocker): + def test_formatWork_showAll(self, testWork, mocker, testApp): mockFormatEdition = mocker.patch.object(APIUtils, 'formatEdition') mockFormatEdition.return_value = { - 'edition_id': 'ed1', 'items': ['it1'] + 'edition_id': 'ed1', 'items': [{'item1':'foo'}] } testWork.id = 'testID' @@ -322,54 +322,57 @@ def test_formatWork_showAll(self, testWork, mocker): mockDBClient = mocker.patch('api.blueprints.drbWork.DBClient') mockDBClient.return_value = mockDB - testWorkDict = APIUtils.formatWork(testWork, ['ed1'], True, dbClient=mockDBClient) + with testApp.test_request_context('/'): + testWorkDict = APIUtils.formatWork(testWork, ['ed1'], True, dbClient=mockDBClient, request=request) - assert testWorkDict['uuid'] == 'testUUID' - assert testWorkDict['title'] == 'Test Title' - assert testWorkDict['editions'][0]['edition_id'] == 'ed1' - assert testWorkDict['editions'][0]['items'][0] == 'it1' - assert testWorkDict['edition_count'] == 1 - assert testWorkDict['date_created'] == '2022-05-12T10:00:41' - assert testWorkDict['date_modified'] == '2022-05-13T10:00:44' - mockFormatEdition.assert_called_once() + assert testWorkDict['uuid'] == 'testUUID' + assert testWorkDict['title'] == 'Test Title' + assert testWorkDict['editions'][0]['edition_id'] == 'ed1' + assert testWorkDict['editions'][0]['items'][0] == {'item1':'foo'} + assert testWorkDict['edition_count'] == 1 + assert testWorkDict['date_created'] == '2022-05-12T10:00:41' + assert testWorkDict['date_modified'] == '2022-05-13T10:00:44' + mockFormatEdition.assert_called_once() - def test_formatWork_showAll_false(self, testWork, mocker): + def test_formatWork_showAll_false(self, testWork, mocker, testApp): mockDB = mocker.MagicMock() mockDBClient = mocker.patch('api.blueprints.drbWork.DBClient') mockDBClient.return_value = mockDB - testWork.id = 'testID' - mockFormatEdition = mocker.patch.object(APIUtils, 'formatEdition') - mockFormatEdition.return_value = { - 'edition_id': 'ed1', 'items': ['it1'] - } - testWorkDict = APIUtils.formatWork(testWork, ['ed1'], False, dbClient=mockDBClient) + with testApp.test_request_context('/'): + testWork.id = 'testID' + mockFormatEdition = mocker.patch.object(APIUtils, 'formatEdition') + mockFormatEdition.return_value = { + 'edition_id': 'ed1', 'items': [{'item1':'foo'}] + } + testWorkDict = APIUtils.formatWork(testWork, ['ed1'], False, dbClient=mockDBClient, request=request) - assert testWorkDict['uuid'] == 'testUUID' - assert testWorkDict['title'] == 'Test Title' - assert len(testWorkDict['editions']) == 1 - assert testWorkDict['edition_count'] == 1 - assert testWorkDict['date_created'] == '2022-05-12T10:00:41' - assert testWorkDict['date_modified'] == '2022-05-13T10:00:44' + assert testWorkDict['uuid'] == 'testUUID' + assert testWorkDict['title'] == 'Test Title' + assert len(testWorkDict['editions']) == 1 + assert testWorkDict['edition_count'] == 1 + assert testWorkDict['date_created'] == '2022-05-12T10:00:41' + assert testWorkDict['date_modified'] == '2022-05-13T10:00:44' - def test_formatWork_blocked_edition(self, testWork, mocker): + def test_formatWork_blocked_edition(self, testWork, mocker, testApp): mockDB = mocker.MagicMock() mockDBClient = mocker.patch('api.blueprints.drbWork.DBClient') mockDBClient.return_value = mockDB - testWork.id = 'testID' - testWork.editions[0].items = [] - testWorkDict = APIUtils.formatWork(testWork, ['ed2'], True, dbClient=mockDBClient) - - assert testWorkDict['uuid'] == 'testUUID' - assert testWorkDict['title'] == 'Test Title' - assert len(testWorkDict['editions']) == 0 - assert testWorkDict['edition_count'] == 1 - assert testWorkDict['date_created'] == '2022-05-12T10:00:41' - assert testWorkDict['date_modified'] == '2022-05-13T10:00:44' - - def test_formatWork_ordered_editions(self, testWork, mocker): + with testApp.test_request_context('/'): + testWork.id = 'testID' + testWork.editions[0].items = [] + testWorkDict = APIUtils.formatWork(testWork, ['ed2'], True, dbClient=mockDBClient, request=request) + + assert testWorkDict['uuid'] == 'testUUID' + assert testWorkDict['title'] == 'Test Title' + assert len(testWorkDict['editions']) == 0 + assert testWorkDict['edition_count'] == 1 + assert testWorkDict['date_created'] == '2022-05-12T10:00:41' + assert testWorkDict['date_modified'] == '2022-05-13T10:00:44' + + def test_formatWork_ordered_editions(self, testWork, mocker, testApp): testWork.editions = [mocker.MagicMock(id=1), mocker.MagicMock(id=2)] testWork.id = 'testID' @@ -379,14 +382,15 @@ def test_formatWork_ordered_editions(self, testWork, mocker): mockFormatEdition = mocker.patch.object(APIUtils, 'formatEdition') mockFormatEdition.side_effect = [ - {'edition_id': 'ed1', 'items': ['it1']}, - {'edition_id': 'ed2', 'items': ['it2']} + {'edition_id': 'ed1', 'items': [{'it1':'item'}]}, + {'edition_id': 'ed2', 'items': [{'it2':'item'}]} ] - testWorkDict = APIUtils.formatWork(testWork, [2, 1], True, dbClient=mockDBClient) + with testApp.test_request_context('/'): + testWorkDict = APIUtils.formatWork(testWork, [2, 1], True, dbClient=mockDBClient, request=request) - assert testWorkDict['editions'][0]['edition_id'] == 'ed2' - assert testWorkDict['editions'][1]['edition_id'] == 'ed1' + assert testWorkDict['editions'][0]['edition_id'] == 'ed2' + assert testWorkDict['editions'][1]['edition_id'] == 'ed1' def test_formatEditionOutput(self, mocker, testApp): mockFormatEdition = mocker.patch.object(APIUtils, 'formatEdition') @@ -668,8 +672,7 @@ def test_getPresignedUrlFromNons3Url(self): with pytest.raises(ValueError): APIUtils.getPresignedUrlFromObjectUrl({"Some Client"}, "https://example.com") - def test_ReplaceWithPrivateLink(self): - testApp = Flask('test') + def test_ReplaceWithPrivateLink(self, testApp): with testApp.test_request_context('/', base_url="http://localhost:5000"): testLoginLinkObj = { 'link_id':'12345', @@ -684,7 +687,7 @@ def test_ReplaceWithPrivateLink(self): 'flags':{'nypl_login': True} } - def test_noLinkReplacement(self): + def test_noLinkReplacement(self, testApp): testElectronicDeliveryLink = { 'link_id':'6789', 'media_type' : "application/html+edd", @@ -695,7 +698,6 @@ def test_noLinkReplacement(self): "reader": False } } - testApp = Flask('test') with testApp.test_request_context('/'): assert APIUtils.replacePrivateLinkUrl( testElectronicDeliveryLink, request