Skip to content

Commit 58fcfc0

Browse files
ipetresfmanivinesh
authored andcommitted
TSE Feature Support
- Added support to TSE
1 parent 6721674 commit 58fcfc0

File tree

7 files changed

+109
-42
lines changed

7 files changed

+109
-42
lines changed

FuelSDK/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = '1.0.1'
1+
__version__ = '1.1.1'
22

33
# Runtime patch the suds library
44
from FuelSDK.suds_patch import _PropertyAppender

FuelSDK/client.py

+71-18
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ class ET_Client(object):
3232
authObj = None
3333
soap_client = None
3434
auth_url = None
35-
35+
soap_endpoint = None
36+
soap_cache_file = "soap_cache_file.json"
37+
3638
## get_server_wsdl - if True and a newer WSDL is on the server than the local filesystem retrieve it
3739
def __init__(self, get_server_wsdl = False, debug = False, params = None, tokenResponse=None):
3840
self.debug = debug
@@ -82,6 +84,15 @@ def __init__(self, get_server_wsdl = False, debug = False, params = None, tokenR
8284
else:
8385
wsdl_server_url = 'https://webservice.exacttarget.com/etframework.wsdl'
8486

87+
if params is not None and 'baseapiurl' in params:
88+
self.base_api_url = params['baseapiurl']
89+
elif config.has_option('Web Services', 'baseapiurl'):
90+
self.base_api_url = config.get('Web Services', 'baseapiurl')
91+
elif 'FUELSDK_BASE_API_URL' in os.environ:
92+
self.base_api_url = os.environ['FUELSDK_BASE_API_URL']
93+
else:
94+
self.base_api_url = 'https://www.exacttargetapis.com'
95+
8596
if params is not None and 'authenticationurl' in params:
8697
self.auth_url = params['authenticationurl']
8798
elif config.has_option('Web Services', 'authenticationurl'):
@@ -91,6 +102,13 @@ def __init__(self, get_server_wsdl = False, debug = False, params = None, tokenR
91102
else:
92103
self.auth_url = 'https://auth.exacttargetapis.com/v1/requestToken?legacy=1'
93104

105+
if params is not None and 'soapendpoint' in params:
106+
self.soap_endpoint = params['soapendpoint']
107+
elif config.has_option('Web Services', 'soapendpoint'):
108+
self.soap_endpoint = config.get('Web Services', 'soapendpoint')
109+
elif 'FUELSDK_SOAP_ENDPOINT' in os.environ:
110+
self.soap_endpoint = os.environ['FUELSDK_SOAP_ENDPOINT']
111+
94112
if params is not None and "wsdl_file_local_loc" in params:
95113
wsdl_file_local_location = params["wsdl_file_local_loc"]
96114
elif config.has_option("Web Services", "wsdl_file_local_loc"):
@@ -152,24 +170,22 @@ def retrieve_server_wsdl(self, wsdl_url, file_location):
152170

153171

154172
def build_soap_client(self):
155-
if self.endpoint is None:
156-
self.endpoint = self.determineStack()
157-
158-
self.authObj = {'oAuth' : {'oAuthToken' : self.internalAuthToken}}
159-
self.authObj['attributes'] = { 'oAuth' : { 'xmlns' : 'http://exacttarget.com' }}
173+
if self.soap_endpoint is None or not self.soap_endpoint:
174+
self.soap_endpoint = self.get_soap_endpoint()
160175

161176
self.soap_client = suds.client.Client(self.wsdl_file_url, faults=False, cachingpolicy=1)
162-
self.soap_client.set_options(location=self.endpoint)
177+
self.soap_client.set_options(location=self.soap_endpoint)
178+
self.soap_client.set_options(headers={'user-agent' : 'FuelSDK-Python-v1.1.1'})
163179

164180
element_oAuth = Element('oAuth', ns=('etns', 'http://exacttarget.com'))
165181
element_oAuthToken = Element('oAuthToken').setText(self.internalAuthToken)
166182
element_oAuth.append(element_oAuthToken)
167-
self.soap_client.set_options(soapheaders=(element_oAuth))
168-
183+
self.soap_client.set_options(soapheaders=(element_oAuth))
184+
169185
security = suds.wsse.Security()
170186
token = suds.wsse.UsernameToken('*', '*')
171187
security.tokens.append(token)
172-
self.soap_client.set_options(wsse=security)
188+
self.soap_client.set_options(wsse=security)
173189

174190

175191
def refresh_token(self, force_refresh = False):
@@ -178,14 +194,19 @@ def refresh_token(self, force_refresh = False):
178194
"""
179195
#If we don't already have a token or the token expires within 5 min(300 seconds), get one
180196
if (force_refresh or self.authToken is None or (self.authTokenExpiration is not None and time.time() + 300 > self.authTokenExpiration)):
181-
headers = {'content-type' : 'application/json', 'user-agent' : 'FuelSDK-Python'}
197+
headers = {'content-type' : 'application/json', 'user-agent' : 'FuelSDK-Python-v1.1.1'}
182198
if (self.authToken is None):
183199
payload = {'clientId' : self.client_id, 'clientSecret' : self.client_secret, 'accessType': 'offline'}
184200
else:
185201
payload = {'clientId' : self.client_id, 'clientSecret' : self.client_secret, 'refreshToken' : self.refreshKey, 'accessType': 'offline'}
186202
if self.refreshKey:
187203
payload['refreshToken'] = self.refreshKey
188204

205+
legacyString = "?legacy=1"
206+
if legacyString not in self.auth_url:
207+
self.auth_url = self.auth_url.strip()
208+
self.auth_url = self.auth_url + legacyString
209+
189210
r = requests.post(self.auth_url, data=json.dumps(payload), headers=headers)
190211
tokenResponse = r.json()
191212

@@ -199,21 +220,53 @@ def refresh_token(self, force_refresh = False):
199220
self.refreshKey = tokenResponse['refreshToken']
200221

201222
self.build_soap_client()
202-
203223

204-
def determineStack(self):
224+
def get_soap_cache_file(self):
225+
json_data = {}
226+
if os.path.isfile(self.soap_cache_file):
227+
file = open(self.soap_cache_file, "r")
228+
json_data = json.load(file)
229+
file.close()
230+
231+
return json_data
232+
233+
def update_cache_file(self, url):
234+
file = open(self.soap_cache_file, "w+")
235+
236+
data = {}
237+
data['url'] = url
238+
data['timestamp'] = time.time() + (10 * 60)
239+
json.dump(data, file)
240+
file.close()
241+
242+
def get_soap_endpoint(self):
243+
default_endpoint = 'https://webservice.exacttarget.com/Service.asmx'
244+
245+
cache_file_data = self.get_soap_cache_file()
246+
247+
if 'url' in cache_file_data and 'timestamp' in cache_file_data \
248+
and cache_file_data['timestamp'] > time.time():
249+
return cache_file_data['url']
250+
205251
"""
206252
find the correct url that data request web calls should go against for the token we have.
207253
"""
208254
try:
209-
r = requests.get('https://www.exacttargetapis.com/platform/v1/endpoints/soap?access_token=' + self.authToken, {'user-agent' : 'FuelSDK-Python'})
255+
r = requests.get(self.base_api_url + '/platform/v1/endpoints/soap', headers={
256+
'user-agent': 'FuelSDK-Python-v1.1.1',
257+
'authorization': 'Bearer ' + self.authToken
258+
})
259+
210260
contextResponse = r.json()
211-
if('url' in contextResponse):
212-
return str(contextResponse['url'])
261+
if ('url' in contextResponse):
262+
soap_url = str(contextResponse['url'])
263+
self.update_cache_file(soap_url)
264+
return soap_url
265+
else:
266+
return default_endpoint
213267

214268
except Exception as e:
215-
raise Exception('Unable to determine stack using /platform/v1/tokenContext: ' + e.message)
216-
269+
return default_endpoint
217270

218271
def AddSubscriberToList(self, emailAddress, listIDs, subscriberKey = None):
219272
"""

FuelSDK/config.python.template

+2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ appsignature: none
33
clientid: XXXXXXXXXXXXXXXXXXXXXXXX
44
clientsecret: XXXXXXXXXXXXXXXXXXXXXXXX
55
defaultwsdl: https://webservice.exacttarget.com/etframework.wsdl
6+
baseapiurl: https://www.exacttargetapis.com
67
authenticationurl: https://auth.exacttargetapis.com/v1/requestToken?legacy=1
8+
soapendpoint: https://webservice.exacttarget.com/Service.asmx
79
wsdl_file_local_loc: /tmp/ExactTargetWSDL.s6.xml

FuelSDK/objects.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,21 @@ def __init__(self):
5757
class ET_Campaign(ET_CUDSupportRest):
5858
def __init__(self):
5959
super(ET_Campaign, self).__init__()
60-
self.endpoint = 'https://www.exacttargetapis.com/hub/v1/campaigns/{id}'
60+
self.path = '/hub/v1/campaigns/{id}'
6161
self.urlProps = ["id"]
6262
self.urlPropsRequired = []
6363

6464
##the patch rest service is not implemented for campaigns yet. use post instead and remove this when patch is implemented on the back end
6565
def patch(self):
66-
self.endpoint = 'https://www.exacttargetapis.com/hub/v1/campaigns' #don't put the id on the url when patching via post
66+
self.path = '/hub/v1/campaigns' #don't put the id on the url when patching via post
6767
obj = super(ET_Campaign, self).post()
68-
self.endpoint = 'https://www.exacttargetapis.com/hub/v1/campaigns/{id}' #but set it back to the url with id for other operations to continue working
68+
self.path = '/hub/v1/campaigns/{id}' #but set it back to the url with id for other operations to continue working
6969
return obj
7070

7171
class ET_Campaign_Asset(ET_CUDSupportRest):
7272
def __init__(self):
7373
super(ET_Campaign_Asset, self).__init__()
74-
self.endpoint = 'https://www.exacttargetapis.com/hub/v1/campaigns/{id}/assets/{assetId}'
74+
self.path = '/hub/v1/campaigns/{id}/assets/{assetId}'
7575
self.urlProps = ["id", "assetId"]
7676
self.urlPropsRequired = ["id"]
7777

FuelSDK/rest.py

+20-16
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ class ET_BaseObject(object):
270270
auth_stub = None
271271
obj = None
272272
last_request_id = None
273-
endpoint = None
273+
path = None
274274
props = None
275275
extProps = None
276276
search_filter = None
@@ -325,11 +325,14 @@ def getMoreResults(self):
325325
class ET_GetRest(ET_Constructor):
326326
def __init__(self, auth_stub, endpoint, qs = None):
327327
auth_stub.refresh_token()
328-
fullendpoint = endpoint + '?access_token=' + auth_stub.authToken
328+
fullendpoint = endpoint
329+
urlSeparator = '?'
329330
for qStringValue in qs:
330-
fullendpoint += '&'+ qStringValue + '=' + str(qs[qStringValue])
331+
fullendpoint += urlSeparator + qStringValue + '=' + str(qs[qStringValue])
332+
urlSeparator = '&'
331333

332-
r = requests.get(fullendpoint)
334+
headers = {'authorization' : 'Bearer ' + auth_stub.authToken, 'user-agent' : 'FuelSDK-Python-v1.1.1'}
335+
r = requests.get(fullendpoint, headers=headers)
333336

334337

335338
self.more_results = False
@@ -346,8 +349,8 @@ class ET_PostRest(ET_Constructor):
346349
def __init__(self, auth_stub, endpoint, payload):
347350
auth_stub.refresh_token()
348351

349-
headers = {'content-type' : 'application/json', 'user-agent' : 'FuelSDK-Python'}
350-
r = requests.post(endpoint + '?access_token=' + auth_stub.authToken , data=json.dumps(payload), headers=headers)
352+
headers = {'content-type' : 'application/json', 'user-agent' : 'FuelSDK-Python-v1.1.1', 'authorization' : 'Bearer ' + auth_stub.authToken}
353+
r = requests.post(endpoint, data=json.dumps(payload), headers=headers)
351354

352355
obj = super(ET_PostRest, self).__init__(r, True)
353356
return obj
@@ -361,8 +364,8 @@ class ET_PatchRest(ET_Constructor):
361364
def __init__(self, auth_stub, endpoint, payload):
362365
auth_stub.refresh_token()
363366

364-
headers = {'content-type' : 'application/json', 'user-agent' : 'FuelSDK-Python'}
365-
r = requests.patch(endpoint + '?access_token=' + auth_stub.authToken , data=json.dumps(payload), headers=headers)
367+
headers = {'content-type' : 'application/json', 'user-agent' : 'FuelSDK-Python-v1.1.1', 'authorization' : 'Bearer ' + auth_stub.authToken}
368+
r = requests.patch(endpoint , data=json.dumps(payload), headers=headers)
366369

367370
obj = super(ET_PatchRest, self).__init__(r, True)
368371
return obj
@@ -375,8 +378,9 @@ def __init__(self, auth_stub, endpoint, payload):
375378
class ET_DeleteRest(ET_Constructor):
376379
def __init__(self, auth_stub, endpoint):
377380
auth_stub.refresh_token()
378-
379-
r = requests.delete(endpoint + '?access_token=' + auth_stub.authToken)
381+
382+
headers = {'authorization' : 'Bearer ' + auth_stub.authToken, 'user-agent' : 'FuelSDK-Python-v1.1.1'}
383+
r = requests.delete(endpoint, headers=headers)
380384

381385
obj = super(ET_DeleteRest, self).__init__(r, True)
382386
return obj
@@ -429,8 +433,8 @@ def __init__(self):
429433
def get(self, props = None):
430434
if props is not None and type(props) is dict:
431435
self.props = props
432-
433-
completeURL = self.endpoint
436+
437+
completeURL = self.auth_stub.base_api_url + self.path
434438
additionalQS = {}
435439

436440
if self.props is not None and type(self.props) is dict:
@@ -497,15 +501,15 @@ def getMoreResults(self):
497501
##
498502
########
499503
class ET_CUDSupportRest(ET_GetSupportRest):
500-
endpoint = None
504+
path = None
501505
urlProps = None
502506
urlPropsRequired = None
503507

504508
def __init__(self):
505509
super
506510

507511
def post(self):
508-
completeURL = self.endpoint
512+
completeURL = self.auth_stub.base_api_url + self.path
509513

510514
if self.props is not None and type(self.props) is dict:
511515
for k, v in self.props.items():
@@ -524,7 +528,7 @@ def post(self):
524528
return obj
525529

526530
def patch(self):
527-
completeURL = self.endpoint
531+
completeURL = self.auth_stub.base_api_url + self.path
528532
# All URL Props are required when doing Patch
529533
for value in self.urlProps:
530534
if self.props is None or value not in self.props:
@@ -539,7 +543,7 @@ def patch(self):
539543
return obj
540544

541545
def delete(self):
542-
completeURL = self.endpoint
546+
completeURL = self.auth_stub.base_api_url + self.path
543547
# All URL Props are required when doing Patch
544548
for value in self.urlProps:
545549
if self.props is None or value not in self.props:

README.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
# FuelSDK-Python v1.0.1
1+
# FuelSDK-Python v1.1.1
22

33
Salesforce Marketing Cloud Fuel SDK for Python
44

55
## Overview
66

77
The Fuel SDK for Python provides easy access to Salesforce Marketing Cloud's Fuel API Family services, including a collection of REST APIs and a SOAP API. These APIs provide access to Salesforce Marketing Cloud functionality via common collection types such as array/hash.
88

9+
New Features in Version 1.1.1
10+
------------
11+
* Added support for your tenant’s endpoints - [More Details](https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/your-subdomain-tenant-specific-endpoints.htm)
12+
913
## Installation
1014

1115
The Fuel SDK for python can be easily installed from the [Python Package Index](https://pypi.python.org/pypi) using the [pip](https://pip.readthedocs.org) command. Pip is a tool for installing and managing Python packages.
@@ -26,12 +30,16 @@ You must configure your access tokens and details for the Fuel SDK in one of the
2630
* `FUELSDK_CLIENT_SECRET` (required)
2731
* `FUELSDK_APP_SIGNATURE`
2832
* `FUELSDK_DEFAULT_WSDL`
33+
* `FUELSDK_BASE_API_URL`
2934
* `FUELSDK_AUTH_URL`
35+
* `FUELSDK_SOAP_ENDPOINT`
3036
* `FUELSDK_WSDL_FILE_LOCAL_LOC`
3137

3238
Edit `config.python` or declare environment variables so you can input the ClientID and Client Secret values provided when you registered your application. If you are building a HubExchange application for the Interactive Marketing Hub then, you must also provide the Application Signature (`appsignature` / `FUELSDK_APP_SIGNATURE`).
3339
The `defaultwsdl` / `FUELSDK_DEFAULT_WSDL` configuration must be [changed depending on the Salesforce marketing cloud service](https://code.exacttarget.com/question/there-any-cetificrate-install-our-server-access-et-api "Salesforce Marketing Cloud Forum").
34-
The `authenticationurl` / `FUELSDK_AUTH_URL` must also be [changed depending on service](https://code.exacttarget.com/question/not-able-create-accesstoken-when-clientidsecret-associated-preproduction-account "Salesforce Marketing Cloud Forum").
40+
The `baseapiurl` / `FUELSDK_BASE_API_URL` refers to the hostname where the API is hosted, if omitted it will default to [https://www.exacttargetapis.com](https://www.exacttargetapis.com).
41+
The `authenticationurl` / `FUELSDK_AUTH_URL` must also be [changed depending on service](https://code.exacttarget.com/question/not-able-create-accesstoken-when-clientidsecret-associated-preproduction-account "Salesforce Marketing Cloud Forum"). If omitted it will default to [https://auth.exacttargetapis.com/v1/requestToken?legacy=1](https://auth.exacttargetapis.com/v1/requestToken?legacy=1).
42+
The `soapendpoint` / `FUELSDK_SOAP_ENDPOINT` refers to the endpoint that will be used for doing SOAP calls. If omitted it will default to [https://webservice.exacttarget.com/Service.asmx](https://webservice.exacttarget.com/Service.asmx).
3543
The `wsdl_file_local_loc` / `FUELSDK_WSDL_FILE_LOCAL_LOC` allows you to specify the full path/filename where the WSDL file will be located on disk, if for instance you are connecting to different endpoints from the same server.
3644

3745
If you have not registered your application or you need to lookup your Application Key or Application Signature values, please go to App Center at [Code@: Salesforce Marketing Cloud's Developer Community](https://developer.salesforce.com/docs/?filter_text=&service=Marketing%20Cloud "Code@ App Center").

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
readme = f.read()
55

66
setup(
7-
version='1.0.1',
7+
version='1.1.1',
88
name='Salesforce-FuelSDK',
99
description='Salesforce Marketing Cloud Fuel SDK for Python',
1010
long_description=readme,

0 commit comments

Comments
 (0)