diff --git a/src/palace/manager/api/controller/loan.py b/src/palace/manager/api/controller/loan.py index 38c4662718..02dddab255 100644 --- a/src/palace/manager/api/controller/loan.py +++ b/src/palace/manager/api/controller/loan.py @@ -116,11 +116,22 @@ def borrow( return loan_or_hold_or_pd loan_or_hold = loan_or_hold_or_pd - # At this point we have either a loan or a hold. If a loan, serve - # a feed that tells the patron how to fulfill the loan. If a hold, - # serve a feed that talks about the hold. - # We also need to drill in the Accept header, so that it eventually - # gets sent to core.feed.opds.BaseOPDSFeed.entry_as_response + # At this point we have either a loan or a hold. + + # If it is a new loan or hold, queue up a task to sync the patron's activity with the remote. + # This way we are sure we have the most up-to-date information. + if is_new and self.circulation.supports_patron_activity( + loan_or_hold.license_pool + ): + sync_patron_activity.apply_async( + (loan_or_hold.license_pool.collection.id, patron.id, credential), + {"force": True}, + countdown=5, + ) + + # If we have a loan, serve a feed that tells the patron how to fulfill the loan. If a hold, + # serve a feed that talks about the hold. We also need to drill in the Accept header, so that + # it eventually gets sent to core.feed.opds.BaseOPDSFeed.entry_as_response response_kwargs = { "status": 201 if is_new else 200, "mime_types": flask.request.accept_mimetypes, diff --git a/tests/manager/api/controller/test_loan.py b/tests/manager/api/controller/test_loan.py index 950ed2b24c..5b3a841d5a 100644 --- a/tests/manager/api/controller/test_loan.py +++ b/tests/manager/api/controller/test_loan.py @@ -286,10 +286,13 @@ def test_borrow_success( # Create a new loan. with loan_fixture.request_context_with_library("/", headers=headers): - loan_fixture.manager.loans.authenticated_patron_from_request() - borrow_response = loan_fixture.manager.loans.borrow( - loan_fixture.identifier_type, loan_fixture.identifier_identifier - ) + patron = loan_fixture.manager.loans.authenticated_patron_from_request() + with patch( + "palace.manager.api.controller.loan.sync_patron_activity" + ) as sync_task: + borrow_response = loan_fixture.manager.loans.borrow( + loan_fixture.identifier_type, loan_fixture.identifier_identifier + ) loan = get_one( loan_fixture.db.session, Loan, license_pool=loan_fixture.pool ) @@ -298,6 +301,17 @@ def test_borrow_success( assert isinstance(borrow_response, FlaskResponse) assert 201 == borrow_response.status_code + # And queue up a task to sync the patron's activity. + sync_task.apply_async.assert_called_once_with( + ( + loan_fixture.pool.collection.id, + patron.id, + loan_fixture.valid_credentials["password"], + ), + {"force": True}, + countdown=5, + ) + # A loan has been created for this license pool. assert loan is not None # The loan has yet to be fulfilled. @@ -310,9 +324,12 @@ def test_borrow_success( # Borrow again with an existing loan. with loan_fixture.request_context_with_library("/", headers=headers): loan_fixture.manager.loans.authenticated_patron_from_request() - borrow_response = loan_fixture.manager.loans.borrow( - loan_fixture.identifier_type, loan_fixture.identifier_identifier - ) + with patch( + "palace.manager.api.controller.loan.sync_patron_activity" + ) as sync_task: + borrow_response = loan_fixture.manager.loans.borrow( + loan_fixture.identifier_type, loan_fixture.identifier_identifier + ) # A loan has been created for this license pool. loan = get_one( @@ -322,6 +339,9 @@ def test_borrow_success( assert isinstance(borrow_response, OPDSEntryResponse) assert 200 == borrow_response.status_code + # Because the loan was existing, we didn't queue up a task to sync the patron's activity. + sync_task.apply_async.assert_not_called() + # There is still a loan that has not yet been fulfilled. assert loan is not None assert loan.fulfillment is None @@ -490,11 +510,16 @@ def test_borrow_and_fulfill_with_streaming_delivery_mechanism( utc_now() + datetime.timedelta(seconds=3600), ), ) - borrow_response = loan_fixture.manager.loans.borrow( - identifier.type, identifier.identifier - ) + with patch( + "palace.manager.api.controller.loan.sync_patron_activity" + ) as sync_task: + borrow_response = loan_fixture.manager.loans.borrow( + identifier.type, identifier.identifier + ) assert isinstance(borrow_response, Response) + sync_task.apply_async.assert_called_once() + # A loan has been created for this license pool. loan = get_one(loan_fixture.db.session, Loan, license_pool=pool) assert loan is not None @@ -1709,9 +1734,10 @@ def create_work_and_return_license_pool_and_loan_info(**kwargs): loan_fixture.manager.d_circulation.queue_checkout(license_pool, loan_info) - response = loan_fixture.manager.loans.borrow( - license_pool.identifier.type, license_pool.identifier.identifier - ) + with patch("palace.manager.api.controller.loan.sync_patron_activity"): + response = loan_fixture.manager.loans.borrow( + license_pool.identifier.type, license_pool.identifier.identifier + ) loan = get_one(loan_fixture.db.session, Loan, license_pool=license_pool) assert loan is not None