Skip to content

Commit 50c7018

Browse files
committed
refactor: use the RequestPayload dataclass
as return value for request preparation methods
1 parent b3acd9e commit 50c7018

File tree

3 files changed

+134
-100
lines changed

3 files changed

+134
-100
lines changed

scim2_client/client.py

Lines changed: 78 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sys
2+
from dataclasses import dataclass
23
from typing import Optional
34
from typing import Union
45

@@ -29,6 +30,15 @@
2930
}
3031

3132

33+
@dataclass
34+
class RequestPayload:
35+
request_kwargs: dict
36+
url: Optional[str] = None
37+
payload: Optional[dict] = None
38+
expected_types: Optional[list[type[Resource]]] = None
39+
expected_status_codes: Optional[list[int]] = None
40+
41+
3242
class BaseSCIMClient:
3343
"""The base model for request clients.
3444
@@ -279,14 +289,18 @@ def prepare_create_request(
279289
self,
280290
resource: Union[AnyResource, dict],
281291
check_request_payload: bool = True,
282-
check_response_payload: bool = True,
283-
expected_status_codes: Optional[list[int]] = CREATION_RESPONSE_STATUS_CODES,
292+
expected_status_codes: Optional[list[int]] = None,
284293
raise_scim_errors: bool = True,
285294
**kwargs,
286-
) -> tuple[str, Union[AnyResource, dict], Optional[list[type[Resource]]], dict]:
295+
) -> RequestPayload:
296+
req = RequestPayload(
297+
expected_status_codes=expected_status_codes,
298+
request_kwargs=kwargs,
299+
)
300+
287301
if not check_request_payload:
288-
payload = resource
289-
url = kwargs.pop("url", None)
302+
req.payload = resource
303+
req.url = req.request_kwargs.pop("url", None)
290304

291305
else:
292306
if isinstance(resource, Resource):
@@ -308,12 +322,15 @@ def prepare_create_request(
308322
raise scim_validation_exc from exc
309323

310324
self.check_resource_model(resource_model, resource)
311-
url = kwargs.pop("url", self.resource_endpoint(resource_model))
312-
payload = resource.model_dump(scim_ctx=Context.RESOURCE_CREATION_REQUEST)
313-
314-
expected_types = [resource.__class__] if check_request_payload else None
325+
req.expected_types = [resource.__class__]
326+
req.url = req.request_kwargs.pop(
327+
"url", self.resource_endpoint(resource_model)
328+
)
329+
req.payload = resource.model_dump(
330+
scim_ctx=Context.RESOURCE_CREATION_REQUEST
331+
)
315332

316-
return url, payload, expected_types, kwargs
333+
return req
317334

318335
def query(
319336
self,
@@ -397,11 +414,15 @@ def prepare_query_request(
397414
id: Optional[str] = None,
398415
search_request: Optional[Union[SearchRequest, dict]] = None,
399416
check_request_payload: bool = True,
400-
check_response_payload: bool = True,
401-
expected_status_codes: Optional[list[int]] = QUERY_RESPONSE_STATUS_CODES,
417+
expected_status_codes: Optional[list[int]] = None,
402418
raise_scim_errors: bool = True,
403419
**kwargs,
404-
) -> tuple[str, Union[AnyResource, dict], Optional[list[type[Resource]]], dict]:
420+
) -> RequestPayload:
421+
req = RequestPayload(
422+
expected_status_codes=expected_status_codes,
423+
request_kwargs=kwargs,
424+
)
425+
405426
if resource_model and check_request_payload:
406427
self.check_resource_model(resource_model)
407428

@@ -418,27 +439,28 @@ def prepare_query_request(
418439
else:
419440
payload = None
420441

421-
url = kwargs.pop("url", self.resource_endpoint(resource_model))
442+
req.payload = payload
443+
req.url = req.request_kwargs.pop("url", self.resource_endpoint(resource_model))
422444

423445
if resource_model is None:
424-
expected_types = [
446+
req.expected_types = [
425447
*self.resource_models,
426448
ListResponse[Union[self.resource_models]],
427449
]
428450

429451
elif resource_model == ServiceProviderConfig:
430-
expected_types = [resource_model]
452+
req.expected_types = [resource_model]
431453
if id:
432454
raise SCIMClientError("ServiceProviderConfig cannot have an id")
433455

434456
elif id:
435-
expected_types = [resource_model]
436-
url = f"{url}/{id}"
457+
req.expected_types = [resource_model]
458+
req.url = f"{req.url}/{id}"
437459

438460
else:
439-
expected_types = [ListResponse[resource_model]]
461+
req.expected_types = [ListResponse[resource_model]]
440462

441-
return url, payload, expected_types, kwargs
463+
return req
442464

443465
def search(
444466
self,
@@ -494,26 +516,30 @@ def prepare_search_request(
494516
self,
495517
search_request: Optional[SearchRequest] = None,
496518
check_request_payload: bool = True,
497-
check_response_payload: bool = True,
498-
expected_status_codes: Optional[list[int]] = SEARCH_RESPONSE_STATUS_CODES,
519+
expected_status_codes: Optional[list[int]] = None,
499520
raise_scim_errors: bool = True,
500521
**kwargs,
501-
) -> tuple[str, Union[AnyResource, dict], Optional[list[type[Resource]]], dict]:
522+
) -> RequestPayload:
523+
req = RequestPayload(
524+
expected_status_codes=expected_status_codes,
525+
request_kwargs=kwargs,
526+
)
527+
502528
if not check_request_payload:
503-
payload = search_request
529+
req.payload = search_request
504530

505531
else:
506-
payload = (
532+
req.payload = (
507533
search_request.model_dump(
508534
exclude_unset=True, scim_ctx=Context.RESOURCE_QUERY_RESPONSE
509535
)
510536
if search_request
511537
else None
512538
)
513539

514-
url = kwargs.pop("url", "/.search")
515-
expected_types = [ListResponse[Union[self.resource_models]]]
516-
return url, payload, expected_types, kwargs
540+
req.url = req.request_kwargs.pop("url", "/.search")
541+
req.expected_types = [ListResponse[Union[self.resource_models]]]
542+
return req
517543

518544
def delete(
519545
self,
@@ -558,15 +584,19 @@ def prepare_delete_request(
558584
self,
559585
resource_model: type,
560586
id: str,
561-
check_response_payload: bool = True,
562-
expected_status_codes: Optional[list[int]] = DELETION_RESPONSE_STATUS_CODES,
587+
expected_status_codes: Optional[list[int]] = None,
563588
raise_scim_errors: bool = True,
564589
**kwargs,
565-
) -> tuple[str, dict]:
590+
) -> RequestPayload:
591+
req = RequestPayload(
592+
expected_status_codes=expected_status_codes,
593+
request_kwargs=kwargs,
594+
)
595+
566596
self.check_resource_model(resource_model)
567597
delete_url = self.resource_endpoint(resource_model) + f"/{id}"
568-
url = kwargs.pop("url", delete_url)
569-
return url, kwargs
598+
req.url = req.request_kwargs.pop("url", delete_url)
599+
return req
570600

571601
def replace(
572602
self,
@@ -621,14 +651,18 @@ def prepare_replace_request(
621651
self,
622652
resource: Union[AnyResource, dict],
623653
check_request_payload: bool = True,
624-
check_response_payload: bool = True,
625-
expected_status_codes: Optional[list[int]] = REPLACEMENT_RESPONSE_STATUS_CODES,
654+
expected_status_codes: Optional[list[int]] = None,
626655
raise_scim_errors: bool = True,
627656
**kwargs,
628-
) -> tuple[str, Union[AnyResource, dict], Optional[list[type[Resource]]], dict]:
657+
) -> RequestPayload:
658+
req = RequestPayload(
659+
expected_status_codes=expected_status_codes,
660+
request_kwargs=kwargs,
661+
)
662+
629663
if not check_request_payload:
630-
payload = resource
631-
url = kwargs.pop("url", None)
664+
req.payload = resource
665+
req.url = kwargs.pop("url", None)
632666

633667
else:
634668
if isinstance(resource, Resource):
@@ -655,13 +689,15 @@ def prepare_replace_request(
655689
if not resource.id:
656690
raise SCIMRequestError("Resource must have an id", source=resource)
657691

658-
payload = resource.model_dump(scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST)
659-
url = kwargs.pop(
692+
req.expected_types = [resource.__class__]
693+
req.payload = resource.model_dump(
694+
scim_ctx=Context.RESOURCE_REPLACEMENT_REQUEST
695+
)
696+
req.url = req.request_kwargs.pop(
660697
"url", self.resource_endpoint(resource.__class__) + f"/{resource.id}"
661698
)
662699

663-
expected_types = [resource.__class__] if check_request_payload else None
664-
return url, payload, expected_types, kwargs
700+
return req
665701

666702
def modify(
667703
self, resource: Union[AnyResource, dict], op: PatchOp, **kwargs

scim2_client/engines/httpx.py

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,24 @@ def create(
6868
raise_scim_errors: bool = True,
6969
**kwargs,
7070
) -> Union[AnyResource, Error, dict]:
71-
url, payload, expected_types, request_kwargs = self.prepare_create_request(
71+
req = self.prepare_create_request(
7272
resource=resource,
7373
check_request_payload=check_request_payload,
74-
check_response_payload=check_response_payload,
7574
expected_status_codes=expected_status_codes,
7675
raise_scim_errors=raise_scim_errors,
7776
**kwargs,
7877
)
7978

80-
with handle_request_error(payload):
81-
response = self.client.post(url, json=payload, **request_kwargs)
79+
with handle_request_error(req.payload):
80+
response = self.client.post(req.url, json=req.payload, **req.request_kwargs)
8281

83-
with handle_response_error(payload):
82+
with handle_response_error(req.payload):
8483
return self.check_response(
8584
payload=response.json() if response.text else None,
8685
status_code=response.status_code,
8786
headers=response.headers,
88-
expected_status_codes=expected_status_codes,
89-
expected_types=expected_types,
87+
expected_status_codes=req.expected_status_codes,
88+
expected_types=req.expected_types,
9089
check_response_payload=check_response_payload,
9190
raise_scim_errors=raise_scim_errors,
9291
scim_ctx=Context.RESOURCE_CREATION_RESPONSE,
@@ -105,27 +104,28 @@ def query(
105104
raise_scim_errors: bool = True,
106105
**kwargs,
107106
):
108-
url, payload, expected_types, request_kwargs = self.prepare_query_request(
107+
req = self.prepare_query_request(
109108
resource_model=resource_model,
110109
id=id,
111110
search_request=search_request,
112111
check_request_payload=check_request_payload,
113-
check_response_payload=check_response_payload,
114112
expected_status_codes=expected_status_codes,
115113
raise_scim_errors=raise_scim_errors,
116114
**kwargs,
117115
)
118116

119-
with handle_request_error(payload):
120-
response = self.client.get(url, params=payload, **request_kwargs)
117+
with handle_request_error(req.payload):
118+
response = self.client.get(
119+
req.url, params=req.payload, **req.request_kwargs
120+
)
121121

122-
with handle_response_error(payload):
122+
with handle_response_error(req.payload):
123123
return self.check_response(
124124
payload=response.json() if response.text else None,
125125
status_code=response.status_code,
126126
headers=response.headers,
127-
expected_status_codes=expected_status_codes,
128-
expected_types=expected_types,
127+
expected_status_codes=req.expected_status_codes,
128+
expected_types=req.expected_types,
129129
check_response_payload=check_response_payload,
130130
raise_scim_errors=raise_scim_errors,
131131
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
@@ -142,25 +142,24 @@ def search(
142142
raise_scim_errors: bool = True,
143143
**kwargs,
144144
) -> Union[AnyResource, ListResponse[AnyResource], Error, dict]:
145-
url, payload, expected_types, request_kwargs = self.prepare_search_request(
145+
req = self.prepare_search_request(
146146
search_request=search_request,
147147
check_request_payload=check_request_payload,
148-
check_response_payload=check_response_payload,
149148
expected_status_codes=expected_status_codes,
150149
raise_scim_errors=raise_scim_errors,
151150
**kwargs,
152151
)
153152

154-
with handle_request_error(payload):
155-
response = self.client.post(url, json=payload, **request_kwargs)
153+
with handle_request_error(req.payload):
154+
response = self.client.post(req.url, json=req.payload, **req.request_kwargs)
156155

157156
with handle_response_error(response):
158157
return self.check_response(
159158
payload=response.json() if response.text else None,
160159
status_code=response.status_code,
161160
headers=response.headers,
162-
expected_status_codes=expected_status_codes,
163-
expected_types=expected_types,
161+
expected_status_codes=req.expected_status_codes,
162+
expected_types=req.expected_types,
164163
check_response_payload=check_response_payload,
165164
raise_scim_errors=raise_scim_errors,
166165
scim_ctx=Context.RESOURCE_QUERY_RESPONSE,
@@ -177,17 +176,16 @@ def delete(
177176
raise_scim_errors: bool = True,
178177
**kwargs,
179178
) -> Optional[Union[Error, dict]]:
180-
url, request_kwargs = self.prepare_delete_request(
179+
req = self.prepare_delete_request(
181180
resource_model=resource_model,
182181
id=id,
183-
check_response_payload=check_response_payload,
184182
expected_status_codes=expected_status_codes,
185183
raise_scim_errors=raise_scim_errors,
186184
**kwargs,
187185
)
188186

189187
with handle_request_error():
190-
response = self.client.delete(url, **request_kwargs)
188+
response = self.client.delete(req.url, **req.request_kwargs)
191189

192190
with handle_response_error(response):
193191
return self.check_response(
@@ -210,25 +208,24 @@ def replace(
210208
raise_scim_errors: bool = True,
211209
**kwargs,
212210
) -> Union[AnyResource, Error, dict]:
213-
url, payload, expected_types, request_kwargs = self.prepare_replace_request(
211+
req = self.prepare_replace_request(
214212
resource=resource,
215213
check_request_payload=check_request_payload,
216-
check_response_payload=check_response_payload,
217214
expected_status_codes=expected_status_codes,
218215
raise_scim_errors=raise_scim_errors,
219216
**kwargs,
220217
)
221218

222-
with handle_request_error(payload):
223-
response = self.client.put(url, json=payload, **request_kwargs)
219+
with handle_request_error(req.payload):
220+
response = self.client.put(req.url, json=req.payload, **req.request_kwargs)
224221

225222
with handle_response_error(response):
226223
return self.check_response(
227224
payload=response.json() if response.text else None,
228225
status_code=response.status_code,
229226
headers=response.headers,
230-
expected_status_codes=expected_status_codes,
231-
expected_types=expected_types,
227+
expected_status_codes=req.expected_status_codes,
228+
expected_types=req.expected_types,
232229
check_response_payload=check_response_payload,
233230
raise_scim_errors=raise_scim_errors,
234231
scim_ctx=Context.RESOURCE_REPLACEMENT_RESPONSE,

0 commit comments

Comments
 (0)