Skip to content

Commit fc0556e

Browse files
author
PureCloud Jenkins
committed
107.0.0
1 parent a644fb3 commit fc0556e

404 files changed

Lines changed: 9259 additions & 8175 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,36 @@ print(usersApi.get_users_me())
5454
5555
```
5656

57+
### Authorization Code Grant
5758

59+
* The app is authenticating as a human, the [Authorization Code Grant](https://developer.mypurecloud.com/api/rest/authorization/use-authorization-code.html)
60+
* The app is served via a web server
61+
* There is server-side code that will be making API requests
62+
63+
```{"language":"python"}
64+
apiclient, auth_token_info = apiclient.get_code_authorization_token("565c3091-4107-4675-b606-b1fead2d15a4",
65+
"9pal483eSr_vCZf0qQomFK298I8htjBZo49FI_lLZQ8",
66+
"fjaXJaIG7Y-gFhgwvxvNZLj-dcqhWS_pm_n-_1MziN8",
67+
"https://redirect-uri.com/oauth/callback")
68+
usersApi = PureCloudPlatformClientV2.UsersApi(apiclient)
69+
```
70+
71+
By default the SDK will use the refresh token to request a new access token transparently when it expires. If multiple threads are running 1 thread will request a new token, other threads will wait a maximum of 10 seconds for the token refresh to complete, this time can be overriden with the _refresh_token_wait_time_ field of the _Configuration_ object.
72+
If you wish to implement the refresh logic, set _should_refresh_access_token_ to false and store the refresh token from the auth response. The expires_in value can be used to proactively request a new one before it expires:
73+
74+
```{"language":"python"}
75+
refresh_token = auth_token_info["refresh_token"]
76+
expires_in = auth_token_info["expires_in"]
77+
PureCloudPlatformClientV2.configuration.should_refresh_access_token = False
78+
```
79+
80+
When the access token expires refresh it using the refresh_code_authorization_token method using the same clientId and clientSecret as used to request it.
81+
82+
```{"language":"python"}
83+
apiclient, auth_token_info = apiclient.refresh_code_authorization_token("565c3091-4107-4675-b606-b1fead2d15a4",
84+
"9pal483eSr_vCZf0qQomFK298I8htjBZo49FI_lLZQ8",
85+
refresh_token)
86+
```
5887

5988
### Setting the Environment
6089

build/PureCloudPlatformClientV2/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,7 @@
12771277
from .models.participant import Participant
12781278
from .models.participant_attributes import ParticipantAttributes
12791279
from .models.participant_basic import ParticipantBasic
1280+
from .models.participant_metrics import ParticipantMetrics
12801281
from .models.patch_action_target import PatchActionTarget
12811282
from .models.patch_bu_rescheduling_options_management_unit_request import PatchBuReschedulingOptionsManagementUnitRequest
12821283
from .models.patch_bu_rescheduling_options_request import PatchBuReschedulingOptionsRequest
@@ -2052,6 +2053,9 @@
20522053
from .models.web_chat_routing_target import WebChatRoutingTarget
20532054
from .models.web_chat_settings import WebChatSettings
20542055
from .models.web_chat_typing import WebChatTyping
2056+
from .models.web_deployments_config_topic_web_messaging_config_change_event_body import WebDeploymentsConfigTopicWebMessagingConfigChangeEventBody
2057+
from .models.web_deployments_deployment_topic_web_messaging_config_change_event_body import WebDeploymentsDeploymentTopicWebMessagingConfigChangeEventBody
2058+
from .models.web_deployments_deployment_topic_web_messaging_deployment_change_event_body import WebDeploymentsDeploymentTopicWebMessagingDeploymentChangeEventBody
20552059
from .models.week_schedule import WeekSchedule
20562060
from .models.week_schedule_generation_result import WeekScheduleGenerationResult
20572061
from .models.week_schedule_list_item_response import WeekScheduleListItemResponse

build/PureCloudPlatformClientV2/api_client.py

Lines changed: 126 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import threading
3535
import base64
3636
import json
37+
import time
3738

3839

3940
from datetime import datetime
@@ -86,8 +87,17 @@ def __init__(self, host=None, header_name=None, header_value=None, cookie=None):
8687
# Set default User-Agent.
8788
self.user_agent = 'PureCloud SDK/python'
8889

90+
# store clientId and clientSecret to request new access token for OAuth (Code Authorization)
91+
self.client_id = ""
92+
self.client_secret = ""
8993
# access token for OAuth
9094
self.access_token = ""
95+
# refresh token for OAuth (Code Authorization)
96+
self.refresh_token = ""
97+
# lock object used to ensure 1 thread tries to request a new access token
98+
self.refresh_token_lock = threading.Lock()
99+
# flag indicating a token refresh is being carried out
100+
self.refresh_in_progress = False
91101

92102

93103

@@ -156,6 +166,83 @@ def get_saml2bearer_token(self,client_id,client_secret,org_name,assertion):
156166
self.access_token = data[0]["access_token"]
157167
return self;
158168

169+
def get_code_authorization_token(self,client_id,client_secret,auth_code,redirect_uri):
170+
""":param client_id: Client Id to authenticate with
171+
:param client_secret: Client Secret to authenticate with
172+
:param auth_code: Authorization code
173+
:param redirect_uri: Authorized redirect URI for your Code Authorization client
174+
:return:
175+
"""
176+
177+
self.client_id = client_id
178+
self.client_secret = client_secret
179+
180+
query_params = {}
181+
body = None
182+
url = re.sub(r'\/\/(api)\.', '//login.', self.host) + '/oauth/token'
183+
184+
post_params = {'grant_type': 'authorization_code',
185+
'code': auth_code,
186+
'redirect_uri': redirect_uri
187+
}
188+
189+
auth_string = 'Basic ' + base64.b64encode(bytes((client_id + ':' + client_secret).encode('ascii'))).decode(
190+
'ascii')
191+
192+
header_params = {
193+
"Authorization": auth_string,
194+
'Content-Type': 'application/x-www-form-urlencoded'
195+
}
196+
header_params = self.sanitize_for_serialization(header_params)
197+
post_params = self.sanitize_for_serialization(post_params)
198+
199+
response = self.request("POST", url,
200+
query_params=query_params,
201+
headers=header_params,
202+
post_params=post_params, body=body)
203+
data = json.loads('[' + response.data + ']')
204+
205+
self.access_token = data[0]["access_token"]
206+
self.refresh_token = data[0]["refresh_token"]
207+
208+
return self, data[0]
209+
210+
def refresh_code_authorization_token(self,client_id,client_secret,refresh_token):
211+
""":param client_id: Client Id to authenticate with
212+
:param client_secret: Client Secret to authenticate with
213+
:param refresh_token: Refresh token used to request a new access token
214+
:return:
215+
"""
216+
217+
query_params = {}
218+
body = None
219+
url = re.sub(r'\/\/(api)\.', '//login.', self.host) + '/oauth/token'
220+
221+
post_params = {'grant_type': 'refresh_token',
222+
'refresh_token': refresh_token
223+
}
224+
225+
auth_string = 'Basic ' + base64.b64encode(bytes((client_id + ':' + client_secret).encode('ascii'))).decode(
226+
'ascii')
227+
228+
header_params = {
229+
"Authorization": auth_string,
230+
'Content-Type': 'application/x-www-form-urlencoded'
231+
}
232+
header_params = self.sanitize_for_serialization(header_params)
233+
post_params = self.sanitize_for_serialization(post_params)
234+
235+
response = self.request("POST", url,
236+
query_params=query_params,
237+
headers=header_params,
238+
post_params=post_params, body=body)
239+
data = json.loads('[' + response.data + ']')
240+
241+
self.access_token = data[0]["access_token"]
242+
self.refresh_token = data[0]["refresh_token"]
243+
244+
return self, data[0]
245+
159246
@property
160247
def user_agent(self):
161248
"""
@@ -173,19 +260,42 @@ def user_agent(self, value):
173260
def set_default_header(self, header_name, header_value):
174261
self.default_headers[header_name] = header_value
175262

263+
def handle_expired_access_token(self):
264+
if self.refresh_token_lock.acquire(False):
265+
try:
266+
self.refresh_in_progress = True
267+
self.refresh_code_authorization_token(self.client_id, self.client_secret, self.refresh_token)
268+
finally:
269+
self.refresh_in_progress = False
270+
self.refresh_token_lock.release()
271+
else:
272+
start_time = time.time()
273+
sleep_duration = 0.200
274+
# Wait maximum of refresh_token_wait_time seconds for other thread to complete refresh
275+
# It would be ideal to use refresh_token_lock.acquire with a timeout value here but that was added in python 3.2 and we have to support older versions
276+
while time.time() - start_time < Configuration().refresh_token_wait_time:
277+
time.sleep(sleep_duration)
278+
if not self.refresh_in_progress:
279+
return
280+
# Abort with error if we have waited refresh_token_wait_time seconds and refresh still isn't complete
281+
raise ApiException(
282+
status=500,
283+
reason="Token refresh took longer than `{0}` seconds"
284+
.format(str(Configuration().refresh_token_wait_time))
285+
)
286+
176287
def __call_api(self, resource_path, method,
177288
path_params=None, query_params=None, header_params=None,
178289
body=None, post_params=None, files=None,
179290
response_type=None, auth_settings=None, callback=None):
180-
181291
# headers parameters
182292
header_params = header_params or {}
183293
header_params.update(self.default_headers)
184294
if self.cookie:
185295
header_params['Cookie'] = self.cookie
186296
if header_params:
187297
header_params = self.sanitize_for_serialization(header_params)
188-
header_params['purecloud-sdk'] = '106.0.0'
298+
header_params['purecloud-sdk'] = '107.0.0'
189299

190300
# path parameters
191301
if path_params:
@@ -216,11 +326,20 @@ def __call_api(self, resource_path, method,
216326
# request url
217327
url = self.host + resource_path
218328

219-
# perform request and return response
220-
response_data = self.request(method, url,
221-
query_params=query_params,
222-
headers=header_params,
223-
post_params=post_params, body=body)
329+
response_data = None
330+
331+
try:
332+
# perform request and return response
333+
response_data = self.request(method, url, query_params=query_params,
334+
headers=header_params, post_params=post_params, body=body)
335+
except ApiException as e:
336+
if Configuration().should_refresh_access_token and e.status == 401 and self.refresh_token != "":
337+
self.handle_expired_access_token()
338+
return self.__call_api(resource_path, method, path_params,
339+
query_params, header_params, body, post_params,
340+
files, response_type, auth_settings, callback)
341+
else:
342+
raise
224343

225344
self.last_response = response_data
226345

build/PureCloudPlatformClientV2/apis/architect_api.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3536,7 +3536,7 @@ def get_flow(self, flow_id, **kwargs):
35363536
:param callback function: The callback function
35373537
for asynchronous request. (optional)
35383538
:param str flow_id: Flow ID (required)
3539-
:param bool deleted: Include deleted flows
3539+
:param bool deleted: Deleted flows
35403540
:return: Flow
35413541
If the method is called asynchronously,
35423542
returns the request thread.
@@ -3716,7 +3716,7 @@ def get_flow_latestconfiguration(self, flow_id, **kwargs):
37163716
:param callback function: The callback function
37173717
for asynchronous request. (optional)
37183718
:param str flow_id: Flow ID (required)
3719-
:param bool deleted: Include deleted flows
3719+
:param bool deleted: Deleted flows
37203720
:return: object
37213721
If the method is called asynchronously,
37223722
returns the request thread.
@@ -3798,7 +3798,7 @@ def get_flow_version(self, flow_id, version_id, **kwargs):
37983798
for asynchronous request. (optional)
37993799
:param str flow_id: Flow ID (required)
38003800
:param str version_id: Version ID (required)
3801-
:param str deleted: Include deleted flows
3801+
:param str deleted: Deleted flows
38023802
:return: FlowVersion
38033803
If the method is called asynchronously,
38043804
returns the request thread.
@@ -3885,7 +3885,7 @@ def get_flow_version_configuration(self, flow_id, version_id, **kwargs):
38853885
for asynchronous request. (optional)
38863886
:param str flow_id: Flow ID (required)
38873887
:param str version_id: Version ID (required)
3888-
:param str deleted: Include deleted flows
3888+
:param str deleted: Deleted flows
38893889
:return: object
38903890
If the method is called asynchronously,
38913891
returns the request thread.
@@ -3973,7 +3973,7 @@ def get_flow_versions(self, flow_id, **kwargs):
39733973
:param str flow_id: Flow ID (required)
39743974
:param int page_number: Page number
39753975
:param int page_size: Page size
3976-
:param bool deleted: Include deleted flows
3976+
:param bool deleted: Include Deleted flows
39773977
:return: FlowVersionEntityListing
39783978
If the method is called asynchronously,
39793979
returns the request thread.

build/PureCloudPlatformClientV2/apis/conversations_api.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8776,6 +8776,90 @@ def post_conversations_call(self, conversation_id, body, **kwargs):
87768776
callback=params.get('callback'))
87778777
return response
87788778

8779+
def post_conversations_call_participant_coach(self, conversation_id, participant_id, **kwargs):
8780+
"""
8781+
Listen in on the conversation from the point of view of a given participant while speaking to just the given participant.
8782+
8783+
8784+
This method makes a synchronous HTTP request by default. To make an
8785+
asynchronous HTTP request, please define a `callback` function
8786+
to be invoked when receiving the response.
8787+
>>> def callback_function(response):
8788+
>>> pprint(response)
8789+
>>>
8790+
>>> thread = api.post_conversations_call_participant_coach(conversation_id, participant_id, callback=callback_function)
8791+
8792+
:param callback function: The callback function
8793+
for asynchronous request. (optional)
8794+
:param str conversation_id: conversationId (required)
8795+
:param str participant_id: participantId (required)
8796+
:return: None
8797+
If the method is called asynchronously,
8798+
returns the request thread.
8799+
"""
8800+
8801+
all_params = ['conversation_id', 'participant_id']
8802+
all_params.append('callback')
8803+
8804+
params = locals()
8805+
for key, val in iteritems(params['kwargs']):
8806+
if key not in all_params:
8807+
raise TypeError(
8808+
"Got an unexpected keyword argument '%s'"
8809+
" to method post_conversations_call_participant_coach" % key
8810+
)
8811+
params[key] = val
8812+
del params['kwargs']
8813+
8814+
# verify the required parameter 'conversation_id' is set
8815+
if ('conversation_id' not in params) or (params['conversation_id'] is None):
8816+
raise ValueError("Missing the required parameter `conversation_id` when calling `post_conversations_call_participant_coach`")
8817+
# verify the required parameter 'participant_id' is set
8818+
if ('participant_id' not in params) or (params['participant_id'] is None):
8819+
raise ValueError("Missing the required parameter `participant_id` when calling `post_conversations_call_participant_coach`")
8820+
8821+
8822+
resource_path = '/api/v2/conversations/calls/{conversationId}/participants/{participantId}/coach'.replace('{format}', 'json')
8823+
path_params = {}
8824+
if 'conversation_id' in params:
8825+
path_params['conversationId'] = params['conversation_id']
8826+
if 'participant_id' in params:
8827+
path_params['participantId'] = params['participant_id']
8828+
8829+
query_params = {}
8830+
8831+
header_params = {}
8832+
8833+
form_params = []
8834+
local_var_files = {}
8835+
8836+
body_params = None
8837+
8838+
# HTTP header `Accept`
8839+
header_params['Accept'] = self.api_client.\
8840+
select_header_accept(['application/json'])
8841+
if not header_params['Accept']:
8842+
del header_params['Accept']
8843+
8844+
# HTTP header `Content-Type`
8845+
header_params['Content-Type'] = self.api_client.\
8846+
select_header_content_type(['application/json'])
8847+
8848+
# Authentication setting
8849+
auth_settings = ['PureCloud OAuth']
8850+
8851+
response = self.api_client.call_api(resource_path, 'POST',
8852+
path_params,
8853+
query_params,
8854+
header_params,
8855+
body=body_params,
8856+
post_params=form_params,
8857+
files=local_var_files,
8858+
response_type=None,
8859+
auth_settings=auth_settings,
8860+
callback=params.get('callback'))
8861+
return response
8862+
87798863
def post_conversations_call_participant_consult(self, conversation_id, participant_id, body, **kwargs):
87808864
"""
87818865
Initiate and update consult transfer

0 commit comments

Comments
 (0)