scim2-client 0.5.1__py3-none-any.whl → 0.6.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.
- scim2_client/client.py +220 -20
- scim2_client/engines/httpx.py +95 -23
- scim2_client/engines/werkzeug.py +60 -18
- scim2_client/errors.py +11 -9
- {scim2_client-0.5.1.dist-info → scim2_client-0.6.0.dist-info}/METADATA +4 -3
- scim2_client-0.6.0.dist-info/RECORD +11 -0
- {scim2_client-0.5.1.dist-info → scim2_client-0.6.0.dist-info}/WHEEL +1 -1
- scim2_client-0.5.1.dist-info/RECORD +0 -11
- {scim2_client-0.5.1.dist-info → scim2_client-0.6.0.dist-info}/licenses/LICENSE.md +0 -0
scim2_client/client.py
CHANGED
|
@@ -3,6 +3,7 @@ import sys
|
|
|
3
3
|
from collections.abc import Collection
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from typing import Optional
|
|
6
|
+
from typing import TypeVar
|
|
6
7
|
from typing import Union
|
|
7
8
|
|
|
8
9
|
from pydantic import ValidationError
|
|
@@ -27,6 +28,8 @@ from scim2_client.errors import SCIMResponseErrorObject
|
|
|
27
28
|
from scim2_client.errors import UnexpectedContentType
|
|
28
29
|
from scim2_client.errors import UnexpectedStatusCode
|
|
29
30
|
|
|
31
|
+
ResourceT = TypeVar("ResourceT", bound=Resource)
|
|
32
|
+
|
|
30
33
|
BASE_HEADERS = {
|
|
31
34
|
"Accept": "application/scim+json",
|
|
32
35
|
"Content-Type": "application/scim+json",
|
|
@@ -151,6 +154,26 @@ class SCIMClient:
|
|
|
151
154
|
:rfc:`RFC7644 §3.12 <7644#section-3.12>`.
|
|
152
155
|
"""
|
|
153
156
|
|
|
157
|
+
PATCH_RESPONSE_STATUS_CODES: list[int] = [
|
|
158
|
+
200,
|
|
159
|
+
204,
|
|
160
|
+
307,
|
|
161
|
+
308,
|
|
162
|
+
400,
|
|
163
|
+
401,
|
|
164
|
+
403,
|
|
165
|
+
404,
|
|
166
|
+
409,
|
|
167
|
+
412,
|
|
168
|
+
500,
|
|
169
|
+
501,
|
|
170
|
+
]
|
|
171
|
+
"""Resource patching HTTP codes.
|
|
172
|
+
|
|
173
|
+
As defined at :rfc:`RFC7644 §3.5.2 <7644#section-3.5.2>` and
|
|
174
|
+
:rfc:`RFC7644 §3.12 <7644#section-3.12>`.
|
|
175
|
+
"""
|
|
176
|
+
|
|
154
177
|
def __init__(
|
|
155
178
|
self,
|
|
156
179
|
resource_models: Optional[Collection[type[Resource]]] = None,
|
|
@@ -179,7 +202,7 @@ class SCIMClient:
|
|
|
179
202
|
return resource_model
|
|
180
203
|
return None
|
|
181
204
|
|
|
182
|
-
def
|
|
205
|
+
def _check_resource_model(
|
|
183
206
|
self, resource_model: type[Resource], payload=None
|
|
184
207
|
) -> None:
|
|
185
208
|
if (
|
|
@@ -299,11 +322,15 @@ class SCIMClient:
|
|
|
299
322
|
if not expected_types:
|
|
300
323
|
return response_payload
|
|
301
324
|
|
|
325
|
+
# For no-content responses, return None directly
|
|
326
|
+
if response_payload is None:
|
|
327
|
+
return None
|
|
328
|
+
|
|
302
329
|
actual_type = Resource.get_by_payload(
|
|
303
330
|
expected_types, response_payload, with_extensions=False
|
|
304
331
|
)
|
|
305
332
|
|
|
306
|
-
if
|
|
333
|
+
if not actual_type:
|
|
307
334
|
expected = ", ".join([type_.__name__ for type_ in expected_types])
|
|
308
335
|
try:
|
|
309
336
|
schema = ", ".join(response_payload["schemas"])
|
|
@@ -323,12 +350,11 @@ class SCIMClient:
|
|
|
323
350
|
scim_exc.add_note(str(exc))
|
|
324
351
|
raise scim_exc from exc
|
|
325
352
|
|
|
326
|
-
def
|
|
353
|
+
def _prepare_create_request(
|
|
327
354
|
self,
|
|
328
355
|
resource: Union[AnyResource, dict],
|
|
329
356
|
check_request_payload: Optional[bool] = None,
|
|
330
357
|
expected_status_codes: Optional[list[int]] = None,
|
|
331
|
-
raise_scim_errors: Optional[bool] = None,
|
|
332
358
|
**kwargs,
|
|
333
359
|
) -> RequestPayload:
|
|
334
360
|
req = RequestPayload(
|
|
@@ -361,7 +387,7 @@ class SCIMClient:
|
|
|
361
387
|
scim_validation_exc.add_note(str(exc))
|
|
362
388
|
raise scim_validation_exc from exc
|
|
363
389
|
|
|
364
|
-
self.
|
|
390
|
+
self._check_resource_model(resource_model, resource)
|
|
365
391
|
req.expected_types = [resource.__class__]
|
|
366
392
|
req.url = req.request_kwargs.pop(
|
|
367
393
|
"url", self.resource_endpoint(resource_model)
|
|
@@ -372,14 +398,13 @@ class SCIMClient:
|
|
|
372
398
|
|
|
373
399
|
return req
|
|
374
400
|
|
|
375
|
-
def
|
|
401
|
+
def _prepare_query_request(
|
|
376
402
|
self,
|
|
377
403
|
resource_model: Optional[type[Resource]] = None,
|
|
378
404
|
id: Optional[str] = None,
|
|
379
405
|
search_request: Optional[Union[SearchRequest, dict]] = None,
|
|
380
406
|
check_request_payload: Optional[bool] = None,
|
|
381
407
|
expected_status_codes: Optional[list[int]] = None,
|
|
382
|
-
raise_scim_errors: Optional[bool] = None,
|
|
383
408
|
**kwargs,
|
|
384
409
|
) -> RequestPayload:
|
|
385
410
|
req = RequestPayload(
|
|
@@ -391,7 +416,7 @@ class SCIMClient:
|
|
|
391
416
|
check_request_payload = self.check_request_payload
|
|
392
417
|
|
|
393
418
|
if resource_model and check_request_payload:
|
|
394
|
-
self.
|
|
419
|
+
self._check_resource_model(resource_model)
|
|
395
420
|
|
|
396
421
|
payload: Optional[SearchRequest]
|
|
397
422
|
if not check_request_payload:
|
|
@@ -429,12 +454,11 @@ class SCIMClient:
|
|
|
429
454
|
|
|
430
455
|
return req
|
|
431
456
|
|
|
432
|
-
def
|
|
457
|
+
def _prepare_search_request(
|
|
433
458
|
self,
|
|
434
459
|
search_request: Optional[SearchRequest] = None,
|
|
435
460
|
check_request_payload: Optional[bool] = None,
|
|
436
461
|
expected_status_codes: Optional[list[int]] = None,
|
|
437
|
-
raise_scim_errors: Optional[bool] = None,
|
|
438
462
|
**kwargs,
|
|
439
463
|
) -> RequestPayload:
|
|
440
464
|
req = RequestPayload(
|
|
@@ -461,12 +485,11 @@ class SCIMClient:
|
|
|
461
485
|
req.expected_types = [ListResponse[Union[self.resource_models]]]
|
|
462
486
|
return req
|
|
463
487
|
|
|
464
|
-
def
|
|
488
|
+
def _prepare_delete_request(
|
|
465
489
|
self,
|
|
466
490
|
resource_model: type,
|
|
467
491
|
id: str,
|
|
468
492
|
expected_status_codes: Optional[list[int]] = None,
|
|
469
|
-
raise_scim_errors: Optional[bool] = None,
|
|
470
493
|
**kwargs,
|
|
471
494
|
) -> RequestPayload:
|
|
472
495
|
req = RequestPayload(
|
|
@@ -474,17 +497,16 @@ class SCIMClient:
|
|
|
474
497
|
request_kwargs=kwargs,
|
|
475
498
|
)
|
|
476
499
|
|
|
477
|
-
self.
|
|
500
|
+
self._check_resource_model(resource_model)
|
|
478
501
|
delete_url = self.resource_endpoint(resource_model) + f"/{id}"
|
|
479
502
|
req.url = req.request_kwargs.pop("url", delete_url)
|
|
480
503
|
return req
|
|
481
504
|
|
|
482
|
-
def
|
|
505
|
+
def _prepare_replace_request(
|
|
483
506
|
self,
|
|
484
507
|
resource: Union[AnyResource, dict],
|
|
485
508
|
check_request_payload: Optional[bool] = None,
|
|
486
509
|
expected_status_codes: Optional[list[int]] = None,
|
|
487
|
-
raise_scim_errors: Optional[bool] = None,
|
|
488
510
|
**kwargs,
|
|
489
511
|
) -> RequestPayload:
|
|
490
512
|
req = RequestPayload(
|
|
@@ -519,7 +541,7 @@ class SCIMClient:
|
|
|
519
541
|
scim_validation_exc.add_note(str(exc))
|
|
520
542
|
raise scim_validation_exc from exc
|
|
521
543
|
|
|
522
|
-
self.
|
|
544
|
+
self._check_resource_model(resource_model, resource)
|
|
523
545
|
|
|
524
546
|
if not resource.id:
|
|
525
547
|
raise SCIMRequestError("Resource must have an id", source=resource)
|
|
@@ -534,9 +556,75 @@ class SCIMClient:
|
|
|
534
556
|
|
|
535
557
|
return req
|
|
536
558
|
|
|
559
|
+
def _prepare_patch_request(
|
|
560
|
+
self,
|
|
561
|
+
resource_model: type[ResourceT],
|
|
562
|
+
id: str,
|
|
563
|
+
patch_op: Union[PatchOp[ResourceT], dict],
|
|
564
|
+
check_request_payload: Optional[bool] = None,
|
|
565
|
+
expected_status_codes: Optional[list[int]] = None,
|
|
566
|
+
**kwargs,
|
|
567
|
+
) -> RequestPayload:
|
|
568
|
+
"""Prepare a PATCH request payload.
|
|
569
|
+
|
|
570
|
+
:param resource_model: The resource type to modify (e.g., User, Group).
|
|
571
|
+
:param id: The resource ID.
|
|
572
|
+
:param patch_op: A PatchOp instance parameterized with the same resource type as resource_model
|
|
573
|
+
(e.g., PatchOp[User] when resource_model is User), or a dict representation.
|
|
574
|
+
:param check_request_payload: If :data:`False`, :code:`patch_op` is expected to be a dict
|
|
575
|
+
that will be passed as-is in the request. This value can be
|
|
576
|
+
overwritten in methods.
|
|
577
|
+
:param expected_status_codes: List of HTTP status codes expected for this request.
|
|
578
|
+
:param raise_scim_errors: If :data:`True` and the server returned an
|
|
579
|
+
:class:`~scim2_models.Error` object during a request, a
|
|
580
|
+
:class:`~scim2_client.SCIMResponseErrorObject` exception will be raised.
|
|
581
|
+
:param kwargs: Additional request parameters.
|
|
582
|
+
:return: The prepared request payload.
|
|
583
|
+
"""
|
|
584
|
+
req = RequestPayload(
|
|
585
|
+
expected_status_codes=expected_status_codes,
|
|
586
|
+
request_kwargs=kwargs,
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
if check_request_payload is None:
|
|
590
|
+
check_request_payload = self.check_request_payload
|
|
591
|
+
|
|
592
|
+
self._check_resource_model(resource_model)
|
|
593
|
+
|
|
594
|
+
if not check_request_payload:
|
|
595
|
+
req.payload = patch_op
|
|
596
|
+
req.url = req.request_kwargs.pop(
|
|
597
|
+
"url", f"{self.resource_endpoint(resource_model)}/{id}"
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
else:
|
|
601
|
+
if isinstance(patch_op, dict):
|
|
602
|
+
req.payload = patch_op
|
|
603
|
+
else:
|
|
604
|
+
try:
|
|
605
|
+
req.payload = patch_op.model_dump(
|
|
606
|
+
scim_ctx=Context.RESOURCE_PATCH_REQUEST
|
|
607
|
+
)
|
|
608
|
+
except ValidationError as exc:
|
|
609
|
+
scim_validation_exc = RequestPayloadValidationError(source=patch_op)
|
|
610
|
+
if sys.version_info >= (3, 11): # pragma: no cover
|
|
611
|
+
scim_validation_exc.add_note(str(exc))
|
|
612
|
+
raise scim_validation_exc from exc
|
|
613
|
+
|
|
614
|
+
req.url = req.request_kwargs.pop(
|
|
615
|
+
"url", f"{self.resource_endpoint(resource_model)}/{id}"
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
req.expected_types = [resource_model]
|
|
619
|
+
return req
|
|
620
|
+
|
|
537
621
|
def modify(
|
|
538
|
-
self,
|
|
539
|
-
|
|
622
|
+
self,
|
|
623
|
+
resource_model: type[ResourceT],
|
|
624
|
+
id: str,
|
|
625
|
+
patch_op: Union[PatchOp[ResourceT], dict],
|
|
626
|
+
**kwargs,
|
|
627
|
+
) -> Optional[Union[ResourceT, Error, dict]]:
|
|
540
628
|
raise NotImplementedError()
|
|
541
629
|
|
|
542
630
|
def build_resource_models(
|
|
@@ -558,7 +646,7 @@ class SCIMClient:
|
|
|
558
646
|
extension = Extension.from_schema(schema_obj)
|
|
559
647
|
extensions.append(extension)
|
|
560
648
|
if extensions:
|
|
561
|
-
model = model[tuple(extensions)]
|
|
649
|
+
model = model[Union[tuple(extensions)]]
|
|
562
650
|
resource_models.append(model)
|
|
563
651
|
|
|
564
652
|
return tuple(resource_models)
|
|
@@ -615,7 +703,7 @@ class BaseSyncSCIMClient(SCIMClient):
|
|
|
615
703
|
|
|
616
704
|
def query(
|
|
617
705
|
self,
|
|
618
|
-
resource_model: Optional[type[
|
|
706
|
+
resource_model: Optional[type[AnyResource]] = None,
|
|
619
707
|
id: Optional[str] = None,
|
|
620
708
|
search_request: Optional[Union[SearchRequest, dict]] = None,
|
|
621
709
|
check_request_payload: Optional[bool] = None,
|
|
@@ -820,6 +908,62 @@ class BaseSyncSCIMClient(SCIMClient):
|
|
|
820
908
|
"""
|
|
821
909
|
raise NotImplementedError()
|
|
822
910
|
|
|
911
|
+
def modify(
|
|
912
|
+
self,
|
|
913
|
+
resource_model: type[ResourceT],
|
|
914
|
+
id: str,
|
|
915
|
+
patch_op: Union[PatchOp[ResourceT], dict],
|
|
916
|
+
check_request_payload: Optional[bool] = None,
|
|
917
|
+
check_response_payload: Optional[bool] = None,
|
|
918
|
+
expected_status_codes: Optional[
|
|
919
|
+
list[int]
|
|
920
|
+
] = SCIMClient.PATCH_RESPONSE_STATUS_CODES,
|
|
921
|
+
raise_scim_errors: Optional[bool] = None,
|
|
922
|
+
**kwargs,
|
|
923
|
+
) -> Optional[Union[ResourceT, Error, dict]]:
|
|
924
|
+
"""Perform a PATCH request to modify a resource, as defined in :rfc:`RFC7644 §3.5.2 <7644#section-3.5.2>`.
|
|
925
|
+
|
|
926
|
+
:param resource_model: The type of the resource to modify.
|
|
927
|
+
:param id: The id of the resource to modify.
|
|
928
|
+
:param patch_op: The :class:`~scim2_models.PatchOp` object describing the modifications.
|
|
929
|
+
Must be parameterized with the same resource type as ``resource_model``
|
|
930
|
+
(e.g., :code:`PatchOp[User]` when ``resource_model`` is :code:`User`).
|
|
931
|
+
:param check_request_payload: If set, overwrites :paramref:`scim2_client.SCIMClient.check_request_payload`.
|
|
932
|
+
:param check_response_payload: If set, overwrites :paramref:`scim2_client.SCIMClient.check_response_payload`.
|
|
933
|
+
:param expected_status_codes: The list of expected status codes form the response.
|
|
934
|
+
If :data:`None` any status code is accepted.
|
|
935
|
+
:param raise_scim_errors: If set, overwrites :paramref:`scim2_client.SCIMClient.raise_scim_errors`.
|
|
936
|
+
:param kwargs: Additional parameters passed to the underlying
|
|
937
|
+
HTTP request library.
|
|
938
|
+
|
|
939
|
+
:return:
|
|
940
|
+
- An :class:`~scim2_models.Error` object in case of error.
|
|
941
|
+
- The updated object as returned by the server in case of success if status code is 200.
|
|
942
|
+
- :data:`None` in case of success if status code is 204.
|
|
943
|
+
|
|
944
|
+
:usage:
|
|
945
|
+
|
|
946
|
+
.. code-block:: python
|
|
947
|
+
:caption: Modification of a `User` resource
|
|
948
|
+
|
|
949
|
+
from scim2_models import User, PatchOp, PatchOperation
|
|
950
|
+
|
|
951
|
+
operation = PatchOperation(
|
|
952
|
+
op="replace", path="displayName", value="New Display Name"
|
|
953
|
+
)
|
|
954
|
+
patch_op = PatchOp[User](operations=[operation])
|
|
955
|
+
response = scim.modify(User, "my-user-id", patch_op)
|
|
956
|
+
# 'response' may be a User, None, or an Error object
|
|
957
|
+
|
|
958
|
+
.. tip::
|
|
959
|
+
|
|
960
|
+
Check the :attr:`~scim2_models.Context.RESOURCE_PATCH_REQUEST`
|
|
961
|
+
and :attr:`~scim2_models.Context.RESOURCE_PATCH_RESPONSE` contexts to understand
|
|
962
|
+
which value will excluded from the request payload, and which values are expected in
|
|
963
|
+
the response payload.
|
|
964
|
+
"""
|
|
965
|
+
raise NotImplementedError()
|
|
966
|
+
|
|
823
967
|
def discover(self, schemas=True, resource_types=True, service_provider_config=True):
|
|
824
968
|
"""Dynamically discover the server configuration objects.
|
|
825
969
|
|
|
@@ -1097,6 +1241,62 @@ class BaseAsyncSCIMClient(SCIMClient):
|
|
|
1097
1241
|
"""
|
|
1098
1242
|
raise NotImplementedError()
|
|
1099
1243
|
|
|
1244
|
+
async def modify(
|
|
1245
|
+
self,
|
|
1246
|
+
resource_model: type[ResourceT],
|
|
1247
|
+
id: str,
|
|
1248
|
+
patch_op: Union[PatchOp[ResourceT], dict],
|
|
1249
|
+
check_request_payload: Optional[bool] = None,
|
|
1250
|
+
check_response_payload: Optional[bool] = None,
|
|
1251
|
+
expected_status_codes: Optional[
|
|
1252
|
+
list[int]
|
|
1253
|
+
] = SCIMClient.PATCH_RESPONSE_STATUS_CODES,
|
|
1254
|
+
raise_scim_errors: Optional[bool] = None,
|
|
1255
|
+
**kwargs,
|
|
1256
|
+
) -> Optional[Union[ResourceT, Error, dict]]:
|
|
1257
|
+
"""Perform a PATCH request to modify a resource, as defined in :rfc:`RFC7644 §3.5.2 <7644#section-3.5.2>`.
|
|
1258
|
+
|
|
1259
|
+
:param resource_model: The type of the resource to modify.
|
|
1260
|
+
:param id: The id of the resource to modify.
|
|
1261
|
+
:param patch_op: The :class:`~scim2_models.PatchOp` object describing the modifications.
|
|
1262
|
+
Must be parameterized with the same resource type as ``resource_model``
|
|
1263
|
+
(e.g., :code:`PatchOp[User]` when ``resource_model`` is :code:`User`).
|
|
1264
|
+
:param check_request_payload: If set, overwrites :paramref:`scim2_client.SCIMClient.check_request_payload`.
|
|
1265
|
+
:param check_response_payload: If set, overwrites :paramref:`scim2_client.SCIMClient.check_response_payload`.
|
|
1266
|
+
:param expected_status_codes: The list of expected status codes form the response.
|
|
1267
|
+
If :data:`None` any status code is accepted.
|
|
1268
|
+
:param raise_scim_errors: If set, overwrites :paramref:`scim2_client.SCIMClient.raise_scim_errors`.
|
|
1269
|
+
:param kwargs: Additional parameters passed to the underlying
|
|
1270
|
+
HTTP request library.
|
|
1271
|
+
|
|
1272
|
+
:return:
|
|
1273
|
+
- An :class:`~scim2_models.Error` object in case of error.
|
|
1274
|
+
- The updated object as returned by the server in case of success if status code is 200.
|
|
1275
|
+
- :data:`None` in case of success if status code is 204.
|
|
1276
|
+
|
|
1277
|
+
:usage:
|
|
1278
|
+
|
|
1279
|
+
.. code-block:: python
|
|
1280
|
+
:caption: Modification of a `User` resource
|
|
1281
|
+
|
|
1282
|
+
from scim2_models import User, PatchOp, PatchOperation
|
|
1283
|
+
|
|
1284
|
+
operation = PatchOperation(
|
|
1285
|
+
op="replace", path="displayName", value="New Display Name"
|
|
1286
|
+
)
|
|
1287
|
+
patch_op = PatchOp[User](operations=[operation])
|
|
1288
|
+
response = await scim.modify(User, "my-user-id", patch_op)
|
|
1289
|
+
# 'response' may be a User, None, or an Error object
|
|
1290
|
+
|
|
1291
|
+
.. tip::
|
|
1292
|
+
|
|
1293
|
+
Check the :attr:`~scim2_models.Context.RESOURCE_PATCH_REQUEST`
|
|
1294
|
+
and :attr:`~scim2_models.Context.RESOURCE_PATCH_RESPONSE` contexts to understand
|
|
1295
|
+
which value will excluded from the request payload, and which values are expected in
|
|
1296
|
+
the response payload.
|
|
1297
|
+
"""
|
|
1298
|
+
raise NotImplementedError()
|
|
1299
|
+
|
|
1100
1300
|
async def discover(
|
|
1101
1301
|
self, schemas=True, resource_types=True, service_provider_config=True
|
|
1102
1302
|
):
|
scim2_client/engines/httpx.py
CHANGED
|
@@ -2,6 +2,7 @@ import json
|
|
|
2
2
|
import sys
|
|
3
3
|
from contextlib import contextmanager
|
|
4
4
|
from typing import Optional
|
|
5
|
+
from typing import TypeVar
|
|
5
6
|
from typing import Union
|
|
6
7
|
|
|
7
8
|
from httpx import Client
|
|
@@ -11,6 +12,7 @@ from scim2_models import AnyResource
|
|
|
11
12
|
from scim2_models import Context
|
|
12
13
|
from scim2_models import Error
|
|
13
14
|
from scim2_models import ListResponse
|
|
15
|
+
from scim2_models import PatchOp
|
|
14
16
|
from scim2_models import Resource
|
|
15
17
|
from scim2_models import SearchRequest
|
|
16
18
|
|
|
@@ -20,6 +22,8 @@ from scim2_client.errors import RequestNetworkError
|
|
|
20
22
|
from scim2_client.errors import SCIMClientError
|
|
21
23
|
from scim2_client.errors import UnexpectedContentFormat
|
|
22
24
|
|
|
25
|
+
ResourceT = TypeVar("ResourceT", bound=Resource)
|
|
26
|
+
|
|
23
27
|
|
|
24
28
|
@contextmanager
|
|
25
29
|
def handle_request_error(payload=None):
|
|
@@ -77,11 +81,10 @@ class SyncSCIMClient(BaseSyncSCIMClient):
|
|
|
77
81
|
raise_scim_errors: Optional[bool] = None,
|
|
78
82
|
**kwargs,
|
|
79
83
|
) -> Union[AnyResource, Error, dict]:
|
|
80
|
-
req = self.
|
|
84
|
+
req = self._prepare_create_request(
|
|
81
85
|
resource=resource,
|
|
82
86
|
check_request_payload=check_request_payload,
|
|
83
87
|
expected_status_codes=expected_status_codes,
|
|
84
|
-
raise_scim_errors=raise_scim_errors,
|
|
85
88
|
**kwargs,
|
|
86
89
|
)
|
|
87
90
|
|
|
@@ -102,7 +105,7 @@ class SyncSCIMClient(BaseSyncSCIMClient):
|
|
|
102
105
|
|
|
103
106
|
def query(
|
|
104
107
|
self,
|
|
105
|
-
resource_model: Optional[type[
|
|
108
|
+
resource_model: Optional[type[AnyResource]] = None,
|
|
106
109
|
id: Optional[str] = None,
|
|
107
110
|
search_request: Optional[Union[SearchRequest, dict]] = None,
|
|
108
111
|
check_request_payload: Optional[bool] = None,
|
|
@@ -112,14 +115,13 @@ class SyncSCIMClient(BaseSyncSCIMClient):
|
|
|
112
115
|
] = BaseSyncSCIMClient.QUERY_RESPONSE_STATUS_CODES,
|
|
113
116
|
raise_scim_errors: Optional[bool] = None,
|
|
114
117
|
**kwargs,
|
|
115
|
-
):
|
|
116
|
-
req = self.
|
|
118
|
+
) -> Union[AnyResource, ListResponse[AnyResource], Error, dict]:
|
|
119
|
+
req = self._prepare_query_request(
|
|
117
120
|
resource_model=resource_model,
|
|
118
121
|
id=id,
|
|
119
122
|
search_request=search_request,
|
|
120
123
|
check_request_payload=check_request_payload,
|
|
121
124
|
expected_status_codes=expected_status_codes,
|
|
122
|
-
raise_scim_errors=raise_scim_errors,
|
|
123
125
|
**kwargs,
|
|
124
126
|
)
|
|
125
127
|
|
|
@@ -151,11 +153,10 @@ class SyncSCIMClient(BaseSyncSCIMClient):
|
|
|
151
153
|
raise_scim_errors: Optional[bool] = None,
|
|
152
154
|
**kwargs,
|
|
153
155
|
) -> Union[AnyResource, ListResponse[AnyResource], Error, dict]:
|
|
154
|
-
req = self.
|
|
156
|
+
req = self._prepare_search_request(
|
|
155
157
|
search_request=search_request,
|
|
156
158
|
check_request_payload=check_request_payload,
|
|
157
159
|
expected_status_codes=expected_status_codes,
|
|
158
|
-
raise_scim_errors=raise_scim_errors,
|
|
159
160
|
**kwargs,
|
|
160
161
|
)
|
|
161
162
|
|
|
@@ -185,11 +186,10 @@ class SyncSCIMClient(BaseSyncSCIMClient):
|
|
|
185
186
|
raise_scim_errors: Optional[bool] = None,
|
|
186
187
|
**kwargs,
|
|
187
188
|
) -> Optional[Union[Error, dict]]:
|
|
188
|
-
req = self.
|
|
189
|
+
req = self._prepare_delete_request(
|
|
189
190
|
resource_model=resource_model,
|
|
190
191
|
id=id,
|
|
191
192
|
expected_status_codes=expected_status_codes,
|
|
192
|
-
raise_scim_errors=raise_scim_errors,
|
|
193
193
|
**kwargs,
|
|
194
194
|
)
|
|
195
195
|
|
|
@@ -217,11 +217,10 @@ class SyncSCIMClient(BaseSyncSCIMClient):
|
|
|
217
217
|
raise_scim_errors: Optional[bool] = None,
|
|
218
218
|
**kwargs,
|
|
219
219
|
) -> Union[AnyResource, Error, dict]:
|
|
220
|
-
req = self.
|
|
220
|
+
req = self._prepare_replace_request(
|
|
221
221
|
resource=resource,
|
|
222
222
|
check_request_payload=check_request_payload,
|
|
223
223
|
expected_status_codes=expected_status_codes,
|
|
224
|
-
raise_scim_errors=raise_scim_errors,
|
|
225
224
|
**kwargs,
|
|
226
225
|
)
|
|
227
226
|
|
|
@@ -240,6 +239,45 @@ class SyncSCIMClient(BaseSyncSCIMClient):
|
|
|
240
239
|
scim_ctx=Context.RESOURCE_REPLACEMENT_RESPONSE,
|
|
241
240
|
)
|
|
242
241
|
|
|
242
|
+
def modify(
|
|
243
|
+
self,
|
|
244
|
+
resource_model: type[ResourceT],
|
|
245
|
+
id: str,
|
|
246
|
+
patch_op: Union[PatchOp[ResourceT], dict],
|
|
247
|
+
check_request_payload: Optional[bool] = None,
|
|
248
|
+
check_response_payload: Optional[bool] = None,
|
|
249
|
+
expected_status_codes: Optional[
|
|
250
|
+
list[int]
|
|
251
|
+
] = BaseSyncSCIMClient.PATCH_RESPONSE_STATUS_CODES,
|
|
252
|
+
raise_scim_errors: Optional[bool] = None,
|
|
253
|
+
**kwargs,
|
|
254
|
+
) -> Optional[Union[ResourceT, Error, dict]]:
|
|
255
|
+
req = self._prepare_patch_request(
|
|
256
|
+
resource_model=resource_model,
|
|
257
|
+
id=id,
|
|
258
|
+
patch_op=patch_op,
|
|
259
|
+
check_request_payload=check_request_payload,
|
|
260
|
+
expected_status_codes=expected_status_codes,
|
|
261
|
+
**kwargs,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
with handle_request_error(req.payload):
|
|
265
|
+
response = self.client.patch(
|
|
266
|
+
req.url, json=req.payload, **req.request_kwargs
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
with handle_response_error(response):
|
|
270
|
+
return self.check_response(
|
|
271
|
+
payload=response.json() if response.text else None,
|
|
272
|
+
status_code=response.status_code,
|
|
273
|
+
headers=response.headers,
|
|
274
|
+
expected_status_codes=req.expected_status_codes,
|
|
275
|
+
expected_types=req.expected_types,
|
|
276
|
+
check_response_payload=check_response_payload,
|
|
277
|
+
raise_scim_errors=raise_scim_errors,
|
|
278
|
+
scim_ctx=Context.RESOURCE_PATCH_RESPONSE,
|
|
279
|
+
)
|
|
280
|
+
|
|
243
281
|
|
|
244
282
|
class AsyncSCIMClient(BaseAsyncSCIMClient):
|
|
245
283
|
"""Perform SCIM requests over the network and validate responses.
|
|
@@ -273,11 +311,10 @@ class AsyncSCIMClient(BaseAsyncSCIMClient):
|
|
|
273
311
|
raise_scim_errors: Optional[bool] = None,
|
|
274
312
|
**kwargs,
|
|
275
313
|
) -> Union[AnyResource, Error, dict]:
|
|
276
|
-
req = self.
|
|
314
|
+
req = self._prepare_create_request(
|
|
277
315
|
resource=resource,
|
|
278
316
|
check_request_payload=check_request_payload,
|
|
279
317
|
expected_status_codes=expected_status_codes,
|
|
280
|
-
raise_scim_errors=raise_scim_errors,
|
|
281
318
|
**kwargs,
|
|
282
319
|
)
|
|
283
320
|
|
|
@@ -310,14 +347,13 @@ class AsyncSCIMClient(BaseAsyncSCIMClient):
|
|
|
310
347
|
] = BaseAsyncSCIMClient.QUERY_RESPONSE_STATUS_CODES,
|
|
311
348
|
raise_scim_errors: Optional[bool] = None,
|
|
312
349
|
**kwargs,
|
|
313
|
-
):
|
|
314
|
-
req = self.
|
|
350
|
+
) -> Union[AnyResource, ListResponse[AnyResource], Error, dict]:
|
|
351
|
+
req = self._prepare_query_request(
|
|
315
352
|
resource_model=resource_model,
|
|
316
353
|
id=id,
|
|
317
354
|
search_request=search_request,
|
|
318
355
|
check_request_payload=check_request_payload,
|
|
319
356
|
expected_status_codes=expected_status_codes,
|
|
320
|
-
raise_scim_errors=raise_scim_errors,
|
|
321
357
|
**kwargs,
|
|
322
358
|
)
|
|
323
359
|
|
|
@@ -349,11 +385,10 @@ class AsyncSCIMClient(BaseAsyncSCIMClient):
|
|
|
349
385
|
raise_scim_errors: Optional[bool] = None,
|
|
350
386
|
**kwargs,
|
|
351
387
|
) -> Union[AnyResource, ListResponse[AnyResource], Error, dict]:
|
|
352
|
-
req = self.
|
|
388
|
+
req = self._prepare_search_request(
|
|
353
389
|
search_request=search_request,
|
|
354
390
|
check_request_payload=check_request_payload,
|
|
355
391
|
expected_status_codes=expected_status_codes,
|
|
356
|
-
raise_scim_errors=raise_scim_errors,
|
|
357
392
|
**kwargs,
|
|
358
393
|
)
|
|
359
394
|
|
|
@@ -385,11 +420,10 @@ class AsyncSCIMClient(BaseAsyncSCIMClient):
|
|
|
385
420
|
raise_scim_errors: Optional[bool] = None,
|
|
386
421
|
**kwargs,
|
|
387
422
|
) -> Optional[Union[Error, dict]]:
|
|
388
|
-
req = self.
|
|
423
|
+
req = self._prepare_delete_request(
|
|
389
424
|
resource_model=resource_model,
|
|
390
425
|
id=id,
|
|
391
426
|
expected_status_codes=expected_status_codes,
|
|
392
|
-
raise_scim_errors=raise_scim_errors,
|
|
393
427
|
**kwargs,
|
|
394
428
|
)
|
|
395
429
|
|
|
@@ -417,11 +451,10 @@ class AsyncSCIMClient(BaseAsyncSCIMClient):
|
|
|
417
451
|
raise_scim_errors: Optional[bool] = None,
|
|
418
452
|
**kwargs,
|
|
419
453
|
) -> Union[AnyResource, Error, dict]:
|
|
420
|
-
req = self.
|
|
454
|
+
req = self._prepare_replace_request(
|
|
421
455
|
resource=resource,
|
|
422
456
|
check_request_payload=check_request_payload,
|
|
423
457
|
expected_status_codes=expected_status_codes,
|
|
424
|
-
raise_scim_errors=raise_scim_errors,
|
|
425
458
|
**kwargs,
|
|
426
459
|
)
|
|
427
460
|
|
|
@@ -441,3 +474,42 @@ class AsyncSCIMClient(BaseAsyncSCIMClient):
|
|
|
441
474
|
raise_scim_errors=raise_scim_errors,
|
|
442
475
|
scim_ctx=Context.RESOURCE_REPLACEMENT_RESPONSE,
|
|
443
476
|
)
|
|
477
|
+
|
|
478
|
+
async def modify(
|
|
479
|
+
self,
|
|
480
|
+
resource_model: type[ResourceT],
|
|
481
|
+
id: str,
|
|
482
|
+
patch_op: Union[PatchOp[ResourceT], dict],
|
|
483
|
+
check_request_payload: Optional[bool] = None,
|
|
484
|
+
check_response_payload: Optional[bool] = None,
|
|
485
|
+
expected_status_codes: Optional[
|
|
486
|
+
list[int]
|
|
487
|
+
] = BaseAsyncSCIMClient.PATCH_RESPONSE_STATUS_CODES,
|
|
488
|
+
raise_scim_errors: Optional[bool] = None,
|
|
489
|
+
**kwargs,
|
|
490
|
+
) -> Optional[Union[ResourceT, Error, dict]]:
|
|
491
|
+
req = self._prepare_patch_request(
|
|
492
|
+
resource_model=resource_model,
|
|
493
|
+
id=id,
|
|
494
|
+
patch_op=patch_op,
|
|
495
|
+
check_request_payload=check_request_payload,
|
|
496
|
+
expected_status_codes=expected_status_codes,
|
|
497
|
+
**kwargs,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
with handle_request_error(req.payload):
|
|
501
|
+
response = await self.client.patch(
|
|
502
|
+
req.url, json=req.payload, **req.request_kwargs
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
with handle_response_error(response):
|
|
506
|
+
return self.check_response(
|
|
507
|
+
payload=response.json() if response.text else None,
|
|
508
|
+
status_code=response.status_code,
|
|
509
|
+
headers=response.headers,
|
|
510
|
+
expected_status_codes=req.expected_status_codes,
|
|
511
|
+
expected_types=req.expected_types,
|
|
512
|
+
check_response_payload=check_response_payload,
|
|
513
|
+
raise_scim_errors=raise_scim_errors,
|
|
514
|
+
scim_ctx=Context.RESOURCE_PATCH_RESPONSE,
|
|
515
|
+
)
|
scim2_client/engines/werkzeug.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from contextlib import contextmanager
|
|
3
3
|
from typing import Optional
|
|
4
|
+
from typing import TypeVar
|
|
4
5
|
from typing import Union
|
|
5
6
|
from urllib.parse import urlencode
|
|
6
7
|
|
|
@@ -8,6 +9,7 @@ from scim2_models import AnyResource
|
|
|
8
9
|
from scim2_models import Context
|
|
9
10
|
from scim2_models import Error
|
|
10
11
|
from scim2_models import ListResponse
|
|
12
|
+
from scim2_models import PatchOp
|
|
11
13
|
from scim2_models import Resource
|
|
12
14
|
from scim2_models import SearchRequest
|
|
13
15
|
from werkzeug.test import Client
|
|
@@ -16,6 +18,8 @@ from scim2_client.client import BaseSyncSCIMClient
|
|
|
16
18
|
from scim2_client.errors import SCIMClientError
|
|
17
19
|
from scim2_client.errors import UnexpectedContentFormat
|
|
18
20
|
|
|
21
|
+
ResourceT = TypeVar("ResourceT", bound=Resource)
|
|
22
|
+
|
|
19
23
|
|
|
20
24
|
@contextmanager
|
|
21
25
|
def handle_response_error(response):
|
|
@@ -86,7 +90,7 @@ class TestSCIMClient(BaseSyncSCIMClient):
|
|
|
86
90
|
self.scim_prefix = scim_prefix
|
|
87
91
|
self.environ = environ or {}
|
|
88
92
|
|
|
89
|
-
def
|
|
93
|
+
def _make_url(self, url: Optional[str]) -> str:
|
|
90
94
|
url = url or ""
|
|
91
95
|
prefix = (
|
|
92
96
|
self.scim_prefix[:-1]
|
|
@@ -110,16 +114,17 @@ class TestSCIMClient(BaseSyncSCIMClient):
|
|
|
110
114
|
raise_scim_errors: Optional[bool] = None,
|
|
111
115
|
**kwargs,
|
|
112
116
|
) -> Union[AnyResource, Error, dict]:
|
|
113
|
-
req = self.
|
|
117
|
+
req = self._prepare_create_request(
|
|
114
118
|
resource=resource,
|
|
115
119
|
check_request_payload=check_request_payload,
|
|
116
120
|
expected_status_codes=expected_status_codes,
|
|
117
|
-
raise_scim_errors=raise_scim_errors,
|
|
118
121
|
**kwargs,
|
|
119
122
|
)
|
|
120
123
|
|
|
121
124
|
environ = {**self.environ, **req.request_kwargs}
|
|
122
|
-
response = self.client.post(
|
|
125
|
+
response = self.client.post(
|
|
126
|
+
self._make_url(req.url), json=req.payload, **environ
|
|
127
|
+
)
|
|
123
128
|
|
|
124
129
|
with handle_response_error(req.payload):
|
|
125
130
|
return self.check_response(
|
|
@@ -135,7 +140,7 @@ class TestSCIMClient(BaseSyncSCIMClient):
|
|
|
135
140
|
|
|
136
141
|
def query(
|
|
137
142
|
self,
|
|
138
|
-
resource_model: Optional[type[
|
|
143
|
+
resource_model: Optional[type[AnyResource]] = None,
|
|
139
144
|
id: Optional[str] = None,
|
|
140
145
|
search_request: Optional[Union[SearchRequest, dict]] = None,
|
|
141
146
|
check_request_payload: Optional[bool] = None,
|
|
@@ -145,21 +150,20 @@ class TestSCIMClient(BaseSyncSCIMClient):
|
|
|
145
150
|
] = BaseSyncSCIMClient.QUERY_RESPONSE_STATUS_CODES,
|
|
146
151
|
raise_scim_errors: Optional[bool] = None,
|
|
147
152
|
**kwargs,
|
|
148
|
-
):
|
|
149
|
-
req = self.
|
|
153
|
+
) -> Union[AnyResource, ListResponse[AnyResource], Error, dict]:
|
|
154
|
+
req = self._prepare_query_request(
|
|
150
155
|
resource_model=resource_model,
|
|
151
156
|
id=id,
|
|
152
157
|
search_request=search_request,
|
|
153
158
|
check_request_payload=check_request_payload,
|
|
154
159
|
expected_status_codes=expected_status_codes,
|
|
155
|
-
raise_scim_errors=raise_scim_errors,
|
|
156
160
|
**kwargs,
|
|
157
161
|
)
|
|
158
162
|
|
|
159
163
|
query_string = urlencode(req.payload, doseq=False) if req.payload else None
|
|
160
164
|
environ = {**self.environ, **req.request_kwargs}
|
|
161
165
|
response = self.client.get(
|
|
162
|
-
self.
|
|
166
|
+
self._make_url(req.url), query_string=query_string, **environ
|
|
163
167
|
)
|
|
164
168
|
|
|
165
169
|
with handle_response_error(req.payload):
|
|
@@ -185,16 +189,17 @@ class TestSCIMClient(BaseSyncSCIMClient):
|
|
|
185
189
|
raise_scim_errors: Optional[bool] = None,
|
|
186
190
|
**kwargs,
|
|
187
191
|
) -> Union[AnyResource, ListResponse[AnyResource], Error, dict]:
|
|
188
|
-
req = self.
|
|
192
|
+
req = self._prepare_search_request(
|
|
189
193
|
search_request=search_request,
|
|
190
194
|
check_request_payload=check_request_payload,
|
|
191
195
|
expected_status_codes=expected_status_codes,
|
|
192
|
-
raise_scim_errors=raise_scim_errors,
|
|
193
196
|
**kwargs,
|
|
194
197
|
)
|
|
195
198
|
|
|
196
199
|
environ = {**self.environ, **req.request_kwargs}
|
|
197
|
-
response = self.client.post(
|
|
200
|
+
response = self.client.post(
|
|
201
|
+
self._make_url(req.url), json=req.payload, **environ
|
|
202
|
+
)
|
|
198
203
|
|
|
199
204
|
with handle_response_error(response):
|
|
200
205
|
return self.check_response(
|
|
@@ -219,16 +224,15 @@ class TestSCIMClient(BaseSyncSCIMClient):
|
|
|
219
224
|
raise_scim_errors: Optional[bool] = None,
|
|
220
225
|
**kwargs,
|
|
221
226
|
) -> Optional[Union[Error, dict]]:
|
|
222
|
-
req = self.
|
|
227
|
+
req = self._prepare_delete_request(
|
|
223
228
|
resource_model=resource_model,
|
|
224
229
|
id=id,
|
|
225
230
|
expected_status_codes=expected_status_codes,
|
|
226
|
-
raise_scim_errors=raise_scim_errors,
|
|
227
231
|
**kwargs,
|
|
228
232
|
)
|
|
229
233
|
|
|
230
234
|
environ = {**self.environ, **req.request_kwargs}
|
|
231
|
-
response = self.client.delete(self.
|
|
235
|
+
response = self.client.delete(self._make_url(req.url), **environ)
|
|
232
236
|
|
|
233
237
|
with handle_response_error(response):
|
|
234
238
|
return self.check_response(
|
|
@@ -251,16 +255,15 @@ class TestSCIMClient(BaseSyncSCIMClient):
|
|
|
251
255
|
raise_scim_errors: Optional[bool] = None,
|
|
252
256
|
**kwargs,
|
|
253
257
|
) -> Union[AnyResource, Error, dict]:
|
|
254
|
-
req = self.
|
|
258
|
+
req = self._prepare_replace_request(
|
|
255
259
|
resource=resource,
|
|
256
260
|
check_request_payload=check_request_payload,
|
|
257
261
|
expected_status_codes=expected_status_codes,
|
|
258
|
-
raise_scim_errors=raise_scim_errors,
|
|
259
262
|
**kwargs,
|
|
260
263
|
)
|
|
261
264
|
|
|
262
265
|
environ = {**self.environ, **req.request_kwargs}
|
|
263
|
-
response = self.client.put(self.
|
|
266
|
+
response = self.client.put(self._make_url(req.url), json=req.payload, **environ)
|
|
264
267
|
|
|
265
268
|
with handle_response_error(response):
|
|
266
269
|
return self.check_response(
|
|
@@ -273,3 +276,42 @@ class TestSCIMClient(BaseSyncSCIMClient):
|
|
|
273
276
|
raise_scim_errors=raise_scim_errors,
|
|
274
277
|
scim_ctx=Context.RESOURCE_REPLACEMENT_RESPONSE,
|
|
275
278
|
)
|
|
279
|
+
|
|
280
|
+
def modify(
|
|
281
|
+
self,
|
|
282
|
+
resource_model: type[ResourceT],
|
|
283
|
+
id: str,
|
|
284
|
+
patch_op: Union[PatchOp[ResourceT], dict],
|
|
285
|
+
check_request_payload: Optional[bool] = None,
|
|
286
|
+
check_response_payload: Optional[bool] = None,
|
|
287
|
+
expected_status_codes: Optional[
|
|
288
|
+
list[int]
|
|
289
|
+
] = BaseSyncSCIMClient.PATCH_RESPONSE_STATUS_CODES,
|
|
290
|
+
raise_scim_errors: Optional[bool] = None,
|
|
291
|
+
**kwargs,
|
|
292
|
+
) -> Optional[Union[ResourceT, Error, dict]]:
|
|
293
|
+
req = self._prepare_patch_request(
|
|
294
|
+
resource_model=resource_model,
|
|
295
|
+
id=id,
|
|
296
|
+
patch_op=patch_op,
|
|
297
|
+
check_request_payload=check_request_payload,
|
|
298
|
+
expected_status_codes=expected_status_codes,
|
|
299
|
+
**kwargs,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
environ = {**self.environ, **req.request_kwargs}
|
|
303
|
+
response = self.client.patch(
|
|
304
|
+
self._make_url(req.url), json=req.payload, **environ
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
with handle_response_error(response):
|
|
308
|
+
return self.check_response(
|
|
309
|
+
payload=response.json if response.text else None,
|
|
310
|
+
status_code=response.status_code,
|
|
311
|
+
headers=response.headers,
|
|
312
|
+
expected_status_codes=req.expected_status_codes,
|
|
313
|
+
expected_types=req.expected_types,
|
|
314
|
+
check_response_payload=check_response_payload,
|
|
315
|
+
raise_scim_errors=raise_scim_errors,
|
|
316
|
+
scim_ctx=Context.RESOURCE_PATCH_RESPONSE,
|
|
317
|
+
)
|
scim2_client/errors.py
CHANGED
|
@@ -9,12 +9,14 @@ class SCIMClientError(Exception):
|
|
|
9
9
|
caused the exception.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
def __init__(
|
|
12
|
+
def __init__(
|
|
13
|
+
self, message: str, source: Any = None, *args: Any, **kwargs: Any
|
|
14
|
+
) -> None:
|
|
13
15
|
self.message = message
|
|
14
16
|
self.source = source
|
|
15
17
|
super().__init__(*args, **kwargs)
|
|
16
18
|
|
|
17
|
-
def __str__(self):
|
|
19
|
+
def __str__(self) -> str:
|
|
18
20
|
return self.message or "UNKNOWN"
|
|
19
21
|
|
|
20
22
|
|
|
@@ -29,7 +31,7 @@ class RequestNetworkError(SCIMRequestError):
|
|
|
29
31
|
The original :class:`~httpx.RequestError` is available with :attr:`~BaseException.__cause__`.
|
|
30
32
|
"""
|
|
31
33
|
|
|
32
|
-
def __init__(self, *args, **kwargs):
|
|
34
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
33
35
|
message = kwargs.pop("message", "Network error happened during request")
|
|
34
36
|
super().__init__(message, *args, **kwargs)
|
|
35
37
|
|
|
@@ -54,7 +56,7 @@ class RequestPayloadValidationError(SCIMRequestError):
|
|
|
54
56
|
print("Original validation error cause", exc.__cause__)
|
|
55
57
|
"""
|
|
56
58
|
|
|
57
|
-
def __init__(self, *args, **kwargs):
|
|
59
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
58
60
|
message = kwargs.pop("message", "Server request payload validation error")
|
|
59
61
|
super().__init__(message, *args, **kwargs)
|
|
60
62
|
|
|
@@ -69,7 +71,7 @@ class SCIMResponseErrorObject(SCIMResponseError):
|
|
|
69
71
|
Those errors are only raised when the :code:`raise_scim_errors` parameter is :data:`True`.
|
|
70
72
|
"""
|
|
71
73
|
|
|
72
|
-
def __init__(self, obj, *args, **kwargs):
|
|
74
|
+
def __init__(self, obj: Any, *args: Any, **kwargs: Any) -> None:
|
|
73
75
|
message = kwargs.pop(
|
|
74
76
|
"message", f"The server returned a SCIM Error object: {obj}"
|
|
75
77
|
)
|
|
@@ -79,7 +81,7 @@ class SCIMResponseErrorObject(SCIMResponseError):
|
|
|
79
81
|
class UnexpectedStatusCode(SCIMResponseError):
|
|
80
82
|
"""Error raised when a server returned an unexpected status code for a given :class:`~scim2_models.Context`."""
|
|
81
83
|
|
|
82
|
-
def __init__(self, status_code: int, *args, **kwargs):
|
|
84
|
+
def __init__(self, status_code: int, *args: Any, **kwargs: Any) -> None:
|
|
83
85
|
message = kwargs.pop(
|
|
84
86
|
"message", f"Unexpected response status code: {status_code}"
|
|
85
87
|
)
|
|
@@ -89,7 +91,7 @@ class UnexpectedStatusCode(SCIMResponseError):
|
|
|
89
91
|
class UnexpectedContentType(SCIMResponseError):
|
|
90
92
|
"""Error raised when a server returned an unexpected `Content-Type` header in a response."""
|
|
91
93
|
|
|
92
|
-
def __init__(self, content_type, *args, **kwargs):
|
|
94
|
+
def __init__(self, content_type: str, *args: Any, **kwargs: Any) -> None:
|
|
93
95
|
message = kwargs.pop("message", f"Unexpected content type: {content_type}")
|
|
94
96
|
super().__init__(message, *args, **kwargs)
|
|
95
97
|
|
|
@@ -97,7 +99,7 @@ class UnexpectedContentType(SCIMResponseError):
|
|
|
97
99
|
class UnexpectedContentFormat(SCIMResponseError):
|
|
98
100
|
"""Error raised when a server returned a response in a non-JSON format."""
|
|
99
101
|
|
|
100
|
-
def __init__(self, *args, **kwargs):
|
|
102
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
101
103
|
message = kwargs.pop("message", "Unexpected response content format")
|
|
102
104
|
super().__init__(message, *args, **kwargs)
|
|
103
105
|
|
|
@@ -117,6 +119,6 @@ class ResponsePayloadValidationError(SCIMResponseError):
|
|
|
117
119
|
print("Original validation error cause", exc.__cause__)
|
|
118
120
|
"""
|
|
119
121
|
|
|
120
|
-
def __init__(self, *args, **kwargs):
|
|
122
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
121
123
|
message = kwargs.pop("message", "Server response payload validation error")
|
|
122
124
|
super().__init__(message, *args, **kwargs)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: scim2-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Pythonically build SCIM requests and parse SCIM responses
|
|
5
5
|
Project-URL: documentation, https://scim2-client.readthedocs.io
|
|
6
6
|
Project-URL: repository, https://github.com/python-scim/scim2-client
|
|
@@ -208,6 +208,7 @@ License: Apache License
|
|
|
208
208
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
209
209
|
See the License for the specific language governing permissions and
|
|
210
210
|
limitations under the License.
|
|
211
|
+
License-File: LICENSE.md
|
|
211
212
|
Keywords: api,httpx,provisioning,rfc7643,rfc7644,scim,scim2
|
|
212
213
|
Classifier: Development Status :: 3 - Alpha
|
|
213
214
|
Classifier: Environment :: Web Environment
|
|
@@ -222,7 +223,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
222
223
|
Classifier: Programming Language :: Python :: 3.13
|
|
223
224
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
224
225
|
Requires-Python: >=3.9
|
|
225
|
-
Requires-Dist: scim2-models>=0.
|
|
226
|
+
Requires-Dist: scim2-models>=0.4.1
|
|
226
227
|
Provides-Extra: httpx
|
|
227
228
|
Requires-Dist: httpx>=0.28.0; extra == 'httpx'
|
|
228
229
|
Provides-Extra: werkzeug
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
scim2_client/__init__.py,sha256=l0pyBLiTpFA68ao98PqQLT_Xx0mw8BHumwrIHYCWa_M,845
|
|
2
|
+
scim2_client/client.py,sha256=Kglw0HfWHaQQ9hLiaDN92KuEn2KGHWxykm_sWNoUg1c,54867
|
|
3
|
+
scim2_client/errors.py,sha256=LTTrjwTImKcPmeEcd_WqSphaGNf_2tlRA4V7UhndAE8,4572
|
|
4
|
+
scim2_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
scim2_client/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
scim2_client/engines/httpx.py,sha256=vpMa6_1q8RsyZo1hCm8FOt5htbgiEW58LZ5EwkMu82s,20535
|
|
7
|
+
scim2_client/engines/werkzeug.py,sha256=mszoQSneEH2SsewcG7Pq6hW-zFIeyreObFuO2K89ssc,12277
|
|
8
|
+
scim2_client-0.6.0.dist-info/METADATA,sha256=vFPWc7J8NmtsibX_W-wZCvgRLMauHZX5lpCI4DxyCw4,16986
|
|
9
|
+
scim2_client-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
10
|
+
scim2_client-0.6.0.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
+
scim2_client-0.6.0.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
scim2_client/__init__.py,sha256=l0pyBLiTpFA68ao98PqQLT_Xx0mw8BHumwrIHYCWa_M,845
|
|
2
|
-
scim2_client/client.py,sha256=Ti7LNwCSa_98GK6_LkG-X0GFMf5HQ1KQ4gOubUc16to,46367
|
|
3
|
-
scim2_client/errors.py,sha256=FVmRXsaZLn1VZhJ3dSDs4IqycuU92AEun9JWWMseVO8,4397
|
|
4
|
-
scim2_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
scim2_client/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
scim2_client/engines/httpx.py,sha256=L39ZZHjqe43uIQpgh_r_maqM2gkf3_Rk6d8MeNe57gk,17781
|
|
7
|
-
scim2_client/engines/werkzeug.py,sha256=DmUBWytoWPehcrgBpGzlRJLCT7mrAW_VWSQ6BHylwCo,10800
|
|
8
|
-
scim2_client-0.5.1.dist-info/METADATA,sha256=eJVaMV-MJtKvkH3V9LyPw8hpMy_6-1W_tKCOpLFtP3A,16961
|
|
9
|
-
scim2_client-0.5.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
10
|
-
scim2_client-0.5.1.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
-
scim2_client-0.5.1.dist-info/RECORD,,
|
|
File without changes
|