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