scim2-models 0.3.6__py3-none-any.whl → 0.4.0__py3-none-any.whl

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 (34) hide show
  1. scim2_models/__init__.py +52 -52
  2. scim2_models/annotations.py +104 -0
  3. scim2_models/attributes.py +57 -0
  4. scim2_models/base.py +195 -554
  5. scim2_models/constants.py +3 -3
  6. scim2_models/context.py +168 -0
  7. scim2_models/{rfc7644 → messages}/bulk.py +4 -4
  8. scim2_models/{rfc7644 → messages}/error.py +13 -13
  9. scim2_models/messages/list_response.py +75 -0
  10. scim2_models/messages/message.py +119 -0
  11. scim2_models/messages/patch_op.py +478 -0
  12. scim2_models/{rfc7644 → messages}/search_request.py +55 -6
  13. scim2_models/reference.py +82 -0
  14. scim2_models/{rfc7643 → resources}/enterprise_user.py +4 -4
  15. scim2_models/{rfc7643 → resources}/group.py +5 -5
  16. scim2_models/resources/resource.py +468 -0
  17. scim2_models/{rfc7643 → resources}/resource_type.py +13 -22
  18. scim2_models/{rfc7643 → resources}/schema.py +51 -45
  19. scim2_models/{rfc7643 → resources}/service_provider_config.py +7 -7
  20. scim2_models/{rfc7643 → resources}/user.py +9 -9
  21. scim2_models/scim_object.py +66 -0
  22. scim2_models/urn.py +109 -0
  23. scim2_models/utils.py +108 -6
  24. {scim2_models-0.3.6.dist-info → scim2_models-0.4.0.dist-info}/METADATA +1 -1
  25. scim2_models-0.4.0.dist-info/RECORD +30 -0
  26. scim2_models/rfc7643/resource.py +0 -355
  27. scim2_models/rfc7644/list_response.py +0 -136
  28. scim2_models/rfc7644/message.py +0 -10
  29. scim2_models/rfc7644/patch_op.py +0 -70
  30. scim2_models-0.3.6.dist-info/RECORD +0 -24
  31. /scim2_models/{rfc7643 → messages}/__init__.py +0 -0
  32. /scim2_models/{rfc7644 → resources}/__init__.py +0 -0
  33. {scim2_models-0.3.6.dist-info → scim2_models-0.4.0.dist-info}/WHEEL +0 -0
  34. {scim2_models-0.3.6.dist-info → scim2_models-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,355 +0,0 @@
1
- from datetime import datetime
2
- from typing import Annotated
3
- from typing import Any
4
- from typing import Generic
5
- from typing import Optional
6
- from typing import TypeVar
7
- from typing import get_args
8
- from typing import get_origin
9
-
10
- from pydantic import Field
11
- from pydantic import WrapSerializer
12
- from pydantic import field_serializer
13
-
14
- from ..base import BaseModel
15
- from ..base import BaseModelType
16
- from ..base import CaseExact
17
- from ..base import ComplexAttribute
18
- from ..base import ExternalReference
19
- from ..base import MultiValuedComplexAttribute
20
- from ..base import Mutability
21
- from ..base import Required
22
- from ..base import Returned
23
- from ..base import Uniqueness
24
- from ..base import URIReference
25
- from ..base import is_complex_attribute
26
- from ..utils import UNION_TYPES
27
- from ..utils import normalize_attribute_name
28
-
29
-
30
- class Meta(ComplexAttribute):
31
- """All "meta" sub-attributes are assigned by the service provider (have a "mutability" of "readOnly"), and all of these sub-attributes have a "returned" characteristic of "default".
32
-
33
- This attribute SHALL be ignored when provided by clients. "meta" contains the following sub-attributes:
34
- """
35
-
36
- resource_type: Optional[str] = None
37
- """The name of the resource type of the resource.
38
-
39
- This attribute has a mutability of "readOnly" and "caseExact" as
40
- "true".
41
- """
42
-
43
- created: Optional[datetime] = None
44
- """The "DateTime" that the resource was added to the service provider.
45
-
46
- This attribute MUST be a DateTime.
47
- """
48
-
49
- last_modified: Optional[datetime] = None
50
- """The most recent DateTime that the details of this resource were updated
51
- at the service provider.
52
-
53
- If this resource has never been modified since its initial creation,
54
- the value MUST be the same as the value of "created".
55
- """
56
-
57
- location: Optional[str] = None
58
- """The URI of the resource being returned.
59
-
60
- This value MUST be the same as the "Content-Location" HTTP response
61
- header (see Section 3.1.4.2 of [RFC7231]).
62
- """
63
-
64
- version: Optional[str] = None
65
- """The version of the resource being returned.
66
-
67
- This value must be the same as the entity-tag (ETag) HTTP response
68
- header (see Sections 2.1 and 2.3 of [RFC7232]). This attribute has
69
- "caseExact" as "true". Service provider support for this attribute
70
- is optional and subject to the service provider's support for
71
- versioning (see Section 3.14 of [RFC7644]). If a service provider
72
- provides "version" (entity-tag) for a representation and the
73
- generation of that entity-tag does not satisfy all of the
74
- characteristics of a strong validator (see Section 2.1 of
75
- [RFC7232]), then the origin server MUST mark the "version" (entity-
76
- tag) as weak by prefixing its opaque value with "W/" (case
77
- sensitive).
78
- """
79
-
80
-
81
- class Extension(BaseModel):
82
- @classmethod
83
- def to_schema(cls):
84
- """Build a :class:`~scim2_models.Schema` from the current extension class."""
85
- return model_to_schema(cls)
86
-
87
- @classmethod
88
- def from_schema(cls, schema) -> "Extension":
89
- """Build a :class:`~scim2_models.Extension` subclass from the schema definition."""
90
- from .schema import make_python_model
91
-
92
- return make_python_model(schema, cls)
93
-
94
-
95
- AnyExtension = TypeVar("AnyExtension", bound="Extension")
96
-
97
-
98
- def extension_serializer(value: Any, handler, info) -> Optional[dict[str, Any]]:
99
- """Exclude the Resource attributes from the extension dump.
100
-
101
- For instance, attributes 'meta', 'id' or 'schemas' should not be
102
- dumped when the model is used as an extension for another model.
103
- """
104
- partial_result = handler(value, info)
105
- result = {
106
- attr_name: value
107
- for attr_name, value in partial_result.items()
108
- if attr_name not in Resource.model_fields
109
- }
110
- return result or None
111
-
112
-
113
- class ResourceMetaclass(BaseModelType):
114
- def __new__(cls, name, bases, attrs, **kwargs):
115
- """Dynamically add a field for each extension."""
116
- if "__pydantic_generic_metadata__" in kwargs:
117
- extensions = kwargs["__pydantic_generic_metadata__"]["args"][0]
118
- extensions = (
119
- get_args(extensions)
120
- if get_origin(extensions) in UNION_TYPES
121
- else [extensions]
122
- )
123
- for extension in extensions:
124
- schema = extension.model_fields["schemas"].default[0]
125
- attrs.setdefault("__annotations__", {})[extension.__name__] = Annotated[
126
- Optional[extension],
127
- WrapSerializer(extension_serializer),
128
- ]
129
- attrs[extension.__name__] = Field(
130
- None,
131
- serialization_alias=schema,
132
- validation_alias=normalize_attribute_name(schema),
133
- )
134
-
135
- klass = super().__new__(cls, name, bases, attrs, **kwargs)
136
- return klass
137
-
138
-
139
- class Resource(BaseModel, Generic[AnyExtension], metaclass=ResourceMetaclass):
140
- schemas: Annotated[list[str], Required.true]
141
- """The "schemas" attribute is a REQUIRED attribute and is an array of
142
- Strings containing URIs that are used to indicate the namespaces of the
143
- SCIM schemas that define the attributes present in the current JSON
144
- structure."""
145
-
146
- # Common attributes as defined by
147
- # https://www.rfc-editor.org/rfc/rfc7643#section-3.1
148
-
149
- id: Annotated[
150
- Optional[str], Mutability.read_only, Returned.always, Uniqueness.global_
151
- ] = None
152
- """A unique identifier for a SCIM resource as defined by the service
153
- provider.
154
-
155
- id is mandatory is the resource representation, but is forbidden in
156
- resource creation or replacement requests.
157
- """
158
-
159
- external_id: Annotated[
160
- Optional[str], Mutability.read_write, Returned.default, CaseExact.true
161
- ] = None
162
- """A String that is an identifier for the resource as defined by the
163
- provisioning client."""
164
-
165
- meta: Annotated[Optional[Meta], Mutability.read_only, Returned.default] = None
166
- """A complex attribute containing resource metadata."""
167
-
168
- def __getitem__(self, item: Any):
169
- if not isinstance(item, type) or not issubclass(item, Extension):
170
- raise KeyError(f"{item} is not a valid extension type")
171
-
172
- return getattr(self, item.__name__)
173
-
174
- def __setitem__(self, item: Any, value: "Resource"):
175
- if not isinstance(item, type) or not issubclass(item, Extension):
176
- raise KeyError(f"{item} is not a valid extension type")
177
-
178
- setattr(self, item.__name__, value)
179
-
180
- @classmethod
181
- def get_extension_models(cls) -> dict[str, type[Extension]]:
182
- """Return extension a dict associating extension models with their schemas."""
183
- extension_models = cls.__pydantic_generic_metadata__.get("args", [])
184
- extension_models = (
185
- get_args(extension_models[0])
186
- if len(extension_models) == 1
187
- and get_origin(extension_models[0]) in UNION_TYPES
188
- else extension_models
189
- )
190
-
191
- by_schema = {
192
- ext.model_fields["schemas"].default[0]: ext for ext in extension_models
193
- }
194
- return by_schema
195
-
196
- @classmethod
197
- def get_extension_model(cls, name_or_schema) -> Optional[type[Extension]]:
198
- """Return an extension by its name or schema."""
199
- for schema, extension in cls.get_extension_models().items():
200
- if schema == name_or_schema or extension.__name__ == name_or_schema:
201
- return extension
202
- return None
203
-
204
- @staticmethod
205
- def get_by_schema(
206
- resource_types: list[type[BaseModel]], schema: str, with_extensions=True
207
- ) -> Optional[type]:
208
- """Given a resource type list and a schema, find the matching resource type."""
209
- by_schema = {
210
- resource_type.model_fields["schemas"].default[0].lower(): resource_type
211
- for resource_type in (resource_types or [])
212
- }
213
- if with_extensions:
214
- for resource_type in list(by_schema.values()):
215
- by_schema.update(
216
- {
217
- schema.lower(): extension
218
- for schema, extension in resource_type.get_extension_models().items()
219
- }
220
- )
221
-
222
- return by_schema.get(schema.lower())
223
-
224
- @staticmethod
225
- def get_by_payload(resource_types: list[type], payload: dict, **kwargs):
226
- """Given a resource type list and a payload, find the matching resource type."""
227
- if not payload or not payload.get("schemas"):
228
- return None
229
-
230
- schema = payload["schemas"][0]
231
- return Resource.get_by_schema(resource_types, schema, **kwargs)
232
-
233
- @field_serializer("schemas")
234
- def set_extension_schemas(self, schemas: Annotated[list[str], Required.true]):
235
- """Add model extension ids to the 'schemas' attribute."""
236
- extension_schemas = self.get_extension_models().keys()
237
- schemas = self.schemas + [
238
- schema for schema in extension_schemas if schema not in self.schemas
239
- ]
240
- return schemas
241
-
242
- @classmethod
243
- def to_schema(cls):
244
- """Build a :class:`~scim2_models.Schema` from the current resource class."""
245
- return model_to_schema(cls)
246
-
247
- @classmethod
248
- def from_schema(cls, schema) -> "Resource":
249
- """Build a :class:`scim2_models.Resource` subclass from the schema definition."""
250
- from .schema import make_python_model
251
-
252
- return make_python_model(schema, cls)
253
-
254
-
255
- AnyResource = TypeVar("AnyResource", bound="Resource")
256
-
257
-
258
- def dedicated_attributes(model, excluded_models):
259
- """Return attributes that are not members the parent 'excluded_models'."""
260
-
261
- def compare_field_infos(fi1, fi2):
262
- return (
263
- fi1
264
- and fi2
265
- and fi1.__slotnames__ == fi2.__slotnames__
266
- and all(
267
- getattr(fi1, attr) == getattr(fi2, attr) for attr in fi1.__slotnames__
268
- )
269
- )
270
-
271
- parent_field_infos = {
272
- field_name: field_info
273
- for excluded_model in excluded_models
274
- for field_name, field_info in excluded_model.model_fields.items()
275
- }
276
- field_infos = {
277
- field_name: field_info
278
- for field_name, field_info in model.model_fields.items()
279
- if not compare_field_infos(field_info, parent_field_infos.get(field_name))
280
- }
281
- return field_infos
282
-
283
-
284
- def model_to_schema(model: type[BaseModel]):
285
- from scim2_models.rfc7643.schema import Schema
286
-
287
- schema_urn = model.model_fields["schemas"].default[0]
288
- field_infos = dedicated_attributes(model, [Resource])
289
- attributes = [
290
- model_attribute_to_attribute(model, attribute_name)
291
- for attribute_name in field_infos
292
- if attribute_name != "schemas"
293
- ]
294
- schema = Schema(
295
- name=model.__name__,
296
- id=schema_urn,
297
- description=model.__doc__ or model.__name__,
298
- attributes=attributes,
299
- )
300
- return schema
301
-
302
-
303
- def get_reference_types(type) -> list[str]:
304
- first_arg = get_args(type)[0]
305
- types = get_args(first_arg) if get_origin(first_arg) in UNION_TYPES else [first_arg]
306
-
307
- def serialize_ref_type(ref_type):
308
- if ref_type == URIReference:
309
- return "uri"
310
-
311
- elif ref_type == ExternalReference:
312
- return "external"
313
-
314
- return get_args(ref_type)[0]
315
-
316
- return list(map(serialize_ref_type, types))
317
-
318
-
319
- def model_attribute_to_attribute(model, attribute_name):
320
- from scim2_models.rfc7643.schema import Attribute
321
-
322
- field_info = model.model_fields[attribute_name]
323
- root_type = model.get_field_root_type(attribute_name)
324
- attribute_type = Attribute.Type.from_python(root_type)
325
- sub_attributes = (
326
- [
327
- model_attribute_to_attribute(root_type, sub_attribute_name)
328
- for sub_attribute_name in dedicated_attributes(
329
- root_type, [MultiValuedComplexAttribute]
330
- )
331
- if (
332
- attribute_name != "sub_attributes"
333
- or sub_attribute_name != "sub_attributes"
334
- )
335
- ]
336
- if is_complex_attribute(root_type)
337
- else None
338
- )
339
-
340
- return Attribute(
341
- name=field_info.serialization_alias or attribute_name,
342
- type=attribute_type,
343
- multi_valued=model.get_field_multiplicity(attribute_name),
344
- description=field_info.description,
345
- canonical_values=field_info.examples,
346
- required=model.get_field_annotation(attribute_name, Required),
347
- case_exact=model.get_field_annotation(attribute_name, CaseExact),
348
- mutability=model.get_field_annotation(attribute_name, Mutability),
349
- returned=model.get_field_annotation(attribute_name, Returned),
350
- uniqueness=model.get_field_annotation(attribute_name, Uniqueness),
351
- sub_attributes=sub_attributes,
352
- reference_types=get_reference_types(root_type)
353
- if attribute_type == Attribute.Type.reference
354
- else None,
355
- )
@@ -1,136 +0,0 @@
1
- from typing import Annotated
2
- from typing import Any
3
- from typing import Generic
4
- from typing import Optional
5
- from typing import Union
6
- from typing import get_args
7
- from typing import get_origin
8
-
9
- from pydantic import Discriminator
10
- from pydantic import Field
11
- from pydantic import Tag
12
- from pydantic import ValidationInfo
13
- from pydantic import ValidatorFunctionWrapHandler
14
- from pydantic import model_validator
15
- from pydantic_core import PydanticCustomError
16
- from typing_extensions import Self
17
-
18
- from ..base import BaseModel
19
- from ..base import BaseModelType
20
- from ..base import Context
21
- from ..base import Required
22
- from ..rfc7643.resource import AnyResource
23
- from ..utils import UNION_TYPES
24
- from .message import Message
25
-
26
-
27
- class ListResponseMetaclass(BaseModelType):
28
- def tagged_resource_union(resource_union):
29
- """Build Discriminated Unions, so pydantic can guess which class are needed to instantiate by inspecting a payload.
30
-
31
- https://docs.pydantic.dev/latest/concepts/unions/#discriminated-unions
32
- """
33
- if get_origin(resource_union) not in UNION_TYPES:
34
- return resource_union
35
-
36
- resource_types = get_args(resource_union)
37
-
38
- def get_schema_from_payload(payload: Any) -> Optional[str]:
39
- if not payload:
40
- return None
41
-
42
- payload_schemas = (
43
- payload.get("schemas", [])
44
- if isinstance(payload, dict)
45
- else payload.schemas
46
- )
47
-
48
- resource_types_schemas = [
49
- resource_type.model_fields["schemas"].default[0]
50
- for resource_type in resource_types
51
- ]
52
- common_schemas = [
53
- schema for schema in payload_schemas if schema in resource_types_schemas
54
- ]
55
- return common_schemas[0] if common_schemas else None
56
-
57
- discriminator = Discriminator(get_schema_from_payload)
58
-
59
- def get_tag(resource_type: type[BaseModel]) -> Tag:
60
- return Tag(resource_type.model_fields["schemas"].default[0])
61
-
62
- tagged_resources = [
63
- Annotated[resource_type, get_tag(resource_type)]
64
- for resource_type in resource_types
65
- ]
66
- union = Union[tuple(tagged_resources)]
67
- return Annotated[union, discriminator]
68
-
69
- def __new__(cls, name, bases, attrs, **kwargs):
70
- if kwargs.get("__pydantic_generic_metadata__") and kwargs[
71
- "__pydantic_generic_metadata__"
72
- ].get("args"):
73
- tagged_union = cls.tagged_resource_union(
74
- kwargs["__pydantic_generic_metadata__"]["args"][0]
75
- )
76
- kwargs["__pydantic_generic_metadata__"]["args"] = (tagged_union,)
77
-
78
- klass = super().__new__(cls, name, bases, attrs, **kwargs)
79
- return klass
80
-
81
-
82
- class ListResponse(Message, Generic[AnyResource], metaclass=ListResponseMetaclass):
83
- schemas: Annotated[list[str], Required.true] = [
84
- "urn:ietf:params:scim:api:messages:2.0:ListResponse"
85
- ]
86
-
87
- total_results: Optional[int] = None
88
- """The total number of results returned by the list or query operation."""
89
-
90
- start_index: Optional[int] = None
91
- """The 1-based index of the first result in the current set of list
92
- results."""
93
-
94
- items_per_page: Optional[int] = None
95
- """The number of resources returned in a list response page."""
96
-
97
- resources: Optional[list[AnyResource]] = Field(
98
- None, serialization_alias="Resources"
99
- )
100
- """A multi-valued list of complex objects containing the requested
101
- resources."""
102
-
103
- @model_validator(mode="wrap")
104
- @classmethod
105
- def check_results_number(
106
- cls, value: Any, handler: ValidatorFunctionWrapHandler, info: ValidationInfo
107
- ) -> Self:
108
- """Validate result numbers.
109
-
110
- :rfc:`RFC7644 §3.4.2 <7644#section-3.4.2.4>` indicates that:
111
-
112
- - 'totalResults' is required
113
- - 'resources' must be set if 'totalResults' is non-zero.
114
- """
115
- obj = handler(value)
116
-
117
- if (
118
- not info.context
119
- or not info.context.get("scim")
120
- or not Context.is_response(info.context["scim"])
121
- ):
122
- return obj
123
-
124
- if obj.total_results is None:
125
- raise PydanticCustomError(
126
- "required_error",
127
- "Field 'total_results' is required but value is missing or null",
128
- )
129
-
130
- if obj.total_results > 0 and not obj.resources:
131
- raise PydanticCustomError(
132
- "no_resource_error",
133
- "Field 'resources' is missing or null but 'total_results' is non-zero.",
134
- )
135
-
136
- return obj
@@ -1,10 +0,0 @@
1
- from typing import Annotated
2
-
3
- from ..base import BaseModel
4
- from ..base import Required
5
-
6
-
7
- class Message(BaseModel):
8
- """SCIM protocol messages as defined by :rfc:`RFC7644 §3.1 <7644#section-3.1>`."""
9
-
10
- schemas: Annotated[list[str], Required.true]
@@ -1,70 +0,0 @@
1
- from enum import Enum
2
- from typing import Annotated
3
- from typing import Any
4
- from typing import Optional
5
-
6
- from pydantic import Field
7
- from pydantic import field_validator
8
-
9
- from ..base import ComplexAttribute
10
- from ..base import Required
11
- from .message import Message
12
-
13
-
14
- class PatchOperation(ComplexAttribute):
15
- class Op(str, Enum):
16
- replace_ = "replace"
17
- remove = "remove"
18
- add = "add"
19
-
20
- op: Optional[Optional[Op]] = None
21
- """Each PATCH operation object MUST have exactly one "op" member, whose
22
- value indicates the operation to perform and MAY be one of "add", "remove",
23
- or "replace".
24
-
25
- .. note::
26
-
27
- For the sake of compatibility with Microsoft Entra,
28
- despite :rfc:`RFC7644 §3.5.2 <7644#section-3.5.2>`, op is case-insensitive.
29
- """
30
-
31
- path: Optional[str] = None
32
- """The "path" attribute value is a String containing an attribute path
33
- describing the target of the operation."""
34
-
35
- value: Optional[Any] = None
36
-
37
- @field_validator("op", mode="before")
38
- @classmethod
39
- def normalize_op(cls, v):
40
- """Ignorecase for op.
41
-
42
- This brings
43
- `compatibility with Microsoft Entra <https://learn.microsoft.com/en-us/entra/identity/app-provisioning/use-scim-to-provision-users-and-groups#general>`_:
44
-
45
- Don't require a case-sensitive match on structural elements in SCIM,
46
- in particular PATCH op operation values, as defined in section 3.5.2.
47
- Microsoft Entra ID emits the values of op as Add, Replace, and Remove.
48
- """
49
- if isinstance(v, str):
50
- return v.lower()
51
- return v
52
-
53
-
54
- class PatchOp(Message):
55
- """Patch Operation as defined in :rfc:`RFC7644 §3.5.2 <7644#section-3.5.2>`.
56
-
57
- .. todo::
58
-
59
- The models for Patch operations are defined, but their behavior is not implemented nor tested yet.
60
- """
61
-
62
- schemas: Annotated[list[str], Required.true] = [
63
- "urn:ietf:params:scim:api:messages:2.0:PatchOp"
64
- ]
65
-
66
- operations: Optional[list[PatchOperation]] = Field(
67
- None, serialization_alias="Operations"
68
- )
69
- """The body of an HTTP PATCH request MUST contain the attribute
70
- "Operations", whose value is an array of one or more PATCH operations."""
@@ -1,24 +0,0 @@
1
- scim2_models/__init__.py,sha256=Y06vTA_51lXfv9zk_dTzyIIqEo1H2bcencvMM5KAwn8,3063
2
- scim2_models/base.py,sha256=NV-uaDWG_pN6Cl_PT1E9PqdG7QH7jMMADGi3PVVurHQ,34917
3
- scim2_models/constants.py,sha256=SuMGFtVNletdV5ZJRUcIq7o2CqZCRvOurnIdLE_cakE,540
4
- scim2_models/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- scim2_models/utils.py,sha256=MzZz212-lkVWgXcpXvNwoi_u28wBTpkTwPrfYC5v92A,2771
6
- scim2_models/rfc7643/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- scim2_models/rfc7643/enterprise_user.py,sha256=EaxdHH2dcBrwWwGpaZC6iZ9dbcaVN1NpoRLAdkTYssQ,1781
8
- scim2_models/rfc7643/group.py,sha256=JML8OtgKjvHHv2YufiYcTtbZuN5GFVQTbm1J6MLtp8o,1427
9
- scim2_models/rfc7643/resource.py,sha256=oEm0RVBFonTrrhS-iYQquAdiy4UW4nXHbk_Bf--fR3g,12849
10
- scim2_models/rfc7643/resource_type.py,sha256=vT2ItHvBzmsFVt3zbIJABxjcnSLUK-mW-zWduHxGE3k,3570
11
- scim2_models/rfc7643/schema.py,sha256=B7TzMbT6ngYQrMqvqW5_LberN6EaqtZaFwBVsgoA3S0,10388
12
- scim2_models/rfc7643/service_provider_config.py,sha256=deMNCXlqiNzuLcVRN9mdHiTUxhczDnvi-oO6k-Anj8U,5402
13
- scim2_models/rfc7643/user.py,sha256=EEje4V_zbMVwVYOu2Gj1W7GjD90WnC0lUK3FJuh4jWE,10607
14
- scim2_models/rfc7644/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- scim2_models/rfc7644/bulk.py,sha256=I6S40kyJPwDQwPFi668wFFDVTST7z6QTe9HTL5QUViI,2577
16
- scim2_models/rfc7644/error.py,sha256=l4-vtGQYm5u13-ParhbHSeqXEil0E09QXSO9twAT3SU,6185
17
- scim2_models/rfc7644/list_response.py,sha256=GNfRKpL2WK2KHGC3kg7ldaSiPwCXl0AxDfhnbHByEAY,4589
18
- scim2_models/rfc7644/message.py,sha256=F4kPqbHAka3-wZzap9a45noQZw-o1vznTJypNABBF7w,253
19
- scim2_models/rfc7644/patch_op.py,sha256=OE-ixDanTkY5zQP7EK7OAp88uE_fMk03mqmaZHxgJ-g,2210
20
- scim2_models/rfc7644/search_request.py,sha256=DRGlixcWtYtbUuP9MT7PsnvyxlONLcXGEcQveWdqQng,3003
21
- scim2_models-0.3.6.dist-info/METADATA,sha256=ihhjxgKo0dF-ufWhrgCdsNiH6vGXOplFlga07UVHrLw,16288
22
- scim2_models-0.3.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- scim2_models-0.3.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
24
- scim2_models-0.3.6.dist-info/RECORD,,
File without changes
File without changes