scim2-client 0.1.1__py3-none-any.whl → 0.1.3__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
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import json.decoder
|
|
3
|
+
from typing import Dict
|
|
3
4
|
from typing import List
|
|
4
5
|
from typing import Optional
|
|
5
6
|
from typing import Tuple
|
|
@@ -14,8 +15,10 @@ from scim2_models import Context
|
|
|
14
15
|
from scim2_models import Error
|
|
15
16
|
from scim2_models import ListResponse
|
|
16
17
|
from scim2_models import PatchOp
|
|
18
|
+
from scim2_models import Resource
|
|
17
19
|
from scim2_models import SearchRequest
|
|
18
20
|
|
|
21
|
+
from .errors import SCIMClientError
|
|
19
22
|
from .errors import UnexpectedContentFormat
|
|
20
23
|
from .errors import UnexpectedContentType
|
|
21
24
|
from .errors import UnexpectedStatusCode
|
|
@@ -29,6 +32,71 @@ BASE_HEADERS = {
|
|
|
29
32
|
class SCIMClient:
|
|
30
33
|
"""An object that perform SCIM requests and validate responses."""
|
|
31
34
|
|
|
35
|
+
CREATION_RESPONSE_STATUS_CODES: List[int] = [
|
|
36
|
+
201,
|
|
37
|
+
409,
|
|
38
|
+
307,
|
|
39
|
+
308,
|
|
40
|
+
400,
|
|
41
|
+
401,
|
|
42
|
+
403,
|
|
43
|
+
404,
|
|
44
|
+
500,
|
|
45
|
+
]
|
|
46
|
+
"""Resource creation HTTP codes defined at :rfc:`RFC7644 §3.3
|
|
47
|
+
<7644#section-3.3>` and :rfc:`RFC7644 §3.12 <7644#section-3.12>`"""
|
|
48
|
+
|
|
49
|
+
QUERY_RESPONSE_STATUS_CODES: List[int] = [200, 400, 307, 308, 401, 403, 404, 500]
|
|
50
|
+
"""Resource querying HTTP codes defined at :rfc:`RFC7644 §3.4.2
|
|
51
|
+
<7644#section-3.4.2>` and :rfc:`RFC7644 §3.12 <7644#section-3.12>`"""
|
|
52
|
+
|
|
53
|
+
SEARCH_RESPONSE_STATUS_CODES: List[int] = [
|
|
54
|
+
200,
|
|
55
|
+
307,
|
|
56
|
+
308,
|
|
57
|
+
400,
|
|
58
|
+
401,
|
|
59
|
+
403,
|
|
60
|
+
404,
|
|
61
|
+
409,
|
|
62
|
+
413,
|
|
63
|
+
500,
|
|
64
|
+
501,
|
|
65
|
+
]
|
|
66
|
+
"""Resource querying HTTP codes defined at :rfc:`RFC7644 §3.4.3
|
|
67
|
+
<7644#section-3.4.3>` and :rfc:`RFC7644 §3.12 <7644#section-3.12>`"""
|
|
68
|
+
|
|
69
|
+
DELETION_RESPONSE_STATUS_CODES: List[int] = [
|
|
70
|
+
204,
|
|
71
|
+
307,
|
|
72
|
+
308,
|
|
73
|
+
400,
|
|
74
|
+
401,
|
|
75
|
+
403,
|
|
76
|
+
404,
|
|
77
|
+
412,
|
|
78
|
+
500,
|
|
79
|
+
501,
|
|
80
|
+
]
|
|
81
|
+
"""Resource deletion HTTP codes defined at :rfc:`RFC7644 §3.6
|
|
82
|
+
<7644#section-3.6>` and :rfc:`RFC7644 §3.12 <7644#section-3.12>`"""
|
|
83
|
+
|
|
84
|
+
REPLACEMENT_RESPONSE_STATUS_CODES: List[int] = [
|
|
85
|
+
200,
|
|
86
|
+
307,
|
|
87
|
+
308,
|
|
88
|
+
400,
|
|
89
|
+
401,
|
|
90
|
+
403,
|
|
91
|
+
404,
|
|
92
|
+
409,
|
|
93
|
+
412,
|
|
94
|
+
500,
|
|
95
|
+
501,
|
|
96
|
+
]
|
|
97
|
+
"""Resource querying HTTP codes defined at :rfc:`RFC7644 §3.4.2
|
|
98
|
+
<7644#section-3.4.2>` and :rfc:`RFC7644 §3.12 <7644#section-3.12>`"""
|
|
99
|
+
|
|
32
100
|
def __init__(self, client: Client, resource_types: Optional[Tuple[Type]] = None):
|
|
33
101
|
self.client = client
|
|
34
102
|
self.resource_types = resource_types or ()
|
|
@@ -47,7 +115,7 @@ class SCIMClient:
|
|
|
47
115
|
expected_type: Optional[Type] = None,
|
|
48
116
|
scim_ctx: Optional[Context] = None,
|
|
49
117
|
):
|
|
50
|
-
if response.status_code not in expected_status_codes:
|
|
118
|
+
if expected_status_codes and response.status_code not in expected_status_codes:
|
|
51
119
|
raise UnexpectedStatusCode(response)
|
|
52
120
|
|
|
53
121
|
# Interoperability considerations: The "application/scim+json" media
|
|
@@ -64,7 +132,8 @@ class SCIMClient:
|
|
|
64
132
|
# the errors in the body of the response in a JSON format
|
|
65
133
|
# https://datatracker.ietf.org/doc/html/rfc7644.html#section-3.12
|
|
66
134
|
|
|
67
|
-
|
|
135
|
+
no_content_status_codes = [204, 205]
|
|
136
|
+
if response.status_code in no_content_status_codes:
|
|
68
137
|
response_payload = None
|
|
69
138
|
|
|
70
139
|
else:
|
|
@@ -79,15 +148,32 @@ class SCIMClient:
|
|
|
79
148
|
pass
|
|
80
149
|
|
|
81
150
|
if expected_type:
|
|
82
|
-
|
|
151
|
+
try:
|
|
152
|
+
return expected_type.model_validate(response_payload, scim_ctx=scim_ctx)
|
|
153
|
+
except ValidationError as exc:
|
|
154
|
+
exc.response_payload = response_payload
|
|
155
|
+
raise exc
|
|
83
156
|
|
|
84
157
|
return response_payload
|
|
85
158
|
|
|
86
|
-
def create(
|
|
159
|
+
def create(
|
|
160
|
+
self,
|
|
161
|
+
resource: Union[AnyResource, Dict],
|
|
162
|
+
check_request_payload: bool = True,
|
|
163
|
+
check_response_payload: bool = True,
|
|
164
|
+
check_status_code: bool = True,
|
|
165
|
+
**kwargs,
|
|
166
|
+
) -> Union[AnyResource, Error, Dict]:
|
|
87
167
|
"""Perform a POST request to create, as defined in :rfc:`RFC7644 §3.3
|
|
88
168
|
<7644#section-3.3>`.
|
|
89
169
|
|
|
90
170
|
:param resource: The resource to create
|
|
171
|
+
If is a :data:`dict`, the resource type will be guessed from the schema.
|
|
172
|
+
:param check_request_payload: If :data:`False`,
|
|
173
|
+
:code:`resource` is expected to be a dict that will be passed as-is in the request.
|
|
174
|
+
:param check_response_payload: Whether to validate that the response payload is valid.
|
|
175
|
+
If set, the raw payload will be returned.
|
|
176
|
+
:param check_status_code: Whether to validate that the response status code is valid.
|
|
91
177
|
:param kwargs: Additional parameters passed to the underlying HTTP request
|
|
92
178
|
library.
|
|
93
179
|
|
|
@@ -96,30 +182,35 @@ class SCIMClient:
|
|
|
96
182
|
- The created object as returned by the server in case of success.
|
|
97
183
|
"""
|
|
98
184
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
185
|
+
if not check_request_payload:
|
|
186
|
+
payload = resource
|
|
187
|
+
url = kwargs.pop("url", None)
|
|
188
|
+
|
|
189
|
+
else:
|
|
190
|
+
if isinstance(resource, Resource):
|
|
191
|
+
resource_type = resource.__class__
|
|
192
|
+
|
|
193
|
+
else:
|
|
194
|
+
resource_type = Resource.get_by_payload(self.resource_types, resource)
|
|
195
|
+
if not resource_type:
|
|
196
|
+
raise SCIMClientError(
|
|
197
|
+
None, "Cannot guess resource type from the payload"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
resource = resource_type.model_validate(resource)
|
|
201
|
+
|
|
202
|
+
self.check_resource_type(resource_type)
|
|
203
|
+
url = kwargs.pop("url", self.resource_endpoint(resource_type))
|
|
204
|
+
payload = resource.model_dump(scim_ctx=Context.RESOURCE_CREATION_REQUEST)
|
|
205
|
+
|
|
206
|
+
response = self.client.post(url, json=payload, **kwargs)
|
|
207
|
+
|
|
119
208
|
return self.check_response(
|
|
120
209
|
response,
|
|
121
|
-
|
|
122
|
-
resource.__class__
|
|
210
|
+
self.CREATION_RESPONSE_STATUS_CODES if check_status_code else None,
|
|
211
|
+
resource.__class__
|
|
212
|
+
if check_request_payload and check_response_payload
|
|
213
|
+
else None,
|
|
123
214
|
scim_ctx=Context.RESOURCE_CREATION_RESPONSE,
|
|
124
215
|
)
|
|
125
216
|
|
|
@@ -127,9 +218,12 @@ class SCIMClient:
|
|
|
127
218
|
self,
|
|
128
219
|
resource_type: Type,
|
|
129
220
|
id: Optional[str] = None,
|
|
130
|
-
search_request: Optional[SearchRequest] = None,
|
|
221
|
+
search_request: Optional[Union[SearchRequest, Dict]] = None,
|
|
222
|
+
check_request_payload: bool = True,
|
|
223
|
+
check_response_payload: bool = True,
|
|
224
|
+
check_status_code: bool = True,
|
|
131
225
|
**kwargs,
|
|
132
|
-
) -> Union[AnyResource, ListResponse[AnyResource], Error]:
|
|
226
|
+
) -> Union[AnyResource, ListResponse[AnyResource], Error, Dict]:
|
|
133
227
|
"""Perform a GET request to read resources, as defined in :rfc:`RFC7644
|
|
134
228
|
§3.4.2 <7644#section-3.4.2>`.
|
|
135
229
|
|
|
@@ -139,6 +233,11 @@ class SCIMClient:
|
|
|
139
233
|
:param resource_type: A :class:`~scim2_models.Resource` subtype or :data:`None`
|
|
140
234
|
:param id: The SCIM id of an object to get, or :data:`None`
|
|
141
235
|
:param search_request: An object detailing the search query parameters.
|
|
236
|
+
:param check_request_payload: If :data:`False`,
|
|
237
|
+
:code:`search_request` is expected to be a dict that will be passed as-is in the request.
|
|
238
|
+
:param check_response_payload: Whether to validate that the response payload is valid.
|
|
239
|
+
If set, the raw payload will be returned.
|
|
240
|
+
:param check_status_code: Whether to validate that the response status code is valid.
|
|
142
241
|
:param kwargs: Additional parameters passed to the underlying HTTP request library.
|
|
143
242
|
|
|
144
243
|
:return:
|
|
@@ -148,14 +247,18 @@ class SCIMClient:
|
|
|
148
247
|
"""
|
|
149
248
|
|
|
150
249
|
self.check_resource_type(resource_type)
|
|
151
|
-
|
|
152
|
-
search_request
|
|
153
|
-
|
|
154
|
-
|
|
250
|
+
if not check_request_payload:
|
|
251
|
+
payload = search_request
|
|
252
|
+
|
|
253
|
+
else:
|
|
254
|
+
payload = (
|
|
255
|
+
search_request.model_dump(
|
|
256
|
+
exclude_unset=True,
|
|
257
|
+
scim_ctx=Context.RESOURCE_QUERY_REQUEST,
|
|
258
|
+
)
|
|
259
|
+
if search_request
|
|
260
|
+
else None
|
|
155
261
|
)
|
|
156
|
-
if search_request
|
|
157
|
-
else None
|
|
158
|
-
)
|
|
159
262
|
|
|
160
263
|
if not id:
|
|
161
264
|
expected_type = ListResponse[resource_type]
|
|
@@ -165,37 +268,31 @@ class SCIMClient:
|
|
|
165
268
|
expected_type = resource_type
|
|
166
269
|
url = self.resource_endpoint(resource_type) + f"/{id}"
|
|
167
270
|
|
|
168
|
-
expected_status_codes = [
|
|
169
|
-
# Resource querying HTTP codes defined at:
|
|
170
|
-
# https://datatracker.ietf.org/doc/html/rfc7644#section-3.4.2
|
|
171
|
-
200,
|
|
172
|
-
400,
|
|
173
|
-
# Default HTTP codes defined at:
|
|
174
|
-
# https://datatracker.ietf.org/doc/html/rfc7644.html#section-3.12
|
|
175
|
-
307,
|
|
176
|
-
308,
|
|
177
|
-
401,
|
|
178
|
-
403,
|
|
179
|
-
404,
|
|
180
|
-
500,
|
|
181
|
-
]
|
|
182
271
|
response = self.client.get(url, params=payload, **kwargs)
|
|
183
272
|
return self.check_response(
|
|
184
273
|
response,
|
|
185
|
-
|
|
186
|
-
expected_type,
|
|
274
|
+
self.QUERY_RESPONSE_STATUS_CODES if check_status_code else None,
|
|
275
|
+
expected_type if check_response_payload else None,
|
|
187
276
|
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
|
|
188
277
|
)
|
|
189
278
|
|
|
190
279
|
def query_all(
|
|
191
280
|
self,
|
|
192
281
|
search_request: Optional[SearchRequest] = None,
|
|
282
|
+
check_request_payload: bool = True,
|
|
283
|
+
check_response_payload: bool = True,
|
|
284
|
+
check_status_code: bool = True,
|
|
193
285
|
**kwargs,
|
|
194
|
-
) -> Union[AnyResource, ListResponse[AnyResource], Error]:
|
|
286
|
+
) -> Union[AnyResource, ListResponse[AnyResource], Error, Dict]:
|
|
195
287
|
"""Perform a GET request to read all available resources, as defined in
|
|
196
288
|
:rfc:`RFC7644 §3.4.2.1 <7644#section-3.4.2.1>`.
|
|
197
289
|
|
|
198
290
|
:param search_request: An object detailing the search query parameters.
|
|
291
|
+
:param check_request_payload: If :data:`False`,
|
|
292
|
+
:code:`search_request` is expected to be a dict that will be passed as-is in the request.
|
|
293
|
+
:param check_response_payload: Whether to validate that the response payload is valid.
|
|
294
|
+
If set, the raw payload will be returned.
|
|
295
|
+
:param check_status_code: Whether to validate that the response status code is valid.
|
|
199
296
|
:param kwargs: Additional parameters passed to the underlying
|
|
200
297
|
HTTP request library.
|
|
201
298
|
|
|
@@ -208,49 +305,48 @@ class SCIMClient:
|
|
|
208
305
|
# server SHALL be included, subject to filtering.
|
|
209
306
|
# https://datatracker.ietf.org/doc/html/rfc7644.html#section-3.4.2.1
|
|
210
307
|
|
|
211
|
-
|
|
212
|
-
search_request
|
|
213
|
-
|
|
308
|
+
if not check_request_payload:
|
|
309
|
+
payload = search_request
|
|
310
|
+
|
|
311
|
+
else:
|
|
312
|
+
payload = (
|
|
313
|
+
search_request.model_dump(
|
|
314
|
+
exclude_unset=True, scim_ctx=Context.RESOURCE_QUERY_REQUEST
|
|
315
|
+
)
|
|
316
|
+
if search_request
|
|
317
|
+
else None
|
|
214
318
|
)
|
|
215
|
-
if search_request
|
|
216
|
-
else None
|
|
217
|
-
)
|
|
218
|
-
response = self.client.get("/", params=payload)
|
|
219
319
|
|
|
220
|
-
|
|
221
|
-
# Resource querying HTTP codes defined at:
|
|
222
|
-
# https://datatracker.ietf.org/doc/html/rfc7644#section-3.4.2
|
|
223
|
-
200,
|
|
224
|
-
400,
|
|
225
|
-
# Default HTTP codes defined at:
|
|
226
|
-
# https://datatracker.ietf.org/doc/html/rfc7644.html#section-3.12
|
|
227
|
-
307,
|
|
228
|
-
308,
|
|
229
|
-
401,
|
|
230
|
-
403,
|
|
231
|
-
404,
|
|
232
|
-
500,
|
|
233
|
-
501,
|
|
234
|
-
]
|
|
320
|
+
response = self.client.get("/", params=payload)
|
|
235
321
|
|
|
236
322
|
return self.check_response(
|
|
237
323
|
response,
|
|
238
|
-
|
|
239
|
-
ListResponse[Union[self.resource_types]]
|
|
324
|
+
self.QUERY_RESPONSE_STATUS_CODES if check_status_code else None,
|
|
325
|
+
ListResponse[Union[self.resource_types]]
|
|
326
|
+
if check_response_payload
|
|
327
|
+
else None,
|
|
240
328
|
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
|
|
241
329
|
)
|
|
242
330
|
|
|
243
331
|
def search(
|
|
244
332
|
self,
|
|
245
333
|
search_request: Optional[SearchRequest] = None,
|
|
334
|
+
check_request_payload: bool = True,
|
|
335
|
+
check_response_payload: bool = True,
|
|
336
|
+
check_status_code: bool = True,
|
|
246
337
|
**kwargs,
|
|
247
|
-
) -> Union[AnyResource, ListResponse[AnyResource], Error]:
|
|
338
|
+
) -> Union[AnyResource, ListResponse[AnyResource], Error, Dict]:
|
|
248
339
|
"""Perform a POST search request to read all available resources, as
|
|
249
340
|
defined in :rfc:`RFC7644 §3.4.3 <7644#section-3.4.3>`.
|
|
250
341
|
|
|
251
342
|
:param resource_types: Resource type or union of types expected
|
|
252
343
|
to be read from the response.
|
|
253
344
|
:param search_request: An object detailing the search query parameters.
|
|
345
|
+
:param check_request_payload: If :data:`False`,
|
|
346
|
+
:code:`search_request` is expected to be a dict that will be passed as-is in the request.
|
|
347
|
+
:param check_response_payload: Whether to validate that the response payload is valid.
|
|
348
|
+
If set, the raw payload will be returned.
|
|
349
|
+
:param check_status_code: Whether to validate that the response status code is valid.
|
|
254
350
|
:param kwargs: Additional parameters passed to the underlying
|
|
255
351
|
HTTP request library.
|
|
256
352
|
|
|
@@ -259,43 +355,36 @@ class SCIMClient:
|
|
|
259
355
|
- A :class:`~scim2_models.ListResponse[resource_type]` object in case of success.
|
|
260
356
|
"""
|
|
261
357
|
|
|
262
|
-
|
|
263
|
-
search_request
|
|
264
|
-
|
|
358
|
+
if not check_request_payload:
|
|
359
|
+
payload = search_request
|
|
360
|
+
|
|
361
|
+
else:
|
|
362
|
+
payload = (
|
|
363
|
+
search_request.model_dump(
|
|
364
|
+
exclude_unset=True, scim_ctx=Context.RESOURCE_QUERY_RESPONSE
|
|
365
|
+
)
|
|
366
|
+
if search_request
|
|
367
|
+
else None
|
|
265
368
|
)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
response = self.client.post("/.search", params=payload)
|
|
270
|
-
|
|
271
|
-
expected_status_codes = [
|
|
272
|
-
# Resource querying HTTP codes defined at:
|
|
273
|
-
# https://datatracker.ietf.org/doc/html/rfc7644#section-3.4.3
|
|
274
|
-
200,
|
|
275
|
-
# Default HTTP codes defined at:
|
|
276
|
-
# https://datatracker.ietf.org/doc/html/rfc7644.html#section-3.12
|
|
277
|
-
307,
|
|
278
|
-
308,
|
|
279
|
-
400,
|
|
280
|
-
401,
|
|
281
|
-
403,
|
|
282
|
-
404,
|
|
283
|
-
409,
|
|
284
|
-
413,
|
|
285
|
-
500,
|
|
286
|
-
501,
|
|
287
|
-
]
|
|
369
|
+
|
|
370
|
+
response = self.client.post("/.search", json=payload)
|
|
371
|
+
|
|
288
372
|
return self.check_response(
|
|
289
373
|
response,
|
|
290
|
-
|
|
291
|
-
ListResponse[Union[self.resource_types]]
|
|
374
|
+
self.SEARCH_RESPONSE_STATUS_CODES if check_status_code else None,
|
|
375
|
+
ListResponse[Union[self.resource_types]]
|
|
376
|
+
if check_response_payload
|
|
377
|
+
else None,
|
|
292
378
|
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
|
|
293
379
|
)
|
|
294
380
|
|
|
295
|
-
def delete(
|
|
381
|
+
def delete(
|
|
382
|
+
self, resource_type: Type, id: str, check_status_code: bool = True, **kwargs
|
|
383
|
+
) -> Optional[Union[Error, Dict]]:
|
|
296
384
|
"""Perform a DELETE request to create, as defined in :rfc:`RFC7644 §3.6
|
|
297
385
|
<7644#section-3.6>`.
|
|
298
386
|
|
|
387
|
+
:param check_status_code: Whether to validate that the response status code is valid.
|
|
299
388
|
:param kwargs: Additional parameters passed to the underlying
|
|
300
389
|
HTTP request library.
|
|
301
390
|
|
|
@@ -308,29 +397,28 @@ class SCIMClient:
|
|
|
308
397
|
url = self.resource_endpoint(resource_type) + f"/{id}"
|
|
309
398
|
response = self.client.delete(url, **kwargs)
|
|
310
399
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
412,
|
|
324
|
-
500,
|
|
325
|
-
501,
|
|
326
|
-
]
|
|
327
|
-
return self.check_response(response, expected_status_codes)
|
|
328
|
-
|
|
329
|
-
def replace(self, resource: AnyResource, **kwargs) -> Union[AnyResource, Error]:
|
|
400
|
+
return self.check_response(
|
|
401
|
+
response, self.DELETION_RESPONSE_STATUS_CODES if check_status_code else None
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
def replace(
|
|
405
|
+
self,
|
|
406
|
+
resource: Union[AnyResource, Dict],
|
|
407
|
+
check_request_payload: bool = True,
|
|
408
|
+
check_response_payload: bool = True,
|
|
409
|
+
check_status_code: bool = True,
|
|
410
|
+
**kwargs,
|
|
411
|
+
) -> Union[AnyResource, Error, Dict]:
|
|
330
412
|
"""Perform a PUT request to replace a resource, as defined in
|
|
331
413
|
:rfc:`RFC7644 §3.5.1 <7644#section-3.5.1>`.
|
|
332
414
|
|
|
333
|
-
:param resource: The new
|
|
415
|
+
:param resource: The new resource to replace.
|
|
416
|
+
If is a :data:`dict`, the resource type will be guessed from the schema.
|
|
417
|
+
:param check_request_payload: If :data:`False`,
|
|
418
|
+
:code:`resource` is expected to be a dict that will be passed as-is in the request.
|
|
419
|
+
:param check_response_payload: Whether to validate that the response payload is valid.
|
|
420
|
+
If set, the raw payload will be returned.
|
|
421
|
+
:param check_status_code: Whether to validate that the response status code is valid.
|
|
334
422
|
:param kwargs: Additional parameters passed to the underlying
|
|
335
423
|
HTTP request library.
|
|
336
424
|
|
|
@@ -339,39 +427,45 @@ class SCIMClient:
|
|
|
339
427
|
- The updated object as returned by the server in case of success.
|
|
340
428
|
"""
|
|
341
429
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
430
|
+
if not check_request_payload:
|
|
431
|
+
payload = resource
|
|
432
|
+
url = kwargs.pop("url", None)
|
|
433
|
+
|
|
434
|
+
else:
|
|
435
|
+
if isinstance(resource, Resource):
|
|
436
|
+
resource_type = resource.__class__
|
|
437
|
+
|
|
438
|
+
else:
|
|
439
|
+
resource_type = Resource.get_by_payload(self.resource_types, resource)
|
|
440
|
+
if not resource_type:
|
|
441
|
+
raise SCIMClientError(
|
|
442
|
+
None, "Cannot guess resource type from the payload"
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
resource = resource_type.model_validate(resource)
|
|
446
|
+
|
|
447
|
+
self.check_resource_type(resource_type)
|
|
448
|
+
|
|
449
|
+
if not resource.id:
|
|
450
|
+
raise SCIMClientError(None, "Resource must have an id")
|
|
451
|
+
|
|
452
|
+
payload = resource.model_dump(scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST)
|
|
453
|
+
url = kwargs.pop(
|
|
454
|
+
"url", self.resource_endpoint(resource.__class__) + f"/{resource.id}"
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
response = self.client.put(url, json=payload, **kwargs)
|
|
458
|
+
|
|
367
459
|
return self.check_response(
|
|
368
460
|
response,
|
|
369
|
-
|
|
370
|
-
resource.__class__
|
|
461
|
+
self.REPLACEMENT_RESPONSE_STATUS_CODES if check_status_code else None,
|
|
462
|
+
resource.__class__
|
|
463
|
+
if check_request_payload and check_response_payload
|
|
464
|
+
else None,
|
|
371
465
|
scim_ctx=Context.RESOURCE_REPLACEMENT_RESPONSE,
|
|
372
466
|
)
|
|
373
467
|
|
|
374
468
|
def modify(
|
|
375
|
-
self, resource: AnyResource, op: PatchOp, **kwargs
|
|
376
|
-
) -> Optional[AnyResource]:
|
|
469
|
+
self, resource: Union[AnyResource, Dict], op: PatchOp, **kwargs
|
|
470
|
+
) -> Optional[Union[AnyResource, Dict]]:
|
|
377
471
|
raise NotImplementedError()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: scim2-client
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Pythonically build SCIM requests and parse SCIM responses
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: scim,scim2,provisioning,httpx,api
|
|
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
21
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
22
|
Requires-Dist: httpx (>=0.27.0,<0.28.0)
|
|
23
|
-
Requires-Dist: scim2-models (>=0.1.
|
|
23
|
+
Requires-Dist: scim2-models (>=0.1.2,<0.2.0)
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
|
|
26
26
|
# scim2-client
|
|
@@ -42,7 +42,7 @@ Here is an example of usage:
|
|
|
42
42
|
|
|
43
43
|
```python
|
|
44
44
|
import datetime
|
|
45
|
-
from httpx
|
|
45
|
+
from httpx import Client
|
|
46
46
|
from scim2_models import User, EnterpriseUserUser, Group, Error
|
|
47
47
|
from scim2_client import SCIMClient
|
|
48
48
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
scim2_client/__init__.py,sha256=2UNsl6HNtVUv5LVcnmLaCHyT4SqAUUFOIWW2r5XGv6A,338
|
|
2
|
+
scim2_client/client.py,sha256=1l7vb_OUPNzs-LP_yCmPEBafx3iwo1kgUNmWiucUDMc,17669
|
|
3
|
+
scim2_client/errors.py,sha256=uOOAwsD8rDrC8BQbwPid051YyWtexTcS8b7i6QC6-CM,1175
|
|
4
|
+
scim2_client-0.1.3.dist-info/METADATA,sha256=JxLH05nejwpIVxPOWwdsnujJed-D3ZwFPHwP9an-qc4,2654
|
|
5
|
+
scim2_client-0.1.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
6
|
+
scim2_client-0.1.3.dist-info/RECORD,,
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
scim2_client/__init__.py,sha256=2UNsl6HNtVUv5LVcnmLaCHyT4SqAUUFOIWW2r5XGv6A,338
|
|
2
|
-
scim2_client/client.py,sha256=LsG_omH6Mz4mxcqLLA6ApnVJqInAWo8GjaVRXuyf08c,13199
|
|
3
|
-
scim2_client/errors.py,sha256=uOOAwsD8rDrC8BQbwPid051YyWtexTcS8b7i6QC6-CM,1175
|
|
4
|
-
scim2_client-0.1.1.dist-info/METADATA,sha256=s1A7Uk7fz_9n0PgU7V2EqxGZMEonmfCzVod5a7l_sJ4,2654
|
|
5
|
-
scim2_client-0.1.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
6
|
-
scim2_client-0.1.1.dist-info/RECORD,,
|
|
File without changes
|