scim2-models 0.3.4__tar.gz → 0.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. {scim2_models-0.3.4 → scim2_models-0.3.6}/PKG-INFO +1 -1
  2. {scim2_models-0.3.4 → scim2_models-0.3.6}/doc/changelog.rst +15 -0
  3. {scim2_models-0.3.4 → scim2_models-0.3.6}/pyproject.toml +1 -1
  4. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7643/resource.py +14 -10
  5. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7643/resource_type.py +13 -1
  6. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7644/list_response.py +2 -1
  7. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_dynamic_schemas.py +50 -0
  8. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_resource_type.py +34 -0
  9. {scim2_models-0.3.4 → scim2_models-0.3.6}/uv.lock +166 -165
  10. {scim2_models-0.3.4 → scim2_models-0.3.6}/.github/FUNDING.yml +0 -0
  11. {scim2_models-0.3.4 → scim2_models-0.3.6}/.github/workflows/release.yml +0 -0
  12. {scim2_models-0.3.4 → scim2_models-0.3.6}/.github/workflows/tests.yaml +0 -0
  13. {scim2_models-0.3.4 → scim2_models-0.3.6}/.gitignore +0 -0
  14. {scim2_models-0.3.4 → scim2_models-0.3.6}/.pre-commit-config.yaml +0 -0
  15. {scim2_models-0.3.4 → scim2_models-0.3.6}/.readthedocs.yml +0 -0
  16. {scim2_models-0.3.4 → scim2_models-0.3.6}/LICENSE +0 -0
  17. {scim2_models-0.3.4 → scim2_models-0.3.6}/README.md +0 -0
  18. {scim2_models-0.3.4 → scim2_models-0.3.6}/conftest.py +0 -0
  19. {scim2_models-0.3.4 → scim2_models-0.3.6}/doc/__init__.py +0 -0
  20. {scim2_models-0.3.4 → scim2_models-0.3.6}/doc/conf.py +0 -0
  21. {scim2_models-0.3.4 → scim2_models-0.3.6}/doc/contributing.rst +0 -0
  22. {scim2_models-0.3.4 → scim2_models-0.3.6}/doc/index.rst +0 -0
  23. {scim2_models-0.3.4 → scim2_models-0.3.6}/doc/reference.rst +0 -0
  24. {scim2_models-0.3.4 → scim2_models-0.3.6}/doc/tutorial.rst +0 -0
  25. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.1-user-minimal.json +0 -0
  26. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.2-user-full.json +0 -0
  27. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.3-enterprise_user.json +0 -0
  28. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.4-group.json +0 -0
  29. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.5-service_provider_configuration.json +0 -0
  30. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.6-resource_type-group.json +0 -0
  31. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.6-resource_type-user.json +0 -0
  32. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.7.1-schema-enterprise_user.json +0 -0
  33. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.7.1-schema-group.json +0 -0
  34. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.7.1-schema-user.json +0 -0
  35. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.7.2-schema-resource_type.json +0 -0
  36. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.7.2-schema-schema.json +0 -0
  37. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7643-8.7.2-schema-service_provider_configuration.json +0 -0
  38. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.12-error-bad_request.json +0 -0
  39. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.12-error-not_found.json +0 -0
  40. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.14-user-post_request.json +0 -0
  41. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.14-user-post_response.json +0 -0
  42. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.3-user-post_request.json +0 -0
  43. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.3-user-post_response.json +0 -0
  44. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.4.1-user-known-resource.json +0 -0
  45. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.4.2-list_response-partial_attributes.json +0 -0
  46. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.4.3-list_response-post_query.json +0 -0
  47. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.4.3-search_request.json +0 -0
  48. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.1-user-put_request.json +0 -0
  49. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.1-user-put_response.json +0 -0
  50. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.1-patch_op-add_emails.json +0 -0
  51. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.1-patch_op-add_members.json +0 -0
  52. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.2-patch_op-remove_all_members.json +0 -0
  53. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.2-patch_op-remove_and_add_one_member.json +0 -0
  54. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.2-patch_op-remove_multi_complex_value.json +0 -0
  55. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.2-patch_op-remove_one_member.json +0 -0
  56. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.3-patch_op-replace_all_email_values.json +0 -0
  57. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.3-patch_op-replace_all_members.json +0 -0
  58. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.3-patch_op-replace_street_address.json +0 -0
  59. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.5.2.3-patch_op-replace_user_work_address.json +0 -0
  60. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.6-error-not_found.json +0 -0
  61. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.1-bulk_request-circular_conflict.json +0 -0
  62. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.1-list_response-circular_reference.json +0 -0
  63. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.2-bulk_request-enterprise_user.json +0 -0
  64. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.2-bulk_request-temporary_identifier.json +0 -0
  65. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.2-bulk_response-temporary_identifier.json +0 -0
  66. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.3-bulk_request-multiple_operations.json +0 -0
  67. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.3-bulk_response-error_invalid_syntax.json +0 -0
  68. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.3-bulk_response-multiple_errors.json +0 -0
  69. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.3-bulk_response-multiple_operations.json +0 -0
  70. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.3-error-invalid_syntax.json +0 -0
  71. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.7.4-error-payload_too_large.json +0 -0
  72. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-3.9-user-partial_response.json +0 -0
  73. {scim2_models-0.3.4 → scim2_models-0.3.6}/samples/rfc7644-4-list_response-resource_types.json +0 -0
  74. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/__init__.py +0 -0
  75. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/base.py +0 -0
  76. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/constants.py +0 -0
  77. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/py.typed +0 -0
  78. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7643/__init__.py +0 -0
  79. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7643/enterprise_user.py +0 -0
  80. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7643/group.py +0 -0
  81. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7643/schema.py +0 -0
  82. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7643/service_provider_config.py +0 -0
  83. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7643/user.py +0 -0
  84. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7644/__init__.py +0 -0
  85. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7644/bulk.py +0 -0
  86. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7644/error.py +0 -0
  87. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7644/message.py +0 -0
  88. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7644/patch_op.py +0 -0
  89. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/rfc7644/search_request.py +0 -0
  90. {scim2_models-0.3.4 → scim2_models-0.3.6}/scim2_models/utils.py +0 -0
  91. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/__init__.py +0 -0
  92. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/conftest.py +0 -0
  93. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_dynamic_resources.py +0 -0
  94. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_enterprise_user.py +0 -0
  95. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_errors.py +0 -0
  96. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_group.py +0 -0
  97. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_list_response.py +0 -0
  98. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_model_attributes.py +0 -0
  99. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_model_serialization.py +0 -0
  100. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_model_validation.py +0 -0
  101. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_models.py +0 -0
  102. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_patch_op.py +0 -0
  103. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_resource_extension.py +0 -0
  104. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_schema.py +0 -0
  105. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_search_request.py +0 -0
  106. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_service_provider_configuration.py +0 -0
  107. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_user.py +0 -0
  108. {scim2_models-0.3.4 → scim2_models-0.3.6}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scim2-models
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: SCIM2 models serialization and validation with pydantic
5
5
  Project-URL: documentation, https://scim2-models.readthedocs.io
6
6
  Project-URL: repository, https://github.com/python-scim/scim2-models
@@ -1,6 +1,21 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ [0.3.6] - 2025-07-02
5
+ --------------------
6
+
7
+ Added
8
+ ^^^^^
9
+ - Fix :meth:`ResourceType.from_resource <scim2_models.ResourceType.from_resource>`
10
+ usage for resources with several extensions. :pr:`95`
11
+
12
+ [0.3.5] - 2025-06-05
13
+ --------------------
14
+
15
+ Added
16
+ ^^^^^
17
+ - Fix dynamic schema generation for user defined classes with inheritance.
18
+
4
19
  [0.3.4] - 2025-06-05
5
20
  --------------------
6
21
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "scim2-models"
7
- version = "0.3.4"
7
+ version = "0.3.6"
8
8
  description = "SCIM2 models serialization and validation with pydantic"
9
9
  authors = [{name="Yaal Coop", email="contact@yaal.coop"}]
10
10
  license = {file = "LICENSE"}
@@ -4,7 +4,6 @@ from typing import Any
4
4
  from typing import Generic
5
5
  from typing import Optional
6
6
  from typing import TypeVar
7
- from typing import Union
8
7
  from typing import get_args
9
8
  from typing import get_origin
10
9
 
@@ -17,12 +16,14 @@ from ..base import BaseModelType
17
16
  from ..base import CaseExact
18
17
  from ..base import ComplexAttribute
19
18
  from ..base import ExternalReference
19
+ from ..base import MultiValuedComplexAttribute
20
20
  from ..base import Mutability
21
21
  from ..base import Required
22
22
  from ..base import Returned
23
23
  from ..base import Uniqueness
24
24
  from ..base import URIReference
25
25
  from ..base import is_complex_attribute
26
+ from ..utils import UNION_TYPES
26
27
  from ..utils import normalize_attribute_name
27
28
 
28
29
 
@@ -116,7 +117,7 @@ class ResourceMetaclass(BaseModelType):
116
117
  extensions = kwargs["__pydantic_generic_metadata__"]["args"][0]
117
118
  extensions = (
118
119
  get_args(extensions)
119
- if get_origin(extensions) == Union
120
+ if get_origin(extensions) in UNION_TYPES
120
121
  else [extensions]
121
122
  )
122
123
  for extension in extensions:
@@ -182,7 +183,8 @@ class Resource(BaseModel, Generic[AnyExtension], metaclass=ResourceMetaclass):
182
183
  extension_models = cls.__pydantic_generic_metadata__.get("args", [])
183
184
  extension_models = (
184
185
  get_args(extension_models[0])
185
- if len(extension_models) == 1 and get_origin(extension_models[0]) == Union
186
+ if len(extension_models) == 1
187
+ and get_origin(extension_models[0]) in UNION_TYPES
186
188
  else extension_models
187
189
  )
188
190
 
@@ -253,8 +255,8 @@ class Resource(BaseModel, Generic[AnyExtension], metaclass=ResourceMetaclass):
253
255
  AnyResource = TypeVar("AnyResource", bound="Resource")
254
256
 
255
257
 
256
- def dedicated_attributes(model):
257
- """Return attributes that are not members of parent classes."""
258
+ def dedicated_attributes(model, excluded_models):
259
+ """Return attributes that are not members the parent 'excluded_models'."""
258
260
 
259
261
  def compare_field_infos(fi1, fi2):
260
262
  return (
@@ -268,8 +270,8 @@ def dedicated_attributes(model):
268
270
 
269
271
  parent_field_infos = {
270
272
  field_name: field_info
271
- for parent in model.__bases__
272
- for field_name, field_info in parent.model_fields.items()
273
+ for excluded_model in excluded_models
274
+ for field_name, field_info in excluded_model.model_fields.items()
273
275
  }
274
276
  field_infos = {
275
277
  field_name: field_info
@@ -283,7 +285,7 @@ def model_to_schema(model: type[BaseModel]):
283
285
  from scim2_models.rfc7643.schema import Schema
284
286
 
285
287
  schema_urn = model.model_fields["schemas"].default[0]
286
- field_infos = dedicated_attributes(model)
288
+ field_infos = dedicated_attributes(model, [Resource])
287
289
  attributes = [
288
290
  model_attribute_to_attribute(model, attribute_name)
289
291
  for attribute_name in field_infos
@@ -300,7 +302,7 @@ def model_to_schema(model: type[BaseModel]):
300
302
 
301
303
  def get_reference_types(type) -> list[str]:
302
304
  first_arg = get_args(type)[0]
303
- types = get_args(first_arg) if get_origin(first_arg) == Union else [first_arg]
305
+ types = get_args(first_arg) if get_origin(first_arg) in UNION_TYPES else [first_arg]
304
306
 
305
307
  def serialize_ref_type(ref_type):
306
308
  if ref_type == URIReference:
@@ -323,7 +325,9 @@ def model_attribute_to_attribute(model, attribute_name):
323
325
  sub_attributes = (
324
326
  [
325
327
  model_attribute_to_attribute(root_type, sub_attribute_name)
326
- for sub_attribute_name in dedicated_attributes(root_type)
328
+ for sub_attribute_name in dedicated_attributes(
329
+ root_type, [MultiValuedComplexAttribute]
330
+ )
327
331
  if (
328
332
  attribute_name != "sub_attributes"
329
333
  or sub_attribute_name != "sub_attributes"
@@ -1,5 +1,7 @@
1
1
  from typing import Annotated
2
2
  from typing import Optional
3
+ from typing import get_args
4
+ from typing import get_origin
3
5
 
4
6
  from pydantic import Field
5
7
  from typing_extensions import Self
@@ -11,6 +13,7 @@ from ..base import Reference
11
13
  from ..base import Required
12
14
  from ..base import Returned
13
15
  from ..base import URIReference
16
+ from ..utils import UNION_TYPES
14
17
  from .resource import Resource
15
18
 
16
19
 
@@ -82,7 +85,16 @@ class ResourceType(Resource):
82
85
  """Build a naive ResourceType from a resource model."""
83
86
  schema = resource_model.model_fields["schemas"].default[0]
84
87
  name = schema.split(":")[-1]
85
- extensions = resource_model.__pydantic_generic_metadata__["args"]
88
+ if resource_model.__pydantic_generic_metadata__["args"]:
89
+ extensions = resource_model.__pydantic_generic_metadata__["args"][0]
90
+ extensions = (
91
+ get_args(extensions)
92
+ if get_origin(extensions) in UNION_TYPES
93
+ else [extensions]
94
+ )
95
+ else:
96
+ extensions = []
97
+
86
98
  return ResourceType(
87
99
  id=name,
88
100
  name=name,
@@ -20,6 +20,7 @@ from ..base import BaseModelType
20
20
  from ..base import Context
21
21
  from ..base import Required
22
22
  from ..rfc7643.resource import AnyResource
23
+ from ..utils import UNION_TYPES
23
24
  from .message import Message
24
25
 
25
26
 
@@ -29,7 +30,7 @@ class ListResponseMetaclass(BaseModelType):
29
30
 
30
31
  https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions
31
32
  """
32
- if not get_origin(resource_union) == Union:
33
+ if get_origin(resource_union) not in UNION_TYPES:
33
34
  return resource_union
34
35
 
35
36
  resource_types = get_args(resource_union)
@@ -1,8 +1,12 @@
1
1
  import operator
2
+ from typing import Annotated
3
+ from typing import Optional
2
4
 
3
5
  from scim2_models.base import Context
6
+ from scim2_models.base import Required
4
7
  from scim2_models.rfc7643.enterprise_user import EnterpriseUser
5
8
  from scim2_models.rfc7643.group import Group
9
+ from scim2_models.rfc7643.resource import Resource
6
10
  from scim2_models.rfc7643.resource_type import ResourceType
7
11
  from scim2_models.rfc7643.schema import Schema
8
12
  from scim2_models.rfc7643.service_provider_config import ServiceProviderConfig
@@ -127,3 +131,49 @@ def test_dump_with_context():
127
131
  models = [User, EnterpriseUser, Group, ResourceType, Schema, ServiceProviderConfig]
128
132
  for model in models:
129
133
  model.to_schema().model_dump(scim_ctx=Context.RESOURCE_QUERY_RESPONSE)
134
+
135
+
136
+ def test_inheritance():
137
+ """Check that parent attributes are included in the schema."""
138
+
139
+ class Foo(Resource):
140
+ schemas: Annotated[list[str], Required.true] = [
141
+ "urn:ietf:params:scim:schemas:core:2.0:Foo"
142
+ ]
143
+
144
+ foo: Optional[str] = None
145
+
146
+ class Bar(Foo):
147
+ bar: Optional[str] = None
148
+
149
+ schema = Bar.to_schema()
150
+ assert schema.model_dump() == {
151
+ "attributes": [
152
+ {
153
+ "caseExact": False,
154
+ "multiValued": False,
155
+ "mutability": "readWrite",
156
+ "name": "foo",
157
+ "required": False,
158
+ "returned": "default",
159
+ "type": "string",
160
+ "uniqueness": "none",
161
+ },
162
+ {
163
+ "caseExact": False,
164
+ "multiValued": False,
165
+ "mutability": "readWrite",
166
+ "name": "bar",
167
+ "required": False,
168
+ "returned": "default",
169
+ "type": "string",
170
+ "uniqueness": "none",
171
+ },
172
+ ],
173
+ "description": "Bar",
174
+ "id": "urn:ietf:params:scim:schemas:core:2.0:Foo",
175
+ "name": "Bar",
176
+ "schemas": [
177
+ "urn:ietf:params:scim:schemas:core:2.0:Schema",
178
+ ],
179
+ }
@@ -1,5 +1,10 @@
1
+ from typing import Annotated
2
+ from typing import Union
3
+
1
4
  from scim2_models import EnterpriseUser
5
+ from scim2_models import Extension
2
6
  from scim2_models import Reference
7
+ from scim2_models import Required
3
8
  from scim2_models import ResourceType
4
9
  from scim2_models import User
5
10
 
@@ -61,3 +66,32 @@ def test_from_resource_with_extensions():
61
66
  == "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
62
67
  )
63
68
  assert not enterprise_user_rt.schema_extensions[0].required
69
+
70
+
71
+ def test_from_resource_with_mulitple_extensions():
72
+ class TestExtension(Extension):
73
+ schemas: Annotated[list[str], Required.true] = [
74
+ "urn:ietf:params:scim:schemas:extension:Test:1.0:User"
75
+ ]
76
+
77
+ test: Union[str, None] = None
78
+ test2: Union[list[str], None] = None
79
+
80
+ enterprise_user_rt = ResourceType.from_resource(
81
+ User[Union[EnterpriseUser, TestExtension]]
82
+ )
83
+ assert enterprise_user_rt.id == "User"
84
+ assert enterprise_user_rt.name == "User"
85
+ assert enterprise_user_rt.description == "User"
86
+ assert enterprise_user_rt.endpoint == "/Users"
87
+ assert enterprise_user_rt.schema_ == "urn:ietf:params:scim:schemas:core:2.0:User"
88
+ assert (
89
+ enterprise_user_rt.schema_extensions[0].schema_
90
+ == "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
91
+ )
92
+ assert not enterprise_user_rt.schema_extensions[0].required
93
+ assert (
94
+ enterprise_user_rt.schema_extensions[1].schema_
95
+ == "urn:ietf:params:scim:schemas:extension:Test:1.0:User"
96
+ )
97
+ assert not enterprise_user_rt.schema_extensions[1].required