Skip to content
Merged
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
4 changes: 4 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Added
- Support for Python 3.14.
- Compile regexes.

Fixed
^^^^^
- Sub-attributes of requested complex attributes are now included in responses. :issue:`114`

Removed
^^^^^^^
- Support for Python 3.9.
Expand Down
25 changes: 16 additions & 9 deletions scim2_models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,21 @@
from scim2_models.utils import _to_camel


def _contains_attribute_or_subattributes(
attribute_urns: list[str], attribute_urn: str
) -> bool:
return attribute_urn in attribute_urns or any(
item.startswith(f"{attribute_urn}.") or item.startswith(f"{attribute_urn}:")
for item in attribute_urns
def _is_attribute_requested(requested_urns: list[str], current_urn: str) -> bool:
"""Check if an attribute should be included based on the requested URNs.

Returns True if:
- The current attribute is explicitly requested
- A sub-attribute of the current attribute is requested
- The current attribute is a sub-attribute of a requested attribute
"""
return (
current_urn in requested_urns
or any(
item.startswith(f"{current_urn}.") or item.startswith(f"{current_urn}:")
for item in requested_urns
)
or any(current_urn.startswith(f"{item}.") for item in requested_urns)
)


Expand Down Expand Up @@ -478,9 +487,7 @@ def _scim_response_serializer(
if returnability == Returned.default and (
(
included_urns
and not _contains_attribute_or_subattributes(
included_urns, attribute_urn
)
and not _is_attribute_requested(included_urns, attribute_urn)
)
or attribute_urn in excluded_urns
):
Expand Down
35 changes: 35 additions & 0 deletions tests/test_model_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from scim2_models.messages.patch_op import PatchOp
from scim2_models.reference import Reference
from scim2_models.resources.enterprise_user import EnterpriseUser
from scim2_models.resources.group import Group
from scim2_models.resources.group import GroupMember
from scim2_models.resources.resource import Extension
from scim2_models.resources.resource import Meta
from scim2_models.resources.resource import Resource
Expand Down Expand Up @@ -354,3 +356,36 @@ def test_patch_op_preserves_case_in_sub_value_fields():
value = result["Operations"][0]["value"]

assert value["name"]["givenName"] == "John"


def test_complex_attribute_inclusion_includes_sub_attributes():
"""When a complex attribute is requested, its sub-attributes should be included."""
user = User(
user_name="bjensen",
name={"given_name": "Barbara", "family_name": "Jensen"},
)
result = user.model_dump(
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
attributes=["name"],
)
assert result["name"] == {"givenName": "Barbara", "familyName": "Jensen"}


def test_multivalued_complex_attribute_inclusion_includes_sub_attributes():
"""When a multi-valued complex attribute is requested, its sub-attributes should be included."""
group = Group(
id="group-123",
display_name="Engineering",
members=[
GroupMember(value="user-1", type="User"),
GroupMember(value="user-2", type="User"),
],
)
result = group.model_dump(
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
attributes=["members"],
)
assert result["members"] == [
{"value": "user-1", "type": "User"},
{"value": "user-2", "type": "User"},
]
1 change: 1 addition & 0 deletions tests/test_model_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ def test_dump_response(context, ret_resource):
"alwaysReturned": "x",
"sub": {
"alwaysReturned": "x",
"defaultReturned": "x",
},
}

Expand Down
Loading