Skip to content

[Devtooling-1012] Pre/Post Hooks for Python #1067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,35 @@ class AbstractHttpClient(ABC):
def __init__(self):
self.timeout = 16000
self.https_agent = None #it is a http proxy agent will be used later
self.pre_hook = None
self.post_hook = None

def set_timeout(self, timeout):
if not isinstance(timeout, (int, float)):
raise ValueError("The 'timeout' property must be a number")
self.timeout = timeout

def set_pre_request_hook(self, hook):
"""
Sets a pre-request hook that will be called before each request

Args:
hook: A function that takes an HTTP request and returns a modified HTTP request
"""
if not callable(hook) or hook.__code__.co_argcount != 1:
raise ValueError("preHook must be a function that accepts (http_request_options)")
self.pre_hook = hook

def set_post_request_hook(self, hook):
"""
Sets a post-request hook that will be called after each request

Args:
hook: A function that takes an HTTP response and returns a modified HTTP response
"""
if not callable(hook) or hook.__code__.co_argcount != 1:
raise ValueError("postHook must be a function that accepts (RESTResponse)")
self.post_hook = hook

@abstractmethod
def request(self, http_request_options):
Expand Down
23 changes: 16 additions & 7 deletions resources/sdk/purecloudpython/extensions/default_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,49 +18,58 @@ def __init__(self, timeout=None, https_agent=None):
def request(self, http_request_options):
if not isinstance(http_request_options, HttpRequestOptions):
raise ValueError("httpRequestOptions must be an instance of HttpRequestOptions")

if self.pre_hook and callable(self.pre_hook):
http_request_options = self.pre_hook(http_request_options)

config = self.to_rest_client_config(http_request_options)

if config['method'] == "GET":
return self.rest_client.GET(config['url'],
response = self.rest_client.GET(config['url'],
query_params=config['query_params'],
headers=config['headers'])
elif config['method'] == "HEAD":
return self.rest_client.HEAD(config['url'],
response = self.rest_client.HEAD(config['url'],
query_params=config['query_params'],
headers=config['headers'])
elif config['method'] == "OPTIONS":
return self.rest_client.OPTIONS(config['url'],
response = self.rest_client.OPTIONS(config['url'],
query_params=config['query_params'],
headers=config['headers'],
post_params=config['post_params'],
body=config['body'])
elif config['method'] == "POST":
return self.rest_client.POST(config['url'],
response = self.rest_client.POST(config['url'],
query_params=config['query_params'],
headers=config['headers'],
post_params=config['post_params'],
body=config['body'])
elif config['method'] == "PUT":
return self.rest_client.PUT(config['url'],
response = self.rest_client.PUT(config['url'],
query_params=config['query_params'],
headers=config['headers'],
post_params=config['post_params'],
body=config['body'])
elif config['method'] == "PATCH":
return self.rest_client.PATCH(config['url'],
response = self.rest_client.PATCH(config['url'],
query_params=config['query_params'],
headers=config['headers'],
post_params=config['post_params'],
body=config['body'])
elif config['method'] == "DELETE":
return self.rest_client.DELETE(config['url'],
response = self.rest_client.DELETE(config['url'],
query_params=config['query_params'],
headers=config['headers'],
body=config['body'])
else:
raise ValueError(
"http method must be `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `OPTIONS`, `HEAD` or `TRACE`."
)

if self.post_hook and callable(self.post_hook):
response = self.post_hook(response)

return response


def to_rest_client_config(self, http_request_options):
Expand Down
18 changes: 16 additions & 2 deletions resources/sdk/purecloudpython/scripts/SdkTests.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ def test_7_reauthenticate(self):
client = PureCloudPlatformClientV2.ApiClient()
# Set the access token on the ApiClient instead of the configuration
client.access_token = responseJson['access_token']
# Set Pre/Post hooks for testing
client.get_http_client().set_pre_request_hook(SdkTests.pre_hook)
client.get_http_client().set_post_request_hook(SdkTests.post_hook)
SdkTests.users_api = PureCloudPlatformClientV2.UsersApi(client)

def test_8_get_user_again(self):
Expand Down Expand Up @@ -158,8 +161,19 @@ def purecloudregiontest(self,x):
'aps1.pure.cloud': PureCloudPlatformClientV2.PureCloudRegionHosts.ap_south_1,
'use2.us-gov-pure.cloud': PureCloudPlatformClientV2.PureCloudRegionHosts.us_east_2
}.get(x,x)



# test hooks
def pre_hook(http_request_options):
print('Running Pre-Request Hook:')
print('PreHook: Making ', http_request_options.method, ' request to ', http_request_options.url)
http_request_options.headers['Test-Header'] = 'PreHook-Test'
return http_request_options

def post_hook(response):
print('Running Post-Request Hook:')
print('PostHook: Received status code: ', response.status)
return response


if __name__ == '__main__':
unittest.sortTestMethodsUsing(None)
Expand Down
22 changes: 22 additions & 0 deletions resources/sdk/purecloudpython/templates/README.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,28 @@ apiclient_mtls.set_gateway(

If you require a custom HTTP client to handle mTLS, you can utilize the set_http_client() method of the API client instance to integrate your own implementation. Remember that you will be responsible for configuring the mTLS settings within your custom HTTP client.

### Using Pre Commit and Post Commit Hooks

For any custom requirements like pre validations or post cleanups (for ex: OCSP and CRL validation), we can inject the prehook and posthook functions.
The SDK's default client will make sure the injected hook functions are executed.

```python
def pre_hook(http_request_options):
try:
print('Running PreHook: Certificate Validation Checks')

// Custom validation logic here

print('Certificate Validation Complete')
except:
raise Exception('Error in prehook validation')

apiclient = PureCloudPlatformClientV2.api_client.ApiClient()
http_client = apiclient.get_http_client()

http_client.set_pre_request_hook(pre_hook)
```


## SDK Source Code Generation

Expand Down