Skip to content

Commit

Permalink
Merge 9633a76 into 1375a0c
Browse files Browse the repository at this point in the history
  • Loading branch information
rohanpm committed Aug 24, 2021
2 parents 1375a0c + 9633a76 commit 3ec14f3
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 3 deletions.
2 changes: 1 addition & 1 deletion pubtools/pulplib/_impl/client/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def accumulate_from_match(self, match_expr):
raise ValueError(
(
"Can't serialize criteria for Pulp query; too complicated. "
"Try simplifying the query with respect to content_type_id."
"Try simplifying the query with respect to unit_type/content_type_id."
)
)

Expand Down
37 changes: 36 additions & 1 deletion pubtools/pulplib/_impl/criteria.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

from pubtools.pulplib._impl import compat_attr as attr

from .model.unit import type_ids_for_class


class Criteria(object):
"""Represents a Pulp search criteria.
Expand All @@ -24,7 +26,7 @@ class Criteria(object):
or used directly. Instances of this class should be obtained and
composed by calls to the documented class methods.
Example:
Example - searching a repository:
.. code-block:: python
# With Pulp 2.x / mongo, this is roughly equivalent
Expand All @@ -40,6 +42,18 @@ class Criteria(object):
# criteria may now be used with client to execute a search
repos = client.search_repository(crit)
Example - searching across all repos for a specific content type:
.. code-block:: python
crit = Criteria.and_(
Criteria.with_unit_type(RpmUnit),
Criteria.with_field("sha256sum", Matcher.in_([
"49ae93732fcf8d63fe1cce759664982dbd5b23161f007dba8561862adc96d063",
"6b30e91df993d96df0bef0f9d232d1068fa2f7055f13650208d77b43cd7c99f6"])))
# Will find RpmUnit instances with above sums
units = client.search_content(crit)
"""

exists = object()
Expand Down Expand Up @@ -87,6 +101,27 @@ def with_field(cls, field_name, field_value):
"""
return FieldMatchCriteria(field_name, field_value)

@classmethod
def with_unit_type(cls, unit_type):
"""Args:
unit_type (class)
A subclass of :class:`~pubtools.pulplib.Unit`.
Returns:
Criteria
criteria for finding units of type ``unit_type``.
.. versionadded:: 2.14.0
"""

# This is just a thin wrapper for searching on content_type_id which allows
# the caller to avoid having to handle the (unit class <=> type id) mapping.
type_ids = type_ids_for_class(unit_type)
if not type_ids:
raise TypeError("Expected a Unit type, got: %s" % repr(unit_type))

return FieldMatchCriteria("content_type_id", Matcher.in_(type_ids))

@classmethod
def with_field_in(cls, field_name, field_value):
warnings.warn(
Expand Down
2 changes: 1 addition & 1 deletion pubtools/pulplib/_impl/model/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .base import Unit
from .base import Unit, type_ids_for_class
from .file import FileUnit
from .rpm import RpmUnit
from .modulemd import ModulemdUnit
Expand Down
12 changes: 12 additions & 0 deletions pubtools/pulplib/_impl/model/unit/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ def decorate(klass):
return decorate


def type_ids_for_class(unit_class):
# Given a concrete Unit subclass, returns those Pulp type id(s)
# which may be used to find/load an object of that class.
out = []

for pulp_type, klass in UNIT_CLASSES.items():
if klass is unit_class:
out.append(pulp_type)

return sorted(out)


@attr.s(kw_only=True, frozen=True)
class Unit(PulpObject):
"""Represents a Pulp unit (a single piece of content).
Expand Down
57 changes: 57 additions & 0 deletions tests/client/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,3 +590,60 @@ def test_can_search_content_pagination(client, requests_mocker):
assert [h.url for h in requests_mocker.request_history].count(
"https://pulp.example.com/pulp/api/v2/content/units/srpm/search/"
) == 2


def test_can_search_content_by_type(client, requests_mocker):
"""search_content can search for a specified unit type."""
requests_mocker.get(
"https://pulp.example.com/pulp/api/v2/plugins/types/",
json=[{"id": "rpm"}, {"id": "srpm"}, {"id": "iso"}],
)
# Note although "iso" is supported, we don't mock the search URL for
# that content type, thus proving that we don't query it if we search
# for a specific unit_type
requests_mocker.post(
"https://pulp.example.com/pulp/api/v2/content/units/rpm/search/",
json=RPM_TEST_UNITS,
)
requests_mocker.post(
"https://pulp.example.com/pulp/api/v2/content/units/srpm/search/",
json=SRPM_TEST_UNITS,
)

units = client.search_content(Criteria.with_unit_type(RpmUnit))

# It should have returned the expected units
assert sorted(units) == [
RpmUnit(
content_type_id="srpm",
sha256sum="4f5a3a0da6f404f6d9988987cd75f13982bd655a0a4f692406611afbbc597679",
arch="src",
epoch="0",
name="glibc",
release="2.57.el4.1",
repository_memberships=["fake-repository-id-3"],
sourcerpm=None,
version="2.3.4",
),
RpmUnit(
sha256sum="4f5a3a0da6f404f6d9988987cd75f13982bd655a0a4f692406611afbbc597679",
arch="ia64",
epoch="0",
name="glibc-headers",
release="2.57.el4.1",
repository_memberships=["fake-repository-id-3"],
sourcerpm="glibc-2.3.4-2.57.el4.1.src.rpm",
version="2.3.4",
),
RpmUnit(
sha1sum="ca995eb1a635c97393466f67aaec8e9e753b8ed5",
sha256sum="1c4baac658fd56e6ec9cca37f440a4bd8c9c0b02a21f41b30b8ea17b402a1907",
arch="i386",
epoch="0",
name="gnu-efi-debuginfo",
release="1.1",
repository_memberships=["fake-repository-id-1", "fake-repository-id-2"],
sourcerpm="gnu-efi-3.0c-1.1.src.rpm",
version="3.0c",
),
]
7 changes: 7 additions & 0 deletions tests/criteria/test_criteria.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ def test_field_in_str_invalid():
with pytest.raises(ValueError) as exc_info:
Criteria.with_field_in("x", "someval")
assert "Must be an iterable: 'someval'" in str(exc_info.value)


def test_unit_type_invalid():
"""Criteria.with_unit_type raises if provided value isn't a unit subclass."""
with pytest.raises(TypeError) as exc_info:
Criteria.with_unit_type([1, 2, 3])
assert "Expected a Unit type, got: [1, 2, 3]" in str(exc_info.value)
15 changes: 15 additions & 0 deletions tests/fake/test_fake_search_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@ def test_search_content_by_unit_field(populated_repo):
]


def test_search_content_by_unit_type(populated_repo):
"""search_content on unit_type returns only units of that type"""

crit = Criteria.with_unit_type(ModulemdUnit)
units = list(populated_repo.search_content(crit))
assert sorted(units) == [
ModulemdUnit(
name="module1", stream="s1", version=1234, context="a1b2", arch="x86_64"
),
ModulemdUnit(
name="module2", stream="s2", version=1234, context="a1b2", arch="x86_64"
),
]


def test_search_content_mixed_fields(populated_repo):
"""search_content crossing multiple fields and types returns matching units"""

Expand Down

0 comments on commit 3ec14f3

Please sign in to comment.