Skip to content

Commit c8193e1

Browse files
Add profileId and testmode to Client (#5)
1 parent b6ecb90 commit c8193e1

File tree

2 files changed

+123
-6
lines changed

2 files changed

+123
-6
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ This documentation is for the new Mollie's SDK. You can find more details on how
2929
* [Authentication](#authentication)
3030
* [Idempotency Key](#idempotency-key)
3131
* [Add Custom User-Agent Header](#add-custom-user-agent-header)
32+
* [Add Profile ID and Testmode to Client](#add-profile-id-and-testmode-to-client)
3233
* [Available Resources and Operations](#available-resources-and-operations)
3334
* [Retries](#retries)
3435
* [Error Handling](#error-handling)
@@ -261,6 +262,25 @@ client = ClientSDK(
261262

262263
<!-- End Add Custom User-Agent Header -->
263264

265+
<!-- Start Add Profile ID and Testmode to Client -->
266+
267+
## Add Profile ID and Testmode to Client
268+
The SDK allows you to define the `profileId` and `testmode` in the client. This way, you don't need to add this
269+
information to the payload every time when using OAuth. This will not override the details provided in the individual
270+
requests.
271+
272+
```py
273+
client = ClientSDK(
274+
security = Security(
275+
o_auth = os.getenv("CLIENT_OAUTH_KEY", "test_..."),
276+
),
277+
testmode = False,
278+
profileId = "pfl_..."
279+
)
280+
```
281+
282+
<!-- End Add Profile ID and Testmode to Client -->
283+
264284
<!-- Start Available Resources and Operations [operations] -->
265285
## Available Resources and Operations
266286

src/mollie/_hooks/mollie_hooks.py

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import uuid
22
import sys
3+
import json
34

45
from httpx import Request
56
from typing import Union
7+
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
68

79
from .types import (
810
BeforeRequestHook,
@@ -37,14 +39,36 @@ def before_request(self, hook_ctx: BeforeRequestContext, request: Request) -> Un
3739
# Customize the User Agent header
3840
headers = self._customize_user_agent(headers, hook_ctx)
3941

40-
return Request(
41-
method = request.method,
42-
url = request.url,
43-
headers = headers,
44-
content = request.content,
42+
# Update request with new headers first
43+
request = Request(
44+
method=request.method,
45+
url=request.url,
46+
headers=headers,
47+
content=request.content,
4548
extensions=request.extensions
4649
)
47-
50+
51+
# Then populate profile ID and testmode if OAuth (this may update headers again)
52+
if self._is_oauth_request(headers, hook_ctx):
53+
request = self._populate_profile_id_and_testmode(request, hook_ctx)
54+
55+
return request
56+
57+
def _is_oauth_request(self, headers: dict, hook_ctx: BeforeRequestContext) -> bool:
58+
security = hook_ctx.config.security
59+
60+
if callable(security):
61+
security = security()
62+
63+
if security is None:
64+
return False
65+
66+
o_auth = getattr(security, 'o_auth', None)
67+
if o_auth is None:
68+
return False
69+
70+
return headers.get("authorization", None) == f"Bearer {o_auth}"
71+
4872
def _handle_idempotency_key(self, headers: dict) -> dict:
4973
idempotency_key = "idempotency-key"
5074
if idempotency_key not in headers or not headers[idempotency_key]:
@@ -66,3 +90,76 @@ def _customize_user_agent(self, headers: dict, hook_ctx: BeforeRequestContext) -
6690
headers[user_agent_key] = new_user_agent
6791

6892
return headers
93+
94+
def _populate_profile_id_and_testmode(self, request: Request, hook_ctx: BeforeRequestContext) -> Request:
95+
client_profile_id = hook_ctx.config.globals.profile_id
96+
client_testmode = hook_ctx.config.globals.testmode
97+
98+
# Get the HTTP method
99+
method = request.method
100+
101+
if method == "GET":
102+
# Update the query parameters. If testmode or profileId are not present, add them.
103+
parsed_url = urlparse(str(request.url))
104+
query_params = parse_qs(parsed_url.query, keep_blank_values=True)
105+
106+
# Add profileId if not already present
107+
if client_profile_id is not None and 'profileId' not in query_params:
108+
query_params['profileId'] = [client_profile_id]
109+
110+
# Add testmode if not already present
111+
if client_testmode is not None and 'testmode' not in query_params:
112+
query_params['testmode'] = [str(client_testmode).lower()]
113+
114+
# Rebuild the URL with updated query parameters
115+
new_query = urlencode(query_params, doseq=True)
116+
new_url = urlunparse((
117+
parsed_url.scheme,
118+
parsed_url.netloc,
119+
parsed_url.path,
120+
parsed_url.params,
121+
new_query,
122+
parsed_url.fragment
123+
))
124+
125+
return Request(
126+
method=request.method,
127+
url=new_url,
128+
headers=request.headers,
129+
content=request.content,
130+
extensions=request.extensions
131+
)
132+
133+
# It's POST, DELETE, PATCH
134+
# Update the JSON body. If testmode or profileId are not present, add them.
135+
if request.content:
136+
try:
137+
body = json.loads(request.content)
138+
except (json.JSONDecodeError, TypeError):
139+
# If it's not JSON, return the request unchanged
140+
return request
141+
else:
142+
body = {}
143+
144+
# Add profileId if not already present
145+
if client_profile_id is not None and 'profileId' not in body:
146+
body['profileId'] = client_profile_id
147+
148+
# Add testmode if not already present
149+
if client_testmode is not None and 'testmode' not in body:
150+
body['testmode'] = client_testmode
151+
152+
# Create a new request with updated body
153+
new_content = json.dumps(body).encode('utf-8')
154+
155+
# Update headers with correct Content-Length
156+
new_headers = dict(request.headers)
157+
new_headers['content-length'] = str(len(new_content))
158+
159+
return Request(
160+
method=request.method,
161+
url=request.url,
162+
headers=new_headers,
163+
content=new_content,
164+
extensions=request.extensions
165+
)

0 commit comments

Comments
 (0)