-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
4f4295e
commit ea60979
Showing
45 changed files
with
2,249 additions
and
928 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,81 @@ | ||
from __future__ import annotations | ||
|
||
import json | ||
from typing import TYPE_CHECKING | ||
|
||
import flask | ||
from flask import Response | ||
from flask_babel import lazy_gettext as _ | ||
from pydantic import ValidationError | ||
from sqlalchemy.orm import Session | ||
|
||
from palace.manager.api.controller.circulation_manager import ( | ||
CirculationManagerController, | ||
) | ||
from palace.manager.api.odl.api import OPDS2WithODLApi | ||
from palace.manager.api.problem_details import ( | ||
INVALID_LOAN_FOR_ODL_NOTIFICATION, | ||
NO_ACTIVE_LOAN, | ||
) | ||
from palace.manager.core.problem_details import INVALID_INPUT | ||
from palace.manager.opds.lcp.status import LoanStatus | ||
from palace.manager.service.integration_registry.license_providers import ( | ||
LicenseProvidersRegistry, | ||
) | ||
from palace.manager.sqlalchemy.model.library import Library | ||
from palace.manager.sqlalchemy.model.patron import Loan | ||
from palace.manager.sqlalchemy.util import get_one | ||
from palace.manager.util.log import LoggerMixin | ||
from palace.manager.util.problem_detail import ProblemDetail | ||
|
||
if TYPE_CHECKING: | ||
from palace.manager.api.circulation_manager import CirculationManager | ||
|
||
|
||
class ODLNotificationController(CirculationManagerController): | ||
class ODLNotificationController(LoggerMixin): | ||
"""Receive notifications from an ODL distributor when the | ||
status of a loan changes. | ||
""" | ||
|
||
def notify(self, loan_id): | ||
library = flask.request.library | ||
status_doc = flask.request.data | ||
loan = get_one(self._db, Loan, id=loan_id) | ||
def __init__( | ||
self, | ||
db: Session, | ||
manager: CirculationManager, | ||
registry: LicenseProvidersRegistry, | ||
) -> None: | ||
self.db = db | ||
self.manager = manager | ||
self.registry = registry | ||
|
||
if not loan: | ||
return NO_ACTIVE_LOAN.detailed(_("No loan was found for this identifier.")) | ||
|
||
collection = loan.license_pool.collection | ||
if collection.protocol != OPDS2WithODLApi.label(): | ||
return INVALID_LOAN_FOR_ODL_NOTIFICATION | ||
|
||
api = self.manager.circulation_apis[library.id].api_for_license_pool( | ||
def get_api(self, library: Library, loan: Loan) -> OPDS2WithODLApi: | ||
return self.manager.circulation_apis[library.id].api_for_license_pool( # type: ignore[no-any-return] | ||
loan.license_pool | ||
) | ||
api.update_loan(loan, json.loads(status_doc)) | ||
return Response(_("Success"), 200) | ||
|
||
def notify(self, loan_id: int) -> Response | ProblemDetail: | ||
library = flask.request.library # type: ignore[attr-defined] | ||
status_doc_json = flask.request.data | ||
loan = get_one(self.db, Loan, id=loan_id) | ||
|
||
try: | ||
status_doc = LoanStatus.model_validate_json(status_doc_json) | ||
except ValidationError as e: | ||
self.log.exception(f"Unable to parse loan status document. {e}") | ||
return INVALID_INPUT | ||
|
||
# We don't have a record of this loan. This likely means that the loan has been returned | ||
# and our local record has been deleted. This is expected, except in the case where the | ||
# distributor thinks the loan is still active. | ||
if loan is None and status_doc.active: | ||
return NO_ACTIVE_LOAN.detailed( | ||
_("No loan was found for this identifier."), status_code=404 | ||
) | ||
|
||
if loan: | ||
integration = loan.license_pool.collection.integration_configuration | ||
if ( | ||
not integration.protocol | ||
or self.registry.get(integration.protocol) != OPDS2WithODLApi | ||
): | ||
return INVALID_LOAN_FOR_ODL_NOTIFICATION | ||
|
||
api = self.get_api(library, loan) | ||
api.update_loan(loan, status_doc) | ||
|
||
return Response(status=204) |
Oops, something went wrong.