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
22 changes: 22 additions & 0 deletions scim2_models/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import warnings
from inspect import isclass
from typing import Any
from typing import ClassVar
from typing import Optional
from typing import get_args
from typing import get_origin
Expand Down Expand Up @@ -112,6 +113,14 @@ class BaseModel(PydanticBaseModel):
extra="forbid",
)

_allow_bulk_id: ClassVar[bool] = False
"""Allow bulkId field for the model"""

@classmethod
def __pydantic_init_subclass__(cls, **kwargs: Any) -> None:
# Validate field names
cls._check_bulk_id()

@classmethod
def get_field_annotation(cls, field_name: str, annotation_type: type) -> Any:
"""Return the annotation of type 'annotation_type' of the field 'field_name'.
Expand Down Expand Up @@ -538,6 +547,19 @@ def _set_complex_attribute_urns(self) -> None:
else:
attr_value._attribute_urn = schema

@classmethod
def _check_bulk_id(cls) -> None:
"""Enforce bulkId as reserved field per RFC 7643 §3.1.

Check if a bulkdId field is defined and
raise error if `_allow_bulk_id` is set to `False`
"""
if cls._allow_bulk_id:
return
for info in cls.model_fields.values():
if info.serialization_alias == "bulkId":
raise TypeError(f"{cls.__name__}: bulkId is reserved for BulkOperation")

@field_serializer("*", mode="wrap")
def scim_serializer(
self,
Expand Down
2 changes: 2 additions & 0 deletions scim2_models/messages/bulk.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@


class BulkOperation(ComplexAttribute):
_allow_bulk_id = True

class Method(str, Enum):
post = "POST"
put = "PUT"
Expand Down
13 changes: 13 additions & 0 deletions tests/test_model_attributes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import uuid
from typing import Annotated

import pytest

from scim2_models import URN
from scim2_models.annotations import Returned
from scim2_models.attributes import ComplexAttribute
Expand Down Expand Up @@ -377,3 +379,14 @@ def test_short_attr_path_with_plain_name():

assert _short_attr_path("userName") == "userName"
assert _short_attr_path("name.familyName") == "name.familyName"


def test_forbid_bulk_id():
"""Forbid bulkId from class definition."""
with pytest.raises(TypeError) as exc_info:

class CustomModel(Resource):
__schema__ = URN("urn:example:schemas:CustomModel")
bulk_id: str | None = None

assert str(exc_info.value) == "CustomModel: bulkId is reserved for BulkOperation"
Loading