Skip to content

Commit 895a2a0

Browse files
committed
refactor: PatchOp utilities are classmethods
1 parent df5ace2 commit 895a2a0

File tree

1 file changed

+39
-26
lines changed

1 file changed

+39
-26
lines changed

scim2_models/messages/patch_op.py

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ def _apply_remove(self, resource: Resource[Any], operation: PatchOperation) -> b
308308

309309
return self._remove_value_at_path(resource, operation.path)
310310

311-
def _apply_root_attributes(self, resource: BaseModel, value: Any) -> bool:
311+
@classmethod
312+
def _apply_root_attributes(cls, resource: BaseModel, value: Any) -> bool:
312313
"""Apply attributes to the resource root."""
313314
if not isinstance(value, dict):
314315
return False
@@ -326,8 +327,9 @@ def _apply_root_attributes(self, resource: BaseModel, value: Any) -> bool:
326327

327328
return modified
328329

330+
@classmethod
329331
def _set_value_at_path(
330-
self, resource: Resource[Any], path: str, value: Any, is_add: bool
332+
cls, resource: Resource[Any], path: str, value: Any, is_add: bool
331333
) -> bool:
332334
"""Set a value at a specific path."""
333335
target, attr_path = _resolve_path_to_target(resource, path)
@@ -337,21 +339,22 @@ def _set_value_at_path(
337339

338340
path_parts = attr_path.split(".")
339341
if len(path_parts) == 1:
340-
return self._set_simple_attribute(target, path_parts[0], value, is_add)
342+
return cls._set_simple_attribute(target, path_parts[0], value, is_add)
341343

342-
return self._set_complex_attribute(target, path_parts, value, is_add)
344+
return cls._set_complex_attribute(target, path_parts, value, is_add)
343345

346+
@classmethod
344347
def _set_simple_attribute(
345-
self, resource: BaseModel, attr_name: str, value: Any, is_add: bool
348+
cls, resource: BaseModel, attr_name: str, value: Any, is_add: bool
346349
) -> bool:
347350
"""Set a value on a simple (non-nested) attribute."""
348351
field_name = _find_field_name(type(resource), attr_name)
349352
if not field_name:
350353
raise ValueError(Error.make_no_target_error().detail)
351354

352355
# RFC 7644 Section 3.5.2.1: "For multi-valued attributes, add operation appends values"
353-
if is_add and self._is_multivalued_field(resource, field_name):
354-
return self._handle_multivalued_add(resource, field_name, value)
356+
if is_add and cls._is_multivalued_field(resource, field_name):
357+
return cls._handle_multivalued_add(resource, field_name, value)
355358

356359
old_value = getattr(resource, field_name)
357360
if old_value == value:
@@ -360,8 +363,9 @@ def _set_simple_attribute(
360363
setattr(resource, field_name, value)
361364
return True
362365

366+
@classmethod
363367
def _set_complex_attribute(
364-
self, resource: BaseModel, path_parts: list[str], value: Any, is_add: bool
368+
cls, resource: BaseModel, path_parts: list[str], value: Any, is_add: bool
365369
) -> bool:
366370
"""Set a value on a complex (nested) attribute."""
367371
parent_attr = path_parts[0]
@@ -373,32 +377,35 @@ def _set_complex_attribute(
373377

374378
parent_obj = getattr(resource, parent_field_name)
375379
if parent_obj is None:
376-
parent_obj = self._create_parent_object(resource, parent_field_name)
380+
parent_obj = cls._create_parent_object(resource, parent_field_name)
377381
if parent_obj is None:
378382
return False
379383

380-
return self._set_value_at_path(parent_obj, sub_path, value, is_add)
384+
return cls._set_value_at_path(parent_obj, sub_path, value, is_add)
381385

382-
def _is_multivalued_field(self, resource: BaseModel, field_name: str) -> bool:
386+
@classmethod
387+
def _is_multivalued_field(cls, resource: BaseModel, field_name: str) -> bool:
383388
"""Check if a field is multi-valued."""
384389
return hasattr(resource, field_name) and type(resource).get_field_multiplicity(
385390
field_name
386391
)
387392

393+
@classmethod
388394
def _handle_multivalued_add(
389-
self, resource: BaseModel, field_name: str, value: Any
395+
cls, resource: BaseModel, field_name: str, value: Any
390396
) -> bool:
391397
"""Handle adding values to a multi-valued attribute."""
392398
current_list = getattr(resource, field_name) or []
393399

394400
# RFC 7644 Section 3.5.2.1: "Add operation appends values to multi-valued attributes"
395401
if isinstance(value, list):
396-
return self._add_multiple_values(resource, field_name, current_list, value)
402+
return cls._add_multiple_values(resource, field_name, current_list, value)
397403

398-
return self._add_single_value(resource, field_name, current_list, value)
404+
return cls._add_single_value(resource, field_name, current_list, value)
399405

406+
@classmethod
400407
def _add_multiple_values(
401-
self,
408+
cls,
402409
resource: BaseModel,
403410
field_name: str,
404411
current_list: list[Any],
@@ -408,7 +415,7 @@ def _add_multiple_values(
408415
new_values = []
409416
# RFC 7644 Section 3.5.2.1: "Do not add duplicate values"
410417
for new_val in values:
411-
if not self._value_exists_in_list(current_list, new_val):
418+
if not cls._value_exists_in_list(current_list, new_val):
412419
new_values.append(new_val)
413420

414421
if not new_values:
@@ -417,23 +424,26 @@ def _add_multiple_values(
417424
setattr(resource, field_name, current_list + new_values)
418425
return True
419426

427+
@classmethod
420428
def _add_single_value(
421-
self, resource: BaseModel, field_name: str, current_list: list[Any], value: Any
429+
cls, resource: BaseModel, field_name: str, current_list: list[Any], value: Any
422430
) -> bool:
423431
"""Add a single value to a multi-valued attribute."""
424432
# RFC 7644 Section 3.5.2.1: "Do not add duplicate values"
425-
if self._value_exists_in_list(current_list, value):
433+
if cls._value_exists_in_list(current_list, value):
426434
return False
427435

428436
current_list.append(value)
429437
setattr(resource, field_name, current_list)
430438
return True
431439

432-
def _value_exists_in_list(self, current_list: list[Any], new_value: Any) -> bool:
440+
@classmethod
441+
def _value_exists_in_list(cls, current_list: list[Any], new_value: Any) -> bool:
433442
"""Check if a value already exists in a list."""
434-
return any(self._values_match(item, new_value) for item in current_list)
443+
return any(cls._values_match(item, new_value) for item in current_list)
435444

436-
def _create_parent_object(self, resource: BaseModel, parent_field_name: str) -> Any:
445+
@classmethod
446+
def _create_parent_object(cls, resource: BaseModel, parent_field_name: str) -> Any:
437447
"""Create a parent object if it doesn't exist."""
438448
parent_class = type(resource).get_field_root_type(parent_field_name)
439449
if not parent_class or not isclass(parent_class):
@@ -443,7 +453,8 @@ def _create_parent_object(self, resource: BaseModel, parent_field_name: str) ->
443453
setattr(resource, parent_field_name, parent_obj)
444454
return parent_obj
445455

446-
def _remove_value_at_path(self, resource: Resource[Any], path: str) -> bool:
456+
@classmethod
457+
def _remove_value_at_path(cls, resource: Resource[Any], path: str) -> bool:
447458
"""Remove a value at a specific path."""
448459
target, attr_path = _resolve_path_to_target(resource, path)
449460

@@ -466,10 +477,11 @@ def _remove_value_at_path(self, resource: Resource[Any], path: str) -> bool:
466477
return True
467478

468479
sub_path = ".".join(path_parts)
469-
return self._remove_value_at_path(parent_obj, sub_path)
480+
return cls._remove_value_at_path(parent_obj, sub_path)
470481

482+
@classmethod
471483
def _remove_specific_value(
472-
self, resource: Resource[Any], path: str, value_to_remove: Any
484+
cls, resource: Resource[Any], path: str, value_to_remove: Any
473485
) -> bool:
474486
"""Remove a specific value from a multi-valued attribute."""
475487
target, attr_path = _resolve_path_to_target(resource, path)
@@ -490,7 +502,7 @@ def _remove_specific_value(
490502
modified = False
491503
# RFC 7644 Section 3.5.2.3: "Remove matching values from multi-valued attributes"
492504
for item in current_list:
493-
if not self._values_match(item, value_to_remove):
505+
if not cls._values_match(item, value_to_remove):
494506
new_list.append(item)
495507
else:
496508
modified = True
@@ -501,7 +513,8 @@ def _remove_specific_value(
501513

502514
return False
503515

504-
def _values_match(self, value1: Any, value2: Any) -> bool:
516+
@classmethod
517+
def _values_match(cls, value1: Any, value2: Any) -> bool:
505518
"""Check if two values match, converting BaseModel to dict for comparison."""
506519

507520
def to_dict(value: Any) -> dict[str, Any]:

0 commit comments

Comments
 (0)