Skip to content

Commit 5120b78

Browse files
committed
feat: add uniqueness, returned and case_exact filters to iter_paths
1 parent 1b50412 commit 5120b78

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

scim2_models/path.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
from .utils import _to_camel
1919

2020
if TYPE_CHECKING:
21+
from .annotations import CaseExact
2122
from .annotations import Mutability
2223
from .annotations import Required
24+
from .annotations import Returned
25+
from .annotations import Uniqueness
2326
from .resources.resource import Resource
2427

2528
from .exceptions import InvalidPathException
@@ -666,6 +669,9 @@ def iter_paths(
666669
include_extensions: bool = True,
667670
required: "list[Required] | None" = None,
668671
mutability: "list[Mutability] | None" = None,
672+
uniqueness: "list[Uniqueness] | None" = None,
673+
returned: "list[Returned] | None" = None,
674+
case_exact: "list[CaseExact] | None" = None,
669675
) -> "Iterator[Path[ResourceT]]":
670676
"""Iterate over all paths for the bound model and its extensions.
671677
@@ -675,10 +681,16 @@ def iter_paths(
675681
:param include_extensions: Whether to include extension attributes.
676682
:param required: Filter by Required annotation values (e.g., [Required.true]).
677683
:param mutability: Filter by Mutability annotation values (e.g., [Mutability.read_write]).
684+
:param uniqueness: Filter by Uniqueness annotation values (e.g., [Uniqueness.server]).
685+
:param returned: Filter by Returned annotation values (e.g., [Returned.always]).
686+
:param case_exact: Filter by CaseExact annotation values (e.g., [CaseExact.true]).
678687
:yields: Path instances for each attribute matching the filters.
679688
"""
689+
from .annotations import CaseExact
680690
from .annotations import Mutability
681691
from .annotations import Required
692+
from .annotations import Returned
693+
from .annotations import Uniqueness
682694
from .attributes import ComplexAttribute
683695
from .resources.resource import Extension
684696
from .resources.resource import Resource
@@ -698,6 +710,22 @@ def matches_filters(target_model: type[BaseModel], field_name: str) -> bool:
698710
)
699711
if field_mutability not in mutability:
700712
return False
713+
if uniqueness is not None:
714+
field_uniqueness = target_model.get_field_annotation(
715+
field_name, Uniqueness
716+
)
717+
if field_uniqueness not in uniqueness:
718+
return False
719+
if returned is not None:
720+
field_returned = target_model.get_field_annotation(field_name, Returned)
721+
if field_returned not in returned:
722+
return False
723+
if case_exact is not None:
724+
field_case_exact = target_model.get_field_annotation(
725+
field_name, CaseExact
726+
)
727+
if field_case_exact not in case_exact:
728+
return False
701729
return True
702730

703731
def iter_model_paths(

tests/test_path.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pydantic
66
import pytest
77

8+
from scim2_models import CaseExact
89
from scim2_models import Email
910
from scim2_models import EnterpriseUser
1011
from scim2_models import Group
@@ -14,6 +15,8 @@
1415
from scim2_models import Name
1516
from scim2_models import PathNotFoundException
1617
from scim2_models import Required
18+
from scim2_models import Returned
19+
from scim2_models import Uniqueness
1720
from scim2_models import User
1821
from scim2_models.base import BaseModel
1922
from scim2_models.path import Path
@@ -1419,6 +1422,44 @@ def test_iter_paths_filter_skips_non_matching_subattributes():
14191422
assert len(paths_with_filter) < len(paths_no_filter)
14201423

14211424

1425+
def test_iter_paths_filter_by_uniqueness():
1426+
"""iter_paths with uniqueness filter only yields matching paths."""
1427+
paths = list(
1428+
Path[User].iter_paths(
1429+
include_subattributes=False, uniqueness=[Uniqueness.server]
1430+
)
1431+
)
1432+
path_strings = [str(p) for p in paths]
1433+
1434+
assert "userName" in path_strings
1435+
for path in paths:
1436+
assert path.get_annotation(Uniqueness) == Uniqueness.server
1437+
1438+
1439+
def test_iter_paths_filter_by_returned():
1440+
"""iter_paths with returned filter only yields matching paths."""
1441+
paths = list(
1442+
Path[User].iter_paths(include_subattributes=False, returned=[Returned.never])
1443+
)
1444+
path_strings = [str(p) for p in paths]
1445+
1446+
assert "password" in path_strings
1447+
for path in paths:
1448+
assert path.get_annotation(Returned) == Returned.never
1449+
1450+
1451+
def test_iter_paths_filter_by_case_exact():
1452+
"""iter_paths with case_exact filter only yields matching paths."""
1453+
paths = list(
1454+
Path[User].iter_paths(include_subattributes=False, case_exact=[CaseExact.true])
1455+
)
1456+
path_strings = [str(p) for p in paths]
1457+
1458+
assert "externalId" in path_strings
1459+
for path in paths:
1460+
assert path.get_annotation(CaseExact) == CaseExact.true
1461+
1462+
14221463
def test_path_init_with_path_object():
14231464
"""Path can be initialized with another Path object."""
14241465
original = Path("userName")

0 commit comments

Comments
 (0)