Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 13 additions & 15 deletions src/palace/manager/feed/serializer/opds2.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,8 @@ def _serialize_publication_metadata(
def _serialize_image_links(self, links: Iterable[Link]) -> list[opds2.Link]:
return [self._serialize_link(link) for link in links]

def _serialize_publication_links(
self, data: WorkEntryData
) -> list[opds2.StrictLink]:
links: list[opds2.StrictLink] = []
def _serialize_publication_links(self, data: WorkEntryData) -> list[opds2.Link]:
links: list[opds2.Link] = []
for link in data.other_links:
if link.rel is None:
self.log.warning(f"Skipping OPDS2 link without rel: {link.href}")
Expand All @@ -234,7 +232,7 @@ def _serialize_publication_links(
self.log.error(f"Skipping OPDS2 link without type: {link.href}")
continue
links.append(
self._strict_link(
self._link(
href=link.href,
rel=link.rel,
type=resolved_type,
Expand All @@ -257,11 +255,11 @@ def _serialize_link(self, link: Link) -> opds2.Link:
title=link.title,
)

def _serialize_acquisition_link(self, link: Acquisition) -> opds2.StrictLink | None:
def _serialize_acquisition_link(self, link: Acquisition) -> opds2.Link | None:
link_type = self._acquisition_link_type(link)
if link_type is None:
return None
return self._strict_link(
return self._link(
href=link.href,
rel=link.rel or opds2.AcquisitionLinkRelations.acquisition,
type=link_type,
Expand Down Expand Up @@ -363,21 +361,21 @@ def entry_content_type(cls) -> str:
def to_string(cls, data: dict[str, Any]) -> str:
return json.dumps(data, indent=2)

def _serialize_feed_links(self, feed: FeedData) -> list[opds2.StrictLink]:
links: list[opds2.StrictLink] = []
def _serialize_feed_links(self, feed: FeedData) -> list[opds2.Link]:
links: list[opds2.Link] = []
for link in feed.links:
strict = self._serialize_feed_link(link)
if strict is not None:
links.append(strict)

return links

def _serialize_feed_link(self, link: Link) -> opds2.StrictLink | None:
def _serialize_feed_link(self, link: Link) -> opds2.Link | None:
if link.rel is None:
self.log.warning(f"Skipping OPDS2 feed link without rel: {link.href}")
return None
resolved_type = self._resolve_type(link.type)
return self._strict_link(
return self._link(
href=link.href,
rel=link.rel,
type=resolved_type or self.content_type(),
Expand Down Expand Up @@ -470,7 +468,7 @@ def _serialize_entry_groups(
opds2.PublicationsGroup(
metadata=opds2.FeedMetadata(title=group.title),
links=[
opds2.StrictLink(
opds2.Link(
href=group.href,
rel="self",
type=self.content_type(),
Expand Down Expand Up @@ -539,7 +537,7 @@ def _link_properties(
palace_default=palace_default,
)

def _strict_link(
def _link(
self,
*,
href: str,
Expand All @@ -548,8 +546,8 @@ def _strict_link(
title: str | None = None,
properties: opds2.LinkProperties,
templated: bool = False,
) -> opds2.StrictLink:
return opds2.StrictLink(
) -> opds2.Link:
return opds2.Link(
href=href,
rel=rel,
type=type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ def _extract_bibliographic_links(

def _extract_opds2_formats(
self,
links: Sequence[opds2.StrictLink],
links: Sequence[opds2.Link],
rights_uri: str,
) -> list[FormatData]:
"""Find circulation formats in non open-access acquisition links.
Expand Down
6 changes: 3 additions & 3 deletions src/palace/manager/opds/odl/odl.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ class License(BaseOpdsModel):
"""

metadata: LicenseMetadata
links: Annotated[CompactCollection[opds2.StrictLink], Field(min_length=1)]
links: Annotated[CompactCollection[opds2.Link], Field(min_length=1)]

@field_validator("links")
@classmethod
def validate_links(
cls, value: CompactCollection[opds2.StrictLink]
) -> CompactCollection[opds2.StrictLink]:
cls, value: CompactCollection[opds2.Link]
) -> CompactCollection[opds2.Link]:
"""
Must have a self link and at least one acquisition link.

Expand Down
32 changes: 9 additions & 23 deletions src/palace/manager/opds/opds2.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
CompactCollection,
validate_unique_links,
)
from palace.manager.opds.util import StrOrTuple, drop_if_falsy, obj_or_tuple_to_tuple
from palace.manager.opds.util import drop_if_falsy, obj_or_tuple_to_tuple
from palace.manager.util.datetime_helpers import utc_now


Expand Down Expand Up @@ -198,20 +198,6 @@ class Link(rwpm.Link):
properties: LinkProperties = Field(default_factory=LinkProperties)


class StrictLink(Link):
"""
OPDS2 link with strict validation.

These links require that the rel and type fields are present.
"""

rel: StrOrTuple[str]
type: str

alternate: CompactCollection[StrictLink] = Field(default_factory=CompactCollection)
children: CompactCollection[StrictLink] = Field(default_factory=CompactCollection)


class TitleLink(Link):
"""
OPDS2 link with title.
Expand Down Expand Up @@ -299,7 +285,7 @@ def content_type(cls) -> str:

metadata: PublicationMetadata
images: CompactCollection[Link]
links: CompactCollection[StrictLink] = Field(default_factory=CompactCollection)
links: CompactCollection[Link] = Field(default_factory=CompactCollection)

_validate_images = field_validator("images")(validate_images)

Expand All @@ -312,13 +298,13 @@ class Publication(BasePublication):
https://github.com/opds-community/drafts/blob/main/schema/publication.schema.json
"""

links: Annotated[CompactCollection[StrictLink], Field(min_length=1)]
links: Annotated[CompactCollection[Link], Field(min_length=1)]

@field_validator("links")
@classmethod
def validate_acquisition_link(
cls, value: CompactCollection[StrictLink]
) -> CompactCollection[StrictLink]:
cls, value: CompactCollection[Link]
) -> CompactCollection[Link]:
"""
Must have at least one acquisition link.

Expand Down Expand Up @@ -364,7 +350,7 @@ class PublicationsGroup(BaseOpdsModel):
"""

metadata: FeedMetadata
links: CompactCollection[StrictLink] = Field(default_factory=CompactCollection)
links: CompactCollection[Link] = Field(default_factory=CompactCollection)
publications: Annotated[list[Publication], Field(min_length=1)]

_validate_unique_links = field_validator("links")(validate_unique_links)
Expand All @@ -379,7 +365,7 @@ class NavigationGroup(BaseOpdsModel):
"""

metadata: FeedMetadata
links: CompactCollection[StrictLink] = Field(default_factory=CompactCollection)
links: CompactCollection[Link] = Field(default_factory=CompactCollection)
navigation: CompactCollection[TitleLink] = Field(..., min_length=1)

_validate_unique_links = field_validator("links")(validate_unique_links)
Expand All @@ -399,7 +385,7 @@ def content_type(cls) -> str:
return "application/opds+json"

metadata: FeedMetadata
links: CompactCollection[StrictLink]
links: CompactCollection[Link]
navigation: CompactCollection[TitleLink] = Field(default_factory=CompactCollection)
publications: list[Publication] = Field(default_factory=list)
facets: list[Facet] = Field(default_factory=list)
Expand Down Expand Up @@ -458,7 +444,7 @@ def content_type(cls) -> str:
return "application/opds+json"

metadata: FeedMetadata
links: CompactCollection[StrictLink]
links: CompactCollection[Link]
publications: list[T]

_validate_links = field_validator("links")(validate_self_link)
Expand Down
8 changes: 4 additions & 4 deletions tests/manager/integration/license/opds/odl/test_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from palace.manager.opds.odl.odl import License, LicenseMetadata, Publication
from palace.manager.opds.odl.protection import Protection
from palace.manager.opds.odl.terms import Terms
from palace.manager.opds.opds2 import PublicationFeedNoValidation, StrictLink
from palace.manager.opds.opds2 import Link, PublicationFeedNoValidation
from palace.manager.opds.schema_org import Audience
from palace.manager.sqlalchemy.constants import EditionConstants, MediaTypes
from palace.manager.sqlalchemy.model.contributor import Contributor
Expand All @@ -30,15 +30,15 @@ def __init__(self) -> None:
self.license_identifier: str = "test-license-123"
self.publication_identifier: str = "urn:isbn:9780306406157"

def license_links(self) -> list[StrictLink]:
def license_links(self) -> list[Link]:
"""Create standard license links for testing."""
return [
StrictLink(
Link(
rel=rwpm.LinkRelations.self,
type=LicenseInfo.content_type(),
href="http://example.org/license",
),
StrictLink(
Link(
rel=opds2.AcquisitionLinkRelations.borrow,
type=LoanStatus.content_type(),
href="http://example.org/borrow",
Expand Down
6 changes: 3 additions & 3 deletions tests/manager/integration/license/opds/test_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from palace.manager.opds.authentication import AuthenticationDocument
from palace.manager.opds.opds2 import (
FeedMetadata,
Link,
PublicationFeedNoValidation,
StrictLink,
)
from tests.fixtures.http import MockHttpClientFixture
from tests.mocks.mock import MockRequestsResponse
Expand Down Expand Up @@ -122,12 +122,12 @@ def opds2_feed_with_auth_link(self) -> str:
metadata=FeedMetadata(title="Test Feed"),
publications=[],
links=[
StrictLink(
Link(
rel="http://opds-spec.org/auth/document",
href=self.auth_doc_url,
type=AuthenticationDocument.content_type(),
),
StrictLink(
Link(
rel="self",
href=self.auth_doc_url,
type=PublicationFeedNoValidation.content_type(),
Expand Down
Loading
Loading