Skip to content

Commit 62d39ce

Browse files
authored
Merge pull request #129 from python-scim/errata
Fix RFC6743 erratas
2 parents 0029df7 + 45424d9 commit 62d39ce

9 files changed

Lines changed: 823 additions & 812 deletions

doc/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ Fixed
88
^^^^^
99
- Fix ``model_json_schema()`` generation for models containing :class:`~scim2_models.Reference` or :class:`~scim2_models.Path` fields. :issue:`125`
1010
- Group ``displayName`` is required. :rfc:`7643` `erratum 5368 <https://www.rfc-editor.org/errata/eid5368>`_ :issue:`123` :pr:`128`
11+
- :class:`~scim2_models.GroupMembership` ``$ref`` only references ``Group``. :rfc:`7643` `erratum 8471 <https://www.rfc-editor.org/errata/eid8471>`_
12+
- :class:`~scim2_models.Manager` ``value`` is case-exact. :rfc:`7643` `erratum 8472 <https://www.rfc-editor.org/errata/eid8472>`_
13+
- :class:`~scim2_models.ResourceType` ``name`` and ``endpoint`` have server uniqueness. :rfc:`7643` `erratum 8475 <https://www.rfc-editor.org/errata/eid8475>`_
14+
- Complex attributes don't have ``uniqueness`` in schema representation. :rfc:`7643` `erratum 6004 <https://www.rfc-editor.org/errata/eid6004>`_
1115

1216
[0.6.2] - 2026-01-25
1317
--------------------

samples/rfc7643-8.7.1-schema-enterprise_user.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
"multiValued": false,
7373
"description": "The id of the SCIM resource representing the User's manager. REQUIRED.",
7474
"required": true,
75-
"caseExact": false,
75+
"caseExact": true,
7676
"mutability": "readWrite",
7777
"returned": "default",
7878
"uniqueness": "none"

samples/rfc7643-8.7.1-schema-user.json

Lines changed: 775 additions & 777 deletions
Large diffs are not rendered by default.

samples/rfc7643-8.7.2-schema-resource_type.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
"multiValued": false,
2222
"description": "The resource type name. When applicable, service providers MUST specify the name, e.g., 'User'.",
2323
"required": true,
24-
"caseExact": false,
24+
"caseExact": true,
2525
"mutability": "readOnly",
2626
"returned": "default",
27-
"uniqueness": "none"
27+
"uniqueness": "server"
2828
},
2929
{
3030
"name": "description",
@@ -49,7 +49,7 @@
4949
"caseExact": false,
5050
"mutability": "readOnly",
5151
"returned": "default",
52-
"uniqueness": "none"
52+
"uniqueness": "server"
5353
},
5454
{
5555
"name": "schema",

scim2_models/resources/enterprise_user.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from pydantic import Field
55

6+
from ..annotations import CaseExact
67
from ..annotations import Mutability
78
from ..annotations import Required
89
from ..attributes import ComplexAttribute
@@ -15,7 +16,7 @@
1516

1617

1718
class Manager(ComplexAttribute):
18-
value: Annotated[str | None, Required.true] = None
19+
value: Annotated[str | None, Required.true, CaseExact.true] = None
1920
"""The id of the SCIM resource representing the User's manager."""
2021

2122
ref: Annotated[ # type: ignore[type-arg]

scim2_models/resources/resource.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -532,19 +532,21 @@ def _model_attribute_to_scim_attribute(
532532
else None
533533
)
534534

535-
return Attribute(
536-
name=field_info.serialization_alias or attribute_name,
537-
type=Attribute.Type(attribute_type),
538-
multi_valued=model.get_field_multiplicity(attribute_name),
539-
description=field_info.description,
540-
canonical_values=field_info.examples,
541-
required=model.get_field_annotation(attribute_name, Required),
542-
case_exact=model.get_field_annotation(attribute_name, CaseExact),
543-
mutability=model.get_field_annotation(attribute_name, Mutability),
544-
returned=model.get_field_annotation(attribute_name, Returned),
545-
uniqueness=model.get_field_annotation(attribute_name, Uniqueness),
546-
sub_attributes=sub_attributes,
547-
reference_types=root_type.get_scim_reference_types() # type: ignore[attr-defined]
548-
if attribute_type == Attribute.Type.reference
549-
else None,
550-
)
535+
kwargs: dict[str, Any] = {
536+
"name": field_info.serialization_alias or attribute_name,
537+
"type": Attribute.Type(attribute_type),
538+
"multi_valued": model.get_field_multiplicity(attribute_name),
539+
"description": field_info.description,
540+
"canonical_values": field_info.examples,
541+
"required": model.get_field_annotation(attribute_name, Required),
542+
"case_exact": model.get_field_annotation(attribute_name, CaseExact),
543+
"mutability": model.get_field_annotation(attribute_name, Mutability),
544+
"returned": model.get_field_annotation(attribute_name, Returned),
545+
"sub_attributes": sub_attributes,
546+
}
547+
if attribute_type != Attribute.Type.complex:
548+
kwargs["uniqueness"] = model.get_field_annotation(attribute_name, Uniqueness)
549+
if attribute_type == Attribute.Type.reference:
550+
kwargs["reference_types"] = root_type.get_scim_reference_types() # type: ignore[attr-defined]
551+
552+
return Attribute(**kwargs)

scim2_models/resources/resource_type.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from ..annotations import Mutability
99
from ..annotations import Required
1010
from ..annotations import Returned
11+
from ..annotations import Uniqueness
1112
from ..attributes import ComplexAttribute
1213
from ..path import URN
1314
from ..reference import URI
@@ -38,7 +39,13 @@ class SchemaExtension(ComplexAttribute):
3839
class ResourceType(Resource[Any]):
3940
__schema__ = URN("urn:ietf:params:scim:schemas:core:2.0:ResourceType")
4041

41-
name: Annotated[str | None, Mutability.read_only, Required.true] = None
42+
name: Annotated[
43+
str | None,
44+
Mutability.read_only,
45+
Required.true,
46+
CaseExact.true,
47+
Uniqueness.server,
48+
] = None
4249
"""The resource type name.
4350
4451
When applicable, service providers MUST specify the name, e.g.,
@@ -57,9 +64,9 @@ class ResourceType(Resource[Any]):
5764
This is often the same value as the "name" attribute.
5865
"""
5966

60-
endpoint: Annotated[Reference[URI] | None, Mutability.read_only, Required.true] = (
61-
None
62-
)
67+
endpoint: Annotated[
68+
Reference[URI] | None, Mutability.read_only, Required.true, Uniqueness.server
69+
] = None
6370
"""The resource type's HTTP-addressable endpoint relative to the Base URL,
6471
e.g., '/Users'."""
6572

scim2_models/resources/user.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from typing import TYPE_CHECKING
33
from typing import Annotated
44
from typing import ClassVar
5-
from typing import Union
65

76
from pydantic import Base64Bytes
87
from pydantic import EmailStr
@@ -202,8 +201,8 @@ class GroupMembership(ComplexAttribute):
202201
value: Annotated[str | None, Mutability.read_only] = None
203202
"""The identifier of the User's group."""
204203

205-
ref: Annotated[ # type: ignore[type-arg]
206-
Reference[Union["User", "Group"]] | None,
204+
ref: Annotated[
205+
Reference["Group"] | None,
207206
Mutability.read_only,
208207
] = Field(None, serialization_alias="$ref")
209208
"""The reference URI of a target resource, if the attribute is a

tests/test_dynamic_resources.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -857,9 +857,7 @@ def test_make_user_model_from_schema(load_sample):
857857
assert Groups.get_field_annotation("value", Uniqueness) == Uniqueness.none
858858

859859
# group.ref
860-
assert (
861-
Groups.get_field_root_type("ref") == Reference[Union["User", "Group"]] # noqa: F821
862-
)
860+
assert Groups.get_field_root_type("ref") == Reference["Group"] # noqa: F821
863861
assert not Groups.get_field_multiplicity("ref")
864862
assert (
865863
Groups.model_fields["ref"].description
@@ -1388,7 +1386,7 @@ def test_make_enterprise_user_model_from_schema(load_sample):
13881386
== "The id of the SCIM resource representing the User's manager. REQUIRED."
13891387
)
13901388
assert Manager.get_field_annotation("value", Required) == Required.true
1391-
assert Manager.get_field_annotation("value", CaseExact) == CaseExact.false
1389+
assert Manager.get_field_annotation("value", CaseExact) == CaseExact.true
13921390
assert Manager.get_field_annotation("value", Mutability) == Mutability.read_write
13931391
assert Manager.get_field_annotation("value", Returned) == Returned.default
13941392
assert Manager.get_field_annotation("value", Uniqueness) == Uniqueness.none
@@ -1452,10 +1450,10 @@ def test_make_resource_type_model_from_schema(load_sample):
14521450
== "The resource type name. When applicable, service providers MUST specify the name, e.g., 'User'."
14531451
)
14541452
assert ResourceType.get_field_annotation("name", Required) == Required.true
1455-
assert ResourceType.get_field_annotation("name", CaseExact) == CaseExact.false
1453+
assert ResourceType.get_field_annotation("name", CaseExact) == CaseExact.true
14561454
assert ResourceType.get_field_annotation("name", Mutability) == Mutability.read_only
14571455
assert ResourceType.get_field_annotation("name", Returned) == Returned.default
1458-
assert ResourceType.get_field_annotation("name", Uniqueness) == Uniqueness.none
1456+
assert ResourceType.get_field_annotation("name", Uniqueness) == Uniqueness.server
14591457

14601458
# description
14611459
assert ResourceType.get_field_root_type("description") is str
@@ -1493,7 +1491,9 @@ def test_make_resource_type_model_from_schema(load_sample):
14931491
== Mutability.read_only
14941492
)
14951493
assert ResourceType.get_field_annotation("endpoint", Returned) == Returned.default
1496-
assert ResourceType.get_field_annotation("endpoint", Uniqueness) == Uniqueness.none
1494+
assert (
1495+
ResourceType.get_field_annotation("endpoint", Uniqueness) == Uniqueness.server
1496+
)
14971497

14981498
# schema
14991499
assert ResourceType.get_field_root_type("schema_") == Reference[URI]

0 commit comments

Comments
 (0)