pyxecm 1.6__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyxecm might be problematic. Click here for more details.
- pyxecm/__init__.py +6 -4
- pyxecm/avts.py +673 -246
- pyxecm/coreshare.py +686 -467
- pyxecm/customizer/__init__.py +16 -4
- pyxecm/customizer/__main__.py +58 -0
- pyxecm/customizer/api/__init__.py +5 -0
- pyxecm/customizer/api/__main__.py +6 -0
- pyxecm/customizer/api/app.py +914 -0
- pyxecm/customizer/api/auth.py +154 -0
- pyxecm/customizer/api/metrics.py +92 -0
- pyxecm/customizer/api/models.py +13 -0
- pyxecm/customizer/api/payload_list.py +865 -0
- pyxecm/customizer/api/settings.py +103 -0
- pyxecm/customizer/browser_automation.py +332 -139
- pyxecm/customizer/customizer.py +1007 -1130
- pyxecm/customizer/exceptions.py +35 -0
- pyxecm/customizer/guidewire.py +322 -0
- pyxecm/customizer/k8s.py +713 -378
- pyxecm/customizer/log.py +107 -0
- pyxecm/customizer/m365.py +2867 -909
- pyxecm/customizer/nhc.py +1169 -0
- pyxecm/customizer/openapi.py +258 -0
- pyxecm/customizer/payload.py +16817 -7467
- pyxecm/customizer/pht.py +699 -285
- pyxecm/customizer/salesforce.py +516 -342
- pyxecm/customizer/sap.py +58 -41
- pyxecm/customizer/servicenow.py +593 -371
- pyxecm/customizer/settings.py +442 -0
- pyxecm/customizer/successfactors.py +408 -346
- pyxecm/customizer/translate.py +83 -48
- pyxecm/helper/__init__.py +5 -2
- pyxecm/helper/assoc.py +83 -43
- pyxecm/helper/data.py +2406 -870
- pyxecm/helper/logadapter.py +27 -0
- pyxecm/helper/web.py +229 -101
- pyxecm/helper/xml.py +527 -171
- pyxecm/maintenance_page/__init__.py +5 -0
- pyxecm/maintenance_page/__main__.py +6 -0
- pyxecm/maintenance_page/app.py +51 -0
- pyxecm/maintenance_page/settings.py +28 -0
- pyxecm/maintenance_page/static/favicon.avif +0 -0
- pyxecm/maintenance_page/templates/maintenance.html +165 -0
- pyxecm/otac.py +234 -140
- pyxecm/otawp.py +1436 -557
- pyxecm/otcs.py +7716 -3161
- pyxecm/otds.py +2150 -919
- pyxecm/otiv.py +36 -21
- pyxecm/otmm.py +1272 -325
- pyxecm/otpd.py +231 -127
- pyxecm-2.0.0.dist-info/METADATA +145 -0
- pyxecm-2.0.0.dist-info/RECORD +54 -0
- {pyxecm-1.6.dist-info → pyxecm-2.0.0.dist-info}/WHEEL +1 -1
- pyxecm-1.6.dist-info/METADATA +0 -53
- pyxecm-1.6.dist-info/RECORD +0 -32
- {pyxecm-1.6.dist-info → pyxecm-2.0.0.dist-info/licenses}/LICENSE +0 -0
- {pyxecm-1.6.dist-info → pyxecm-2.0.0.dist-info}/top_level.txt +0 -0
pyxecm/customizer/salesforce.py
CHANGED
|
@@ -1,71 +1,23 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Salesforce Module to interact with the Salesforce API
|
|
3
|
-
See: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest.htm
|
|
1
|
+
"""Salesforce Module to interact with the Salesforce API.
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
Methods:
|
|
7
|
-
|
|
8
|
-
__init__ : class initializer
|
|
9
|
-
config : Returns config data set
|
|
10
|
-
credentials: Returns the token data
|
|
11
|
-
|
|
12
|
-
request_header: Returns the request header for Salesforce API calls
|
|
13
|
-
do_request: Call an Salesforce REST API in a safe way
|
|
14
|
-
parse_request_response: Parse the REST API responses and convert
|
|
15
|
-
them to Python dict in a safe way
|
|
16
|
-
exist_result_item: Check if an dict item is in the response
|
|
17
|
-
of the Salesforce API call
|
|
18
|
-
get_result_value: Check if a defined value (based on a key) is in the Salesforce API response
|
|
19
|
-
|
|
20
|
-
authenticate: Authenticates at Salesforce API
|
|
21
|
-
|
|
22
|
-
get_object_id_by_name: Get the ID of a given Salesforce object with a given type and name
|
|
23
|
-
get_object: Get a Salesforce object based on a defined
|
|
24
|
-
field value and return selected result fields.
|
|
25
|
-
add_object: Add object to Salesforce. This is a generic wrapper method
|
|
26
|
-
for the actual add methods.
|
|
27
|
-
|
|
28
|
-
get_group: Get a Salesforce group based on its ID.
|
|
29
|
-
add_group: Add a new Salesforce group.
|
|
30
|
-
update_group: Update a Salesforce group.
|
|
31
|
-
get_group_members: Get Salesforce group members
|
|
32
|
-
add_group_member: Add a user or group to a Salesforce group
|
|
33
|
-
|
|
34
|
-
get_all_user_profiles: Get all user profiles
|
|
35
|
-
get_user_profile_id: Get a user profile ID by profile name
|
|
36
|
-
get_user_id: Get a user ID by user name
|
|
37
|
-
get_user: Get a Salesforce user based on its ID.
|
|
38
|
-
add_user: Add a new Salesforce user.
|
|
39
|
-
update_user: Update a Salesforce user.
|
|
40
|
-
update_user_password: Update the password of a Salesforce user.
|
|
41
|
-
update_user_photo: update the Salesforce user photo.
|
|
42
|
-
|
|
43
|
-
add_account: Add a new Account object to Salesforce.
|
|
44
|
-
add_product: Add a new Product object to Salesforce.
|
|
45
|
-
add_opportunity: Add a new Opportunity object to Salesfoce.
|
|
46
|
-
add_case: Add a new Case object to Salesforce. The case number
|
|
47
|
-
is automatically created and can not be provided.
|
|
48
|
-
add_asset: Add a new Asset object to Salesforce.
|
|
49
|
-
add_contract: Add a new Contract object to Salesforce.
|
|
3
|
+
See: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest.htm
|
|
50
4
|
"""
|
|
51
5
|
|
|
52
6
|
__author__ = "Dr. Marc Diefenbruch"
|
|
53
|
-
__copyright__ = "Copyright 2024, OpenText"
|
|
7
|
+
__copyright__ = "Copyright (C) 2024-2025, OpenText"
|
|
54
8
|
__credits__ = ["Kai-Philip Gatzweiler"]
|
|
55
9
|
__maintainer__ = "Dr. Marc Diefenbruch"
|
|
56
10
|
__email__ = "mdiefenb@opentext.com"
|
|
57
11
|
|
|
58
|
-
import os
|
|
59
12
|
import json
|
|
60
13
|
import logging
|
|
14
|
+
import os
|
|
61
15
|
import time
|
|
62
|
-
|
|
63
|
-
from typing import Optional, Union, Any
|
|
64
|
-
|
|
65
16
|
from http import HTTPStatus
|
|
17
|
+
|
|
66
18
|
import requests
|
|
67
19
|
|
|
68
|
-
|
|
20
|
+
default_logger = logging.getLogger("pyxecm.customizer.salesforce")
|
|
69
21
|
|
|
70
22
|
REQUEST_LOGIN_HEADERS = {
|
|
71
23
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -78,8 +30,11 @@ REQUEST_MAX_RETRIES = 3
|
|
|
78
30
|
|
|
79
31
|
SALESFORCE_API_VERSION = "v60.0"
|
|
80
32
|
|
|
81
|
-
|
|
82
|
-
|
|
33
|
+
|
|
34
|
+
class Salesforce:
|
|
35
|
+
"""Class Salesforce is used to retrieve and automate stettings and objects in Salesforce."""
|
|
36
|
+
|
|
37
|
+
logger: logging.Logger = default_logger
|
|
83
38
|
|
|
84
39
|
_config: dict
|
|
85
40
|
_access_token = None
|
|
@@ -94,21 +49,38 @@ class Salesforce(object):
|
|
|
94
49
|
password: str,
|
|
95
50
|
authorization_url: str = "",
|
|
96
51
|
security_token: str = "",
|
|
97
|
-
|
|
98
|
-
|
|
52
|
+
logger: logging.Logger = default_logger,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Initialize the Salesforce object.
|
|
99
55
|
|
|
100
56
|
Args:
|
|
101
|
-
base_url (str):
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
57
|
+
base_url (str):
|
|
58
|
+
Base URL of the Salesforce tenant.
|
|
59
|
+
authorization_url (str):
|
|
60
|
+
Authorization URL of the Salesforce tenant, typically ending with "/services/oauth2/token".
|
|
61
|
+
client_id (str):
|
|
62
|
+
The Salesforce Client ID.
|
|
63
|
+
client_secret (str):
|
|
64
|
+
The Salesforce Client Secret.
|
|
65
|
+
username (str):
|
|
66
|
+
User name in Saleforce used by the REST API.
|
|
67
|
+
password (str):
|
|
68
|
+
Password of the user used by the REST API.
|
|
69
|
+
authorization_url (str, optional):
|
|
70
|
+
URL for Salesforce login. If not given it will be constructed with default values
|
|
71
|
+
using base_url.
|
|
72
|
+
security_token (str, optional):
|
|
73
|
+
Security token for Salesforce login.
|
|
74
|
+
logger (logging.Logger, optional):
|
|
75
|
+
The logging object to use for all log messages. Defaults to default_logger.
|
|
76
|
+
|
|
110
77
|
"""
|
|
111
78
|
|
|
79
|
+
if logger != default_logger:
|
|
80
|
+
self.logger = logger.getChild("salesforce")
|
|
81
|
+
for logfilter in logger.filters:
|
|
82
|
+
self.logger.addFilter(logfilter)
|
|
83
|
+
|
|
112
84
|
# The instance URL is also returned by the authenticate call
|
|
113
85
|
# but typically it is identical to the base_url.
|
|
114
86
|
self._instance_url = base_url
|
|
@@ -124,38 +96,32 @@ class Salesforce(object):
|
|
|
124
96
|
|
|
125
97
|
# Set the Salesforce URLs and REST API endpoints:
|
|
126
98
|
salesforce_config["baseUrl"] = base_url
|
|
127
|
-
salesforce_config["objectUrl"] = salesforce_config[
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
salesforce_config["queryUrl"] = salesforce_config[
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
salesforce_config["compositeUrl"] = salesforce_config[
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
salesforce_config["connectUrl"] = salesforce_config[
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
salesforce_config["toolingUrl"] = salesforce_config[
|
|
140
|
-
|
|
141
|
-
|
|
99
|
+
salesforce_config["objectUrl"] = salesforce_config["baseUrl"] + "/services/data/{}/sobjects/".format(
|
|
100
|
+
SALESFORCE_API_VERSION,
|
|
101
|
+
)
|
|
102
|
+
salesforce_config["queryUrl"] = salesforce_config["baseUrl"] + "/services/data/{}/query/".format(
|
|
103
|
+
SALESFORCE_API_VERSION,
|
|
104
|
+
)
|
|
105
|
+
salesforce_config["compositeUrl"] = salesforce_config["baseUrl"] + "/services/data/{}/composite/".format(
|
|
106
|
+
SALESFORCE_API_VERSION,
|
|
107
|
+
)
|
|
108
|
+
salesforce_config["connectUrl"] = salesforce_config["baseUrl"] + "/services/data/{}/connect/".format(
|
|
109
|
+
SALESFORCE_API_VERSION,
|
|
110
|
+
)
|
|
111
|
+
salesforce_config["toolingUrl"] = salesforce_config["baseUrl"] + "/services/data/{}/tooling/".format(
|
|
112
|
+
SALESFORCE_API_VERSION,
|
|
113
|
+
)
|
|
142
114
|
if authorization_url:
|
|
143
115
|
salesforce_config["authenticationUrl"] = authorization_url
|
|
144
116
|
else:
|
|
145
|
-
salesforce_config["authenticationUrl"] =
|
|
146
|
-
salesforce_config["baseUrl"] + "/services/oauth2/token"
|
|
147
|
-
)
|
|
117
|
+
salesforce_config["authenticationUrl"] = salesforce_config["baseUrl"] + "/services/oauth2/token"
|
|
148
118
|
# URLs that are based on the objectURL (sobjects/):
|
|
149
119
|
salesforce_config["userUrl"] = salesforce_config["objectUrl"] + "User/"
|
|
150
120
|
salesforce_config["groupUrl"] = salesforce_config["objectUrl"] + "Group/"
|
|
151
|
-
salesforce_config["groupMemberUrl"] =
|
|
152
|
-
salesforce_config["objectUrl"] + "GroupMember/"
|
|
153
|
-
)
|
|
121
|
+
salesforce_config["groupMemberUrl"] = salesforce_config["objectUrl"] + "GroupMember/"
|
|
154
122
|
salesforce_config["accountUrl"] = salesforce_config["objectUrl"] + "Account/"
|
|
155
123
|
salesforce_config["productUrl"] = salesforce_config["objectUrl"] + "Product2/"
|
|
156
|
-
salesforce_config["opportunityUrl"] =
|
|
157
|
-
salesforce_config["objectUrl"] + "Opportunity/"
|
|
158
|
-
)
|
|
124
|
+
salesforce_config["opportunityUrl"] = salesforce_config["objectUrl"] + "Opportunity/"
|
|
159
125
|
salesforce_config["caseUrl"] = salesforce_config["objectUrl"] + "Case/"
|
|
160
126
|
salesforce_config["assetUrl"] = salesforce_config["objectUrl"] + "Asset/"
|
|
161
127
|
salesforce_config["contractUrl"] = salesforce_config["objectUrl"] + "Contract/"
|
|
@@ -174,33 +140,44 @@ class Salesforce(object):
|
|
|
174
140
|
# end method definition
|
|
175
141
|
|
|
176
142
|
def config(self) -> dict:
|
|
177
|
-
"""
|
|
143
|
+
"""Return the configuration dictionary.
|
|
178
144
|
|
|
179
145
|
Returns:
|
|
180
|
-
dict:
|
|
146
|
+
dict:
|
|
147
|
+
The configuration dictionary.
|
|
148
|
+
|
|
181
149
|
"""
|
|
150
|
+
|
|
182
151
|
return self._config
|
|
183
152
|
|
|
184
153
|
# end method definition
|
|
185
154
|
|
|
186
155
|
def credentials(self) -> dict:
|
|
187
|
-
"""Return the login credentials
|
|
156
|
+
"""Return the login credentials.
|
|
188
157
|
|
|
189
158
|
Returns:
|
|
190
|
-
dict:
|
|
159
|
+
dict:
|
|
160
|
+
The dictionary with login credentials for Salesforce.
|
|
161
|
+
|
|
191
162
|
"""
|
|
163
|
+
|
|
192
164
|
return self.config()["authenticationData"]
|
|
193
165
|
|
|
194
166
|
# end method definition
|
|
195
167
|
|
|
196
168
|
def request_header(self, content_type: str = "application/json") -> dict:
|
|
197
|
-
"""
|
|
198
|
-
|
|
169
|
+
"""Return the request header used for Application calls.
|
|
170
|
+
|
|
171
|
+
Consists of Bearer access token and Content Type
|
|
199
172
|
|
|
200
173
|
Args:
|
|
201
|
-
content_type (str, optional):
|
|
202
|
-
|
|
203
|
-
|
|
174
|
+
content_type (str, optional):
|
|
175
|
+
Content type for the request. Default is "pplication/json".
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
dict:
|
|
179
|
+
The equest header values
|
|
180
|
+
|
|
204
181
|
"""
|
|
205
182
|
|
|
206
183
|
request_header = {
|
|
@@ -234,36 +211,64 @@ class Salesforce(object):
|
|
|
234
211
|
stream: bool = False,
|
|
235
212
|
verify: bool = True,
|
|
236
213
|
) -> dict | None:
|
|
237
|
-
"""Call an Salesforce REST API in a safe way
|
|
214
|
+
"""Call an Salesforce REST API in a safe way.
|
|
238
215
|
|
|
239
216
|
Args:
|
|
240
|
-
url (str):
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
217
|
+
url (str):
|
|
218
|
+
The URL to send the request to.
|
|
219
|
+
method (str, optional):
|
|
220
|
+
HTTP method (GET, POST, etc.). Defaults to "GET".
|
|
221
|
+
headers (dict | None, optional):
|
|
222
|
+
Request Headers. Defaults to None.
|
|
223
|
+
data (dict | None, optional):
|
|
224
|
+
Request payload. Defaults to None
|
|
225
|
+
json_data (dict | None, optional):
|
|
226
|
+
Request payload. Defaults to None.
|
|
227
|
+
files (dict | None, optional):
|
|
228
|
+
Dictionary of {"name": file-tuple} for multipart encoding upload.
|
|
229
|
+
The file-tuple can be a 2-tuple ("filename", fileobj) or a 3-tuple ("filename", fileobj, "content_type")
|
|
230
|
+
params (dict | None, optional):
|
|
231
|
+
Add key-value pairs to the query string of the URL.
|
|
232
|
+
When you use the params parameter, requests automatically appends
|
|
233
|
+
the key-value pairs to the URL as part of the query string
|
|
234
|
+
timeout (int | None, optional):
|
|
235
|
+
Timeout for the request in seconds. Defaults to REQUEST_TIMEOUT.
|
|
236
|
+
show_error (bool, optional):
|
|
237
|
+
Whether or not an error should be logged in case of a failed REST call.
|
|
238
|
+
If False, then only a warning is logged. Defaults to True.
|
|
239
|
+
show_warning (bool, optional):
|
|
240
|
+
Whether or not an warning should be logged in case of a failed REST call.
|
|
241
|
+
If False, then only a warning is logged. Defaults to True.
|
|
242
|
+
warning_message (str, optional):
|
|
243
|
+
Specific warning message. Defaults to "".
|
|
244
|
+
If not given the error_message will be used.
|
|
245
|
+
failure_message (str, optional):
|
|
246
|
+
Specific error message. Defaults to "".
|
|
247
|
+
success_message (str, optional):
|
|
248
|
+
Specific success message. Defaults to "".
|
|
249
|
+
max_retries (int, optional):
|
|
250
|
+
How many retries on Connection errors? Default is REQUEST_MAX_RETRIES.
|
|
251
|
+
retry_forever (bool, optional):
|
|
252
|
+
Eventually wait forever - without timeout. Defaults to False.
|
|
253
|
+
parse_request_response (bool, optional):
|
|
254
|
+
Should the response.text be interpreted as json and loaded into a dictionary.
|
|
255
|
+
True is the default.
|
|
256
|
+
stream (bool, optional):
|
|
257
|
+
Control whether the response content should be immediately downloaded or streamed incrementally.
|
|
258
|
+
verify (bool, optional):
|
|
259
|
+
Specify whether or not SSL certificates should be verified when making an HTTPS request.
|
|
260
|
+
Default = True
|
|
260
261
|
|
|
261
262
|
Returns:
|
|
262
|
-
dict | None:
|
|
263
|
+
dict | None:
|
|
264
|
+
Response of OTDS REST API or None in case of an error.
|
|
265
|
+
|
|
263
266
|
"""
|
|
264
267
|
|
|
265
268
|
if headers is None:
|
|
266
|
-
logger.error(
|
|
269
|
+
self.logger.error(
|
|
270
|
+
"Missing request header. Cannot send request to Core Share!",
|
|
271
|
+
)
|
|
267
272
|
return None
|
|
268
273
|
|
|
269
274
|
# In case of an expired session we reauthenticate and
|
|
@@ -288,19 +293,19 @@ class Salesforce(object):
|
|
|
288
293
|
|
|
289
294
|
if response.ok:
|
|
290
295
|
if success_message:
|
|
291
|
-
logger.info(success_message)
|
|
296
|
+
self.logger.info(success_message)
|
|
292
297
|
if parse_request_response:
|
|
293
298
|
return self.parse_request_response(response)
|
|
294
299
|
else:
|
|
295
300
|
return response
|
|
296
301
|
# Check if Session has expired - then re-authenticate and try once more
|
|
297
302
|
elif response.status_code == 401 and retries == 0:
|
|
298
|
-
logger.debug("Session has expired - try to re-authenticate...")
|
|
303
|
+
self.logger.debug("Session has expired - try to re-authenticate...")
|
|
299
304
|
self.authenticate(revalidate=True)
|
|
300
305
|
# Make sure to not change an existing content type
|
|
301
306
|
# the do_request() method is called with:
|
|
302
307
|
headers = self.request_header(
|
|
303
|
-
content_type=headers.get("Content-Type", None)
|
|
308
|
+
content_type=headers.get("Content-Type", None),
|
|
304
309
|
)
|
|
305
310
|
retries += 1
|
|
306
311
|
else:
|
|
@@ -312,7 +317,7 @@ class Salesforce(object):
|
|
|
312
317
|
response_text = response.text
|
|
313
318
|
|
|
314
319
|
if show_error:
|
|
315
|
-
logger.error(
|
|
320
|
+
self.logger.error(
|
|
316
321
|
"%s; status -> %s/%s; error -> %s",
|
|
317
322
|
failure_message,
|
|
318
323
|
response.status_code,
|
|
@@ -320,7 +325,7 @@ class Salesforce(object):
|
|
|
320
325
|
response_text,
|
|
321
326
|
)
|
|
322
327
|
elif show_warning:
|
|
323
|
-
logger.warning(
|
|
328
|
+
self.logger.warning(
|
|
324
329
|
"%s; status -> %s/%s; warning -> %s",
|
|
325
330
|
warning_message if warning_message else failure_message,
|
|
326
331
|
response.status_code,
|
|
@@ -328,7 +333,7 @@ class Salesforce(object):
|
|
|
328
333
|
response_text,
|
|
329
334
|
)
|
|
330
335
|
if content_type == "text/html":
|
|
331
|
-
logger.debug(
|
|
336
|
+
self.logger.debug(
|
|
332
337
|
"%s; status -> %s/%s; warning -> %s",
|
|
333
338
|
failure_message,
|
|
334
339
|
response.status_code,
|
|
@@ -338,45 +343,45 @@ class Salesforce(object):
|
|
|
338
343
|
return None
|
|
339
344
|
except requests.exceptions.Timeout:
|
|
340
345
|
if retries <= max_retries:
|
|
341
|
-
logger.warning(
|
|
346
|
+
self.logger.warning(
|
|
342
347
|
"Request timed out. Retrying in %s seconds...",
|
|
343
348
|
str(REQUEST_RETRY_DELAY),
|
|
344
349
|
)
|
|
345
350
|
retries += 1
|
|
346
351
|
time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
|
|
347
352
|
else:
|
|
348
|
-
logger.error(
|
|
349
|
-
"%s; timeout error",
|
|
353
|
+
self.logger.error(
|
|
354
|
+
"%s; timeout error.",
|
|
350
355
|
failure_message,
|
|
351
356
|
)
|
|
352
357
|
if retry_forever:
|
|
353
358
|
# If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
|
|
354
|
-
logger.warning("Turn timeouts off and wait forever...")
|
|
359
|
+
self.logger.warning("Turn timeouts off and wait forever...")
|
|
355
360
|
timeout = None
|
|
356
361
|
else:
|
|
357
362
|
return None
|
|
358
363
|
except requests.exceptions.ConnectionError:
|
|
359
364
|
if retries <= max_retries:
|
|
360
|
-
logger.warning(
|
|
365
|
+
self.logger.warning(
|
|
361
366
|
"Connection error. Retrying in %s seconds...",
|
|
362
367
|
str(REQUEST_RETRY_DELAY),
|
|
363
368
|
)
|
|
364
369
|
retries += 1
|
|
365
370
|
time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
|
|
366
371
|
else:
|
|
367
|
-
logger.error(
|
|
368
|
-
"%s; connection error",
|
|
372
|
+
self.logger.error(
|
|
373
|
+
"%s; connection error.",
|
|
369
374
|
failure_message,
|
|
370
375
|
)
|
|
371
376
|
if retry_forever:
|
|
372
377
|
# If it fails after REQUEST_MAX_RETRIES retries we let it wait forever
|
|
373
|
-
logger.warning("Turn timeouts off and wait forever...")
|
|
378
|
+
self.logger.warning("Turn timeouts off and wait forever...")
|
|
374
379
|
timeout = None
|
|
375
380
|
time.sleep(REQUEST_RETRY_DELAY) # Add a delay before retrying
|
|
376
381
|
else:
|
|
377
382
|
return None
|
|
378
383
|
# end try
|
|
379
|
-
logger.debug(
|
|
384
|
+
self.logger.debug(
|
|
380
385
|
"Retrying REST API %s call -> %s... (retry = %s)",
|
|
381
386
|
method,
|
|
382
387
|
url,
|
|
@@ -392,43 +397,47 @@ class Salesforce(object):
|
|
|
392
397
|
additional_error_message: str = "",
|
|
393
398
|
show_error: bool = True,
|
|
394
399
|
) -> dict | None:
|
|
395
|
-
"""
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
+
"""Convert the request response (JSon) to a Python dict in a safe way.
|
|
401
|
+
|
|
402
|
+
This includes handling exceptions.
|
|
403
|
+
|
|
404
|
+
It first tries to load the response.text via json.loads() that produces
|
|
405
|
+
a dict output. Only if response.text is not set or is empty it just converts
|
|
406
|
+
the response_object to a dict using the vars() built-in method.
|
|
400
407
|
|
|
401
408
|
Args:
|
|
402
|
-
response_object (object):
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
409
|
+
response_object (object):
|
|
410
|
+
This is reponse object delivered by the request call.
|
|
411
|
+
additional_error_message (str, optional):
|
|
412
|
+
Provide a a more specific error message that is logged in case of an error.
|
|
413
|
+
show_error (bool):
|
|
414
|
+
If True, write an error to the log file.
|
|
415
|
+
If False, write a warning to the log file.
|
|
416
|
+
|
|
407
417
|
Returns:
|
|
408
|
-
dict: response information or None in case of an error
|
|
418
|
+
dict | None: Parsed response information or None in case of an error.
|
|
419
|
+
|
|
409
420
|
"""
|
|
410
421
|
|
|
411
422
|
if not response_object:
|
|
412
423
|
return None
|
|
413
424
|
|
|
414
425
|
try:
|
|
415
|
-
if response_object.text
|
|
416
|
-
dict_object = json.loads(response_object.text)
|
|
417
|
-
else:
|
|
418
|
-
dict_object = vars(response_object)
|
|
426
|
+
dict_object = json.loads(response_object.text) if response_object.text else vars(response_object)
|
|
419
427
|
except json.JSONDecodeError as exception:
|
|
420
428
|
if additional_error_message:
|
|
421
429
|
message = "Cannot decode response as JSon. {}; error -> {}".format(
|
|
422
|
-
additional_error_message,
|
|
430
|
+
additional_error_message,
|
|
431
|
+
exception,
|
|
423
432
|
)
|
|
424
433
|
else:
|
|
425
434
|
message = "Cannot decode response as JSon; error -> {}".format(
|
|
426
|
-
exception
|
|
435
|
+
exception,
|
|
427
436
|
)
|
|
428
437
|
if show_error:
|
|
429
|
-
logger.error(message)
|
|
438
|
+
self.logger.error(message)
|
|
430
439
|
else:
|
|
431
|
-
logger.warning(message)
|
|
440
|
+
self.logger.warning(message)
|
|
432
441
|
return None
|
|
433
442
|
else:
|
|
434
443
|
return dict_object
|
|
@@ -436,14 +445,20 @@ class Salesforce(object):
|
|
|
436
445
|
# end method definition
|
|
437
446
|
|
|
438
447
|
def exist_result_item(self, response: dict, key: str, value: str) -> bool:
|
|
439
|
-
"""Check existence of key / value pair in the response properties of
|
|
448
|
+
"""Check existence of key / value pair in the response properties of a Salesforce API call.
|
|
440
449
|
|
|
441
450
|
Args:
|
|
442
|
-
response (dict):
|
|
443
|
-
|
|
444
|
-
|
|
451
|
+
response (dict):
|
|
452
|
+
REST response from an Salesforce API call
|
|
453
|
+
key (str):
|
|
454
|
+
The property name (key) of the item to lookup.
|
|
455
|
+
value (str):
|
|
456
|
+
The value to find in the item with the matching key.
|
|
457
|
+
|
|
445
458
|
Returns:
|
|
446
|
-
bool:
|
|
459
|
+
bool:
|
|
460
|
+
True if the value was found, False otherwise.
|
|
461
|
+
|
|
447
462
|
"""
|
|
448
463
|
|
|
449
464
|
if not response:
|
|
@@ -455,10 +470,10 @@ class Salesforce(object):
|
|
|
455
470
|
return False
|
|
456
471
|
|
|
457
472
|
for record in records:
|
|
458
|
-
if value == record[key]:
|
|
473
|
+
if key in record and value == record[key]:
|
|
459
474
|
return True
|
|
460
475
|
else:
|
|
461
|
-
if not
|
|
476
|
+
if key not in response:
|
|
462
477
|
return False
|
|
463
478
|
if value == response[key]:
|
|
464
479
|
return True
|
|
@@ -473,15 +488,22 @@ class Salesforce(object):
|
|
|
473
488
|
key: str,
|
|
474
489
|
index: int = 0,
|
|
475
490
|
) -> str | None:
|
|
476
|
-
"""Get value of a result property with a given key of an Salesforce API call.
|
|
491
|
+
"""Get the value of a result property with a given key of an Salesforce API call.
|
|
477
492
|
|
|
478
493
|
Args:
|
|
479
|
-
response (dict):
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
494
|
+
response (dict):
|
|
495
|
+
REST response from an Salesforce REST Call.
|
|
496
|
+
key (str):
|
|
497
|
+
The property name (key) of the item to lookup.
|
|
498
|
+
index (int, optional):
|
|
499
|
+
Index to use (1st element has index 0).
|
|
500
|
+
Defaults to 0.
|
|
501
|
+
|
|
483
502
|
Returns:
|
|
484
|
-
str
|
|
503
|
+
str | None:
|
|
504
|
+
The value for the key or None in case of an error or if the
|
|
505
|
+
key is not found.
|
|
506
|
+
|
|
485
507
|
"""
|
|
486
508
|
|
|
487
509
|
if not response:
|
|
@@ -495,7 +517,7 @@ class Salesforce(object):
|
|
|
495
517
|
return None
|
|
496
518
|
value = values[index][key]
|
|
497
519
|
else: # simple response - try to find key in response directly:
|
|
498
|
-
if not
|
|
520
|
+
if key not in response:
|
|
499
521
|
return None
|
|
500
522
|
value = response[key]
|
|
501
523
|
|
|
@@ -507,15 +529,20 @@ class Salesforce(object):
|
|
|
507
529
|
"""Authenticate at Salesforce with client ID and client secret.
|
|
508
530
|
|
|
509
531
|
Args:
|
|
510
|
-
revalidate (bool, optional):
|
|
511
|
-
|
|
532
|
+
revalidate (bool, optional):
|
|
533
|
+
Determins if a re-athentication is enforced
|
|
534
|
+
(e.g. if session has timed out with 401 error).
|
|
535
|
+
|
|
512
536
|
Returns:
|
|
513
|
-
str
|
|
537
|
+
str | None:
|
|
538
|
+
The Access token. Also stores access token in self._access_token.
|
|
539
|
+
None in case of error
|
|
540
|
+
|
|
514
541
|
"""
|
|
515
542
|
|
|
516
543
|
# Already authenticated and session still valid?
|
|
517
544
|
if self._access_token and not revalidate:
|
|
518
|
-
logger.debug(
|
|
545
|
+
self.logger.debug(
|
|
519
546
|
"Session still valid - return existing access token -> %s",
|
|
520
547
|
str(self._access_token),
|
|
521
548
|
)
|
|
@@ -524,7 +551,7 @@ class Salesforce(object):
|
|
|
524
551
|
request_url = self.config()["authenticationUrl"]
|
|
525
552
|
request_header = REQUEST_LOGIN_HEADERS
|
|
526
553
|
|
|
527
|
-
logger.debug("Requesting Salesforce Access Token from -> %s", request_url)
|
|
554
|
+
self.logger.debug("Requesting Salesforce Access Token from -> %s", request_url)
|
|
528
555
|
|
|
529
556
|
authenticate_post_body = self.credentials()
|
|
530
557
|
|
|
@@ -540,7 +567,7 @@ class Salesforce(object):
|
|
|
540
567
|
timeout=REQUEST_TIMEOUT,
|
|
541
568
|
)
|
|
542
569
|
except requests.exceptions.ConnectionError as exception:
|
|
543
|
-
logger.warning(
|
|
570
|
+
self.logger.warning(
|
|
544
571
|
"Unable to connect to -> %s : %s",
|
|
545
572
|
self.config()["authenticationUrl"],
|
|
546
573
|
exception,
|
|
@@ -554,11 +581,11 @@ class Salesforce(object):
|
|
|
554
581
|
else:
|
|
555
582
|
# Store authentication access_token:
|
|
556
583
|
self._access_token = authenticate_dict["access_token"]
|
|
557
|
-
logger.debug("Access Token -> %s", self._access_token)
|
|
584
|
+
self.logger.debug("Access Token -> %s", self._access_token)
|
|
558
585
|
self._instance_url = authenticate_dict["instance_url"]
|
|
559
|
-
logger.debug("Instance URL -> %s", self._instance_url)
|
|
586
|
+
self.logger.debug("Instance URL -> %s", self._instance_url)
|
|
560
587
|
else:
|
|
561
|
-
logger.error(
|
|
588
|
+
self.logger.error(
|
|
562
589
|
"Failed to request an Salesforce Access Token; error -> %s",
|
|
563
590
|
response.text,
|
|
564
591
|
)
|
|
@@ -569,8 +596,11 @@ class Salesforce(object):
|
|
|
569
596
|
# end method definition
|
|
570
597
|
|
|
571
598
|
def get_object_id_by_name(
|
|
572
|
-
self,
|
|
573
|
-
|
|
599
|
+
self,
|
|
600
|
+
object_type: str,
|
|
601
|
+
name: str,
|
|
602
|
+
name_field: str = "Name",
|
|
603
|
+
) -> str | None:
|
|
574
604
|
"""Get the ID of a given Salesforce object with a given type and name.
|
|
575
605
|
|
|
576
606
|
Args:
|
|
@@ -580,6 +610,7 @@ class Salesforce(object):
|
|
|
580
610
|
|
|
581
611
|
Returns:
|
|
582
612
|
Optional[str]: Object ID or None if the request fails.
|
|
613
|
+
|
|
583
614
|
"""
|
|
584
615
|
|
|
585
616
|
if not self._access_token or not self._instance_url:
|
|
@@ -597,7 +628,8 @@ class Salesforce(object):
|
|
|
597
628
|
params={"q": query},
|
|
598
629
|
timeout=REQUEST_TIMEOUT,
|
|
599
630
|
failure_message="Failed to get Salesforce object ID for object type -> '{}' and object name -> '{}'".format(
|
|
600
|
-
object_type,
|
|
631
|
+
object_type,
|
|
632
|
+
name,
|
|
601
633
|
),
|
|
602
634
|
)
|
|
603
635
|
if not response:
|
|
@@ -619,16 +651,18 @@ class Salesforce(object):
|
|
|
619
651
|
|
|
620
652
|
Args:
|
|
621
653
|
object_type (str): Salesforce Business Object type. Such as "Account" or "Case".
|
|
622
|
-
search_field (str): object field to search in
|
|
623
|
-
search_value (str): value to search for
|
|
624
|
-
result_fields (list | None):
|
|
625
|
-
|
|
626
|
-
|
|
654
|
+
search_field (str): The object field to search in.
|
|
655
|
+
search_value (str): The value to search for.
|
|
656
|
+
result_fields (list | None):
|
|
657
|
+
List of fields to return. If None, then all standard fields
|
|
658
|
+
of the object will be returned.
|
|
659
|
+
limit (int, optional):
|
|
660
|
+
The maximum number of fields to return. Salesforce enforces 200 as upper limit.
|
|
627
661
|
|
|
628
662
|
Returns:
|
|
629
663
|
dict | None: Dictionary with the Salesforce object data.
|
|
630
664
|
|
|
631
|
-
|
|
665
|
+
Example:
|
|
632
666
|
{
|
|
633
667
|
'totalSize': 2,
|
|
634
668
|
'done': True,
|
|
@@ -649,20 +683,21 @@ class Salesforce(object):
|
|
|
649
683
|
}
|
|
650
684
|
]
|
|
651
685
|
}
|
|
686
|
+
|
|
652
687
|
"""
|
|
653
688
|
|
|
654
689
|
if not self._access_token or not self._instance_url:
|
|
655
690
|
self.authenticate()
|
|
656
691
|
|
|
657
692
|
if search_field and not search_value:
|
|
658
|
-
logger.error(
|
|
693
|
+
self.logger.error(
|
|
659
694
|
"No search value has been provided for search field -> %s!",
|
|
660
695
|
search_field,
|
|
661
696
|
)
|
|
662
697
|
return None
|
|
663
698
|
if not result_fields:
|
|
664
|
-
logger.debug(
|
|
665
|
-
"No result fields defined. Using 'FIELDS(STANDARD)' to deliver all standard fields of the object."
|
|
699
|
+
self.logger.debug(
|
|
700
|
+
"No result fields defined. Using 'FIELDS(STANDARD)' to deliver all standard fields of the object.",
|
|
666
701
|
)
|
|
667
702
|
result_fields = ["FIELDS(STANDARD)"]
|
|
668
703
|
|
|
@@ -674,8 +709,10 @@ class Salesforce(object):
|
|
|
674
709
|
request_header = self.request_header()
|
|
675
710
|
request_url = self.config()["queryUrl"] + "?q={}".format(query)
|
|
676
711
|
|
|
677
|
-
logger.debug(
|
|
678
|
-
"Sending query -> %s to Salesforce; calling -> %s",
|
|
712
|
+
self.logger.debug(
|
|
713
|
+
"Sending query -> %s to Salesforce; calling -> %s",
|
|
714
|
+
query,
|
|
715
|
+
request_url,
|
|
679
716
|
)
|
|
680
717
|
|
|
681
718
|
return self.do_request(
|
|
@@ -684,22 +721,29 @@ class Salesforce(object):
|
|
|
684
721
|
headers=request_header,
|
|
685
722
|
timeout=REQUEST_TIMEOUT,
|
|
686
723
|
failure_message="Failed to retrieve Salesforce object type -> '{}' with {} = {}".format(
|
|
687
|
-
object_type,
|
|
724
|
+
object_type,
|
|
725
|
+
search_field,
|
|
726
|
+
search_value,
|
|
688
727
|
),
|
|
689
728
|
)
|
|
690
729
|
|
|
691
730
|
# end method definition
|
|
692
731
|
|
|
693
|
-
def add_object(self, object_type: str, **kwargs:
|
|
694
|
-
"""Add object to Salesforce.
|
|
695
|
-
|
|
732
|
+
def add_object(self, object_type: str, **kwargs: dict[str, str]) -> dict | None:
|
|
733
|
+
"""Add object to Salesforce.
|
|
734
|
+
|
|
735
|
+
This is a generic wrapper method for the actual add methods.
|
|
696
736
|
|
|
697
737
|
Args:
|
|
698
|
-
object_type (str):
|
|
699
|
-
|
|
738
|
+
object_type (str):
|
|
739
|
+
Type of the Salesforce business object, like "Account" or "Case".
|
|
740
|
+
**kwargs (dict):
|
|
741
|
+
This is a keyword / value dictionary with additional parameters that depend
|
|
742
|
+
on the object type.
|
|
700
743
|
|
|
701
744
|
Returns:
|
|
702
745
|
dict | None: Dictionary with the Salesforce object data or None if the request fails.
|
|
746
|
+
|
|
703
747
|
"""
|
|
704
748
|
|
|
705
749
|
match object_type:
|
|
@@ -767,25 +811,30 @@ class Salesforce(object):
|
|
|
767
811
|
**kwargs,
|
|
768
812
|
)
|
|
769
813
|
case _:
|
|
770
|
-
logger.error(
|
|
814
|
+
self.logger.error(
|
|
771
815
|
"Unsupported Salesforce business object -> %s!",
|
|
772
816
|
object_type,
|
|
773
817
|
)
|
|
774
818
|
|
|
819
|
+
return None
|
|
820
|
+
|
|
775
821
|
# end method definition
|
|
776
822
|
|
|
777
|
-
def get_group_id(self,
|
|
823
|
+
def get_group_id(self, group_name: str) -> str | None:
|
|
778
824
|
"""Get a group ID by group name.
|
|
779
825
|
|
|
780
826
|
Args:
|
|
781
|
-
|
|
827
|
+
group_name (str): Name of the Group.
|
|
782
828
|
|
|
783
829
|
Returns:
|
|
784
830
|
Optional[str]: Technical ID of the group
|
|
831
|
+
|
|
785
832
|
"""
|
|
786
833
|
|
|
787
834
|
return self.get_object_id_by_name(
|
|
788
|
-
object_type="Group",
|
|
835
|
+
object_type="Group",
|
|
836
|
+
name=group_name,
|
|
837
|
+
name_field="Name",
|
|
789
838
|
)
|
|
790
839
|
|
|
791
840
|
# end method definition
|
|
@@ -794,10 +843,13 @@ class Salesforce(object):
|
|
|
794
843
|
"""Get a Salesforce group based on its ID.
|
|
795
844
|
|
|
796
845
|
Args:
|
|
797
|
-
group_id (str):
|
|
846
|
+
group_id (str):
|
|
847
|
+
ID of the Salesforce group to retrieve.
|
|
798
848
|
|
|
799
849
|
Returns:
|
|
800
|
-
dict | None:
|
|
850
|
+
dict | None:
|
|
851
|
+
Dictionary with the Salesforce group data or None if the request fails.
|
|
852
|
+
|
|
801
853
|
"""
|
|
802
854
|
|
|
803
855
|
if not self._access_token or not self._instance_url:
|
|
@@ -806,8 +858,10 @@ class Salesforce(object):
|
|
|
806
858
|
request_header = self.request_header()
|
|
807
859
|
request_url = self.config()["groupUrl"] + group_id
|
|
808
860
|
|
|
809
|
-
logger.debug(
|
|
810
|
-
"Get Salesforce group with ID -> %s; calling -> %s",
|
|
861
|
+
self.logger.debug(
|
|
862
|
+
"Get Salesforce group with ID -> %s; calling -> %s",
|
|
863
|
+
group_id,
|
|
864
|
+
request_url,
|
|
811
865
|
)
|
|
812
866
|
|
|
813
867
|
return self.do_request(
|
|
@@ -816,7 +870,7 @@ class Salesforce(object):
|
|
|
816
870
|
headers=request_header,
|
|
817
871
|
timeout=REQUEST_TIMEOUT,
|
|
818
872
|
failure_message="Failed to get Salesforce group with ID -> {}".format(
|
|
819
|
-
group_id
|
|
873
|
+
group_id,
|
|
820
874
|
),
|
|
821
875
|
)
|
|
822
876
|
|
|
@@ -831,16 +885,18 @@ class Salesforce(object):
|
|
|
831
885
|
|
|
832
886
|
Args:
|
|
833
887
|
group_name (str): Name of the new Salesforce group
|
|
888
|
+
group_type (str, optional): Type of the group. Default is "Regular".
|
|
834
889
|
|
|
835
890
|
Returns:
|
|
836
891
|
dict | None: Dictionary with the Salesforce Group data or None if the request fails.
|
|
837
892
|
|
|
838
|
-
|
|
893
|
+
Example:
|
|
839
894
|
{
|
|
840
895
|
'id': '00GDn000000KWE0MAO',
|
|
841
896
|
'success': True,
|
|
842
897
|
'errors': []
|
|
843
898
|
}
|
|
899
|
+
|
|
844
900
|
"""
|
|
845
901
|
|
|
846
902
|
if not self._access_token or not self._instance_url:
|
|
@@ -851,8 +907,10 @@ class Salesforce(object):
|
|
|
851
907
|
|
|
852
908
|
payload = {"Name": group_name, "Type": group_type}
|
|
853
909
|
|
|
854
|
-
logger.debug(
|
|
855
|
-
"Adding Salesforce group -> %s; calling -> %s",
|
|
910
|
+
self.logger.debug(
|
|
911
|
+
"Adding Salesforce group -> %s; calling -> %s",
|
|
912
|
+
group_name,
|
|
913
|
+
request_url,
|
|
856
914
|
)
|
|
857
915
|
|
|
858
916
|
return self.do_request(
|
|
@@ -878,7 +936,8 @@ class Salesforce(object):
|
|
|
878
936
|
update_data (dict): Dictionary containing the fields to update.
|
|
879
937
|
|
|
880
938
|
Returns:
|
|
881
|
-
dict: Response from the Salesforce API.
|
|
939
|
+
dict | None: Response from the Salesforce API. None in case of an error.
|
|
940
|
+
|
|
882
941
|
"""
|
|
883
942
|
|
|
884
943
|
if not self._access_token or not self._instance_url:
|
|
@@ -888,7 +947,7 @@ class Salesforce(object):
|
|
|
888
947
|
|
|
889
948
|
request_url = self.config()["groupUrl"] + group_id
|
|
890
949
|
|
|
891
|
-
logger.debug(
|
|
950
|
+
self.logger.debug(
|
|
892
951
|
"Update Salesforce group with ID -> %s; calling -> %s",
|
|
893
952
|
group_id,
|
|
894
953
|
request_url,
|
|
@@ -901,22 +960,24 @@ class Salesforce(object):
|
|
|
901
960
|
json_data=update_data,
|
|
902
961
|
timeout=REQUEST_TIMEOUT,
|
|
903
962
|
failure_message="Failed to update Salesforce group with ID -> {}".format(
|
|
904
|
-
group_id
|
|
963
|
+
group_id,
|
|
905
964
|
),
|
|
906
965
|
)
|
|
907
966
|
|
|
908
967
|
# end method definition
|
|
909
968
|
|
|
910
969
|
def get_group_members(self, group_id: str) -> list | None:
|
|
911
|
-
"""Get Salesforce group members
|
|
970
|
+
"""Get Salesforce group members.
|
|
912
971
|
|
|
913
972
|
Args:
|
|
914
|
-
group_id (str):
|
|
973
|
+
group_id (str):
|
|
974
|
+
The ID of the group to retrieve the members.
|
|
915
975
|
|
|
916
976
|
Returns:
|
|
917
|
-
list | None:
|
|
977
|
+
list | None:
|
|
978
|
+
The group members.
|
|
918
979
|
|
|
919
|
-
|
|
980
|
+
Example:
|
|
920
981
|
{
|
|
921
982
|
'totalSize': 1,
|
|
922
983
|
'done': True,
|
|
@@ -930,6 +991,7 @@ class Salesforce(object):
|
|
|
930
991
|
}
|
|
931
992
|
]
|
|
932
993
|
}
|
|
994
|
+
|
|
933
995
|
"""
|
|
934
996
|
|
|
935
997
|
if not self._access_token or not self._instance_url:
|
|
@@ -942,7 +1004,7 @@ class Salesforce(object):
|
|
|
942
1004
|
query = f"SELECT UserOrGroupId FROM GroupMember WHERE GroupId = '{group_id}'"
|
|
943
1005
|
params = {"q": query}
|
|
944
1006
|
|
|
945
|
-
logger.debug(
|
|
1007
|
+
self.logger.debug(
|
|
946
1008
|
"Get members of Salesforce group with ID -> %s; calling -> %s",
|
|
947
1009
|
group_id,
|
|
948
1010
|
request_url,
|
|
@@ -955,28 +1017,32 @@ class Salesforce(object):
|
|
|
955
1017
|
params=params,
|
|
956
1018
|
timeout=REQUEST_TIMEOUT,
|
|
957
1019
|
failure_message="Failed to get members of Salesforce group with ID -> {}".format(
|
|
958
|
-
group_id
|
|
1020
|
+
group_id,
|
|
959
1021
|
),
|
|
960
1022
|
)
|
|
961
1023
|
|
|
962
1024
|
# end method definition
|
|
963
1025
|
|
|
964
1026
|
def add_group_member(self, group_id: str, member_id: str) -> dict | None:
|
|
965
|
-
"""Add a user or group to a Salesforce group
|
|
1027
|
+
"""Add a user or group to a Salesforce group.
|
|
966
1028
|
|
|
967
1029
|
Args:
|
|
968
|
-
group_id (str):
|
|
969
|
-
|
|
1030
|
+
group_id (str):
|
|
1031
|
+
The ID of the Salesforce Group to add member to.
|
|
1032
|
+
member_id (str):
|
|
1033
|
+
The ID of the user or group.
|
|
970
1034
|
|
|
971
1035
|
Returns:
|
|
972
|
-
dict | None:
|
|
1036
|
+
dict | None:
|
|
1037
|
+
Dictionary with the Salesforce membership data or None if the request fails.
|
|
973
1038
|
|
|
974
|
-
|
|
1039
|
+
Example response (id is the membership ID):
|
|
975
1040
|
{
|
|
976
1041
|
'id': '011Dn000000ELhwIAG',
|
|
977
1042
|
'success': True,
|
|
978
1043
|
'errors': []
|
|
979
1044
|
}
|
|
1045
|
+
|
|
980
1046
|
"""
|
|
981
1047
|
|
|
982
1048
|
if not self._access_token or not self._instance_url:
|
|
@@ -988,7 +1054,7 @@ class Salesforce(object):
|
|
|
988
1054
|
|
|
989
1055
|
payload = {"GroupId": group_id, "UserOrGroupId": member_id}
|
|
990
1056
|
|
|
991
|
-
logger.debug(
|
|
1057
|
+
self.logger.debug(
|
|
992
1058
|
"Add member with ID -> %s to Salesforce group with ID -> %s; calling -> %s",
|
|
993
1059
|
member_id,
|
|
994
1060
|
group_id,
|
|
@@ -1002,19 +1068,21 @@ class Salesforce(object):
|
|
|
1002
1068
|
json_data=payload,
|
|
1003
1069
|
timeout=REQUEST_TIMEOUT,
|
|
1004
1070
|
failure_message="Failed to add member with ID -> {} to Salesforce group with ID -> {}".format(
|
|
1005
|
-
member_id,
|
|
1071
|
+
member_id,
|
|
1072
|
+
group_id,
|
|
1006
1073
|
),
|
|
1007
1074
|
)
|
|
1008
1075
|
|
|
1009
1076
|
# end method definition
|
|
1010
1077
|
|
|
1011
1078
|
def get_all_user_profiles(self) -> dict | None:
|
|
1012
|
-
"""Get all user profiles
|
|
1079
|
+
"""Get all user profiles.
|
|
1013
1080
|
|
|
1014
1081
|
Returns:
|
|
1015
|
-
dict | None:
|
|
1082
|
+
dict | None:
|
|
1083
|
+
Dictionary with Salesforce user profiles.
|
|
1016
1084
|
|
|
1017
|
-
|
|
1085
|
+
Example response:
|
|
1018
1086
|
{
|
|
1019
1087
|
'totalSize': 15,
|
|
1020
1088
|
'done': True,
|
|
@@ -1038,6 +1106,7 @@ class Salesforce(object):
|
|
|
1038
1106
|
}, ...
|
|
1039
1107
|
]
|
|
1040
1108
|
}
|
|
1109
|
+
|
|
1041
1110
|
"""
|
|
1042
1111
|
|
|
1043
1112
|
if not self._access_token or not self._instance_url:
|
|
@@ -1059,7 +1128,7 @@ class Salesforce(object):
|
|
|
1059
1128
|
|
|
1060
1129
|
# end method definition
|
|
1061
1130
|
|
|
1062
|
-
def get_user_profile_id(self, profile_name: str) ->
|
|
1131
|
+
def get_user_profile_id(self, profile_name: str) -> str | None:
|
|
1063
1132
|
"""Get a user profile ID by profile name.
|
|
1064
1133
|
|
|
1065
1134
|
Args:
|
|
@@ -1067,13 +1136,14 @@ class Salesforce(object):
|
|
|
1067
1136
|
|
|
1068
1137
|
Returns:
|
|
1069
1138
|
Optional[str]: Technical ID of the user profile.
|
|
1139
|
+
|
|
1070
1140
|
"""
|
|
1071
1141
|
|
|
1072
1142
|
return self.get_object_id_by_name(object_type="Profile", name=profile_name)
|
|
1073
1143
|
|
|
1074
1144
|
# end method definition
|
|
1075
1145
|
|
|
1076
|
-
def get_user_id(self, username: str) ->
|
|
1146
|
+
def get_user_id(self, username: str) -> str | None:
|
|
1077
1147
|
"""Get a user ID by user name.
|
|
1078
1148
|
|
|
1079
1149
|
Args:
|
|
@@ -1081,10 +1151,13 @@ class Salesforce(object):
|
|
|
1081
1151
|
|
|
1082
1152
|
Returns:
|
|
1083
1153
|
Optional[str]: Technical ID of the user
|
|
1154
|
+
|
|
1084
1155
|
"""
|
|
1085
1156
|
|
|
1086
1157
|
return self.get_object_id_by_name(
|
|
1087
|
-
object_type="User",
|
|
1158
|
+
object_type="User",
|
|
1159
|
+
name=username,
|
|
1160
|
+
name_field="Username",
|
|
1088
1161
|
)
|
|
1089
1162
|
|
|
1090
1163
|
# end method definition
|
|
@@ -1097,6 +1170,7 @@ class Salesforce(object):
|
|
|
1097
1170
|
|
|
1098
1171
|
Returns:
|
|
1099
1172
|
dict | None: Dictionary with the Salesforce user data or None if the request fails.
|
|
1173
|
+
|
|
1100
1174
|
"""
|
|
1101
1175
|
|
|
1102
1176
|
if not self._access_token or not self._instance_url:
|
|
@@ -1105,8 +1179,10 @@ class Salesforce(object):
|
|
|
1105
1179
|
request_header = self.request_header()
|
|
1106
1180
|
request_url = self.config()["userUrl"] + user_id
|
|
1107
1181
|
|
|
1108
|
-
logger.debug(
|
|
1109
|
-
"Get Salesforce user with ID -> %s; calling -> %s",
|
|
1182
|
+
self.logger.debug(
|
|
1183
|
+
"Get Salesforce user with ID -> %s; calling -> %s",
|
|
1184
|
+
user_id,
|
|
1185
|
+
request_url,
|
|
1110
1186
|
)
|
|
1111
1187
|
|
|
1112
1188
|
return self.do_request(
|
|
@@ -1115,7 +1191,7 @@ class Salesforce(object):
|
|
|
1115
1191
|
headers=request_header,
|
|
1116
1192
|
timeout=REQUEST_TIMEOUT,
|
|
1117
1193
|
failure_message="Failed to get Salesforce user with ID -> {}".format(
|
|
1118
|
-
user_id
|
|
1194
|
+
user_id,
|
|
1119
1195
|
),
|
|
1120
1196
|
)
|
|
1121
1197
|
|
|
@@ -1130,34 +1206,48 @@ class Salesforce(object):
|
|
|
1130
1206
|
title: str | None = None,
|
|
1131
1207
|
department: str | None = None,
|
|
1132
1208
|
company_name: str = "Innovate",
|
|
1133
|
-
profile_name:
|
|
1134
|
-
profile_id:
|
|
1135
|
-
time_zone_key:
|
|
1136
|
-
email_encoding_key:
|
|
1137
|
-
locale_key:
|
|
1138
|
-
alias:
|
|
1209
|
+
profile_name: str | None = "Standard User",
|
|
1210
|
+
profile_id: str | None = None,
|
|
1211
|
+
time_zone_key: str | None = "America/Los_Angeles",
|
|
1212
|
+
email_encoding_key: str | None = "ISO-8859-1",
|
|
1213
|
+
locale_key: str | None = "en_US",
|
|
1214
|
+
alias: str | None = None,
|
|
1139
1215
|
) -> dict | None:
|
|
1140
1216
|
"""Add a new Salesforce user. The password has to be set separately.
|
|
1141
1217
|
|
|
1142
1218
|
Args:
|
|
1143
|
-
username (str):
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1219
|
+
username (str):
|
|
1220
|
+
Login name of the new user
|
|
1221
|
+
email (str):
|
|
1222
|
+
Email of the new user
|
|
1223
|
+
firstname (str):
|
|
1224
|
+
First name of the new user.
|
|
1225
|
+
lastname (str):
|
|
1226
|
+
Last name of the new user.
|
|
1227
|
+
title (str):
|
|
1228
|
+
Title of the user.
|
|
1229
|
+
department (str):
|
|
1230
|
+
Department of the user.
|
|
1231
|
+
company_name (str):
|
|
1232
|
+
Name of the Company of the user.
|
|
1233
|
+
profile_name (str):
|
|
1234
|
+
Profile name like "Standard User"
|
|
1235
|
+
profile_id (str, optional):
|
|
1236
|
+
Profile ID of the new user. Defaults to None.
|
|
1237
|
+
Use method get_all_user_profiles() to determine
|
|
1238
|
+
the desired Profile for the user. Or pass the profile_name.
|
|
1239
|
+
time_zone_key (str, optional):
|
|
1240
|
+
Timezone provided in format country/city like "America/Los_Angeles",
|
|
1241
|
+
email_encoding_key (str, optional):
|
|
1242
|
+
Default is "ISO-8859-1".
|
|
1243
|
+
locale_key (str, optional):
|
|
1244
|
+
Default is "en_US".
|
|
1245
|
+
alias (str, optional):
|
|
1246
|
+
Alias of the new user. Defaults to None.
|
|
1158
1247
|
|
|
1159
1248
|
Returns:
|
|
1160
1249
|
dict | None: Dictionary with the Salesforce User data or None if the request fails.
|
|
1250
|
+
|
|
1161
1251
|
"""
|
|
1162
1252
|
|
|
1163
1253
|
if not self._access_token or not self._instance_url:
|
|
@@ -1186,8 +1276,10 @@ class Salesforce(object):
|
|
|
1186
1276
|
"LanguageLocaleKey": locale_key, # Set default LanguageLocaleKey
|
|
1187
1277
|
}
|
|
1188
1278
|
|
|
1189
|
-
logger.debug(
|
|
1190
|
-
"Adding Salesforce user -> %s; calling -> %s",
|
|
1279
|
+
self.logger.debug(
|
|
1280
|
+
"Adding Salesforce user -> %s; calling -> %s",
|
|
1281
|
+
username,
|
|
1282
|
+
request_url,
|
|
1191
1283
|
)
|
|
1192
1284
|
|
|
1193
1285
|
return self.do_request(
|
|
@@ -1214,6 +1306,7 @@ class Salesforce(object):
|
|
|
1214
1306
|
|
|
1215
1307
|
Returns:
|
|
1216
1308
|
dict: Response from the Salesforce API.
|
|
1309
|
+
|
|
1217
1310
|
"""
|
|
1218
1311
|
|
|
1219
1312
|
if not self._access_token or not self._instance_url:
|
|
@@ -1223,8 +1316,10 @@ class Salesforce(object):
|
|
|
1223
1316
|
|
|
1224
1317
|
request_url = self.config()["userUrl"] + user_id
|
|
1225
1318
|
|
|
1226
|
-
logger.debug(
|
|
1227
|
-
"Update Salesforce user with ID -> %s; calling -> %s",
|
|
1319
|
+
self.logger.debug(
|
|
1320
|
+
"Update Salesforce user with ID -> %s; calling -> %s",
|
|
1321
|
+
user_id,
|
|
1322
|
+
request_url,
|
|
1228
1323
|
)
|
|
1229
1324
|
|
|
1230
1325
|
return self.do_request(
|
|
@@ -1234,7 +1329,7 @@ class Salesforce(object):
|
|
|
1234
1329
|
json_data=update_data,
|
|
1235
1330
|
timeout=REQUEST_TIMEOUT,
|
|
1236
1331
|
failure_message="Failed to update Salesforce user with ID -> {}".format(
|
|
1237
|
-
user_id
|
|
1332
|
+
user_id,
|
|
1238
1333
|
),
|
|
1239
1334
|
)
|
|
1240
1335
|
|
|
@@ -1244,7 +1339,7 @@ class Salesforce(object):
|
|
|
1244
1339
|
self,
|
|
1245
1340
|
user_id: str,
|
|
1246
1341
|
password: str,
|
|
1247
|
-
) -> dict:
|
|
1342
|
+
) -> dict | None:
|
|
1248
1343
|
"""Update the password of a Salesforce user.
|
|
1249
1344
|
|
|
1250
1345
|
Args:
|
|
@@ -1253,6 +1348,7 @@ class Salesforce(object):
|
|
|
1253
1348
|
|
|
1254
1349
|
Returns:
|
|
1255
1350
|
dict: Response from the Salesforce API.
|
|
1351
|
+
|
|
1256
1352
|
"""
|
|
1257
1353
|
|
|
1258
1354
|
if not self._access_token or not self._instance_url:
|
|
@@ -1262,7 +1358,7 @@ class Salesforce(object):
|
|
|
1262
1358
|
|
|
1263
1359
|
request_url = self.config()["userUrl"] + "{}/password".format(user_id)
|
|
1264
1360
|
|
|
1265
|
-
logger.debug(
|
|
1361
|
+
self.logger.debug(
|
|
1266
1362
|
"Update password of Salesforce user with ID -> %s; calling -> %s",
|
|
1267
1363
|
user_id,
|
|
1268
1364
|
request_url,
|
|
@@ -1277,7 +1373,7 @@ class Salesforce(object):
|
|
|
1277
1373
|
json_data=update_data,
|
|
1278
1374
|
timeout=REQUEST_TIMEOUT,
|
|
1279
1375
|
failure_message="Failed to update password of Salesforce user with ID -> {}".format(
|
|
1280
|
-
user_id
|
|
1376
|
+
user_id,
|
|
1281
1377
|
),
|
|
1282
1378
|
)
|
|
1283
1379
|
|
|
@@ -1293,8 +1389,10 @@ class Salesforce(object):
|
|
|
1293
1389
|
Args:
|
|
1294
1390
|
user_id (str): Salesforce ID of the user
|
|
1295
1391
|
photo_path (str): file system path with the location of the photo
|
|
1392
|
+
|
|
1296
1393
|
Returns:
|
|
1297
1394
|
dict | None: Dictionary with the Salesforce User data or None if the request fails.
|
|
1395
|
+
|
|
1298
1396
|
"""
|
|
1299
1397
|
|
|
1300
1398
|
if not self._access_token or not self._instance_url:
|
|
@@ -1302,17 +1400,18 @@ class Salesforce(object):
|
|
|
1302
1400
|
|
|
1303
1401
|
# Check if the photo file exists
|
|
1304
1402
|
if not os.path.isfile(photo_path):
|
|
1305
|
-
logger.error("Photo file -> %s not found!", photo_path)
|
|
1403
|
+
self.logger.error("Photo file -> %s not found!", photo_path)
|
|
1306
1404
|
return None
|
|
1307
1405
|
|
|
1308
1406
|
try:
|
|
1309
1407
|
# Read the photo file as binary data
|
|
1310
1408
|
with open(photo_path, "rb") as image_file:
|
|
1311
1409
|
photo_data = image_file.read()
|
|
1312
|
-
except OSError
|
|
1410
|
+
except OSError:
|
|
1313
1411
|
# Handle any errors that occurred while reading the photo file
|
|
1314
|
-
logger.error(
|
|
1315
|
-
"Error reading photo file -> %s
|
|
1412
|
+
self.logger.error(
|
|
1413
|
+
"Error reading photo file -> %s",
|
|
1414
|
+
photo_path,
|
|
1316
1415
|
)
|
|
1317
1416
|
return None
|
|
1318
1417
|
|
|
@@ -1327,10 +1426,10 @@ class Salesforce(object):
|
|
|
1327
1426
|
photo_path,
|
|
1328
1427
|
photo_data,
|
|
1329
1428
|
"application/octet-stream",
|
|
1330
|
-
)
|
|
1429
|
+
),
|
|
1331
1430
|
}
|
|
1332
1431
|
|
|
1333
|
-
logger.debug(
|
|
1432
|
+
self.logger.debug(
|
|
1334
1433
|
"Update profile photo of Salesforce user with ID -> %s; calling -> %s",
|
|
1335
1434
|
user_id,
|
|
1336
1435
|
request_url,
|
|
@@ -1344,7 +1443,7 @@ class Salesforce(object):
|
|
|
1344
1443
|
data=data,
|
|
1345
1444
|
timeout=REQUEST_TIMEOUT,
|
|
1346
1445
|
failure_message="Failed to update profile photo of Salesforce user with ID -> {}".format(
|
|
1347
|
-
user_id
|
|
1446
|
+
user_id,
|
|
1348
1447
|
),
|
|
1349
1448
|
verify=False,
|
|
1350
1449
|
)
|
|
@@ -1356,11 +1455,11 @@ class Salesforce(object):
|
|
|
1356
1455
|
account_name: str,
|
|
1357
1456
|
account_number: str,
|
|
1358
1457
|
account_type: str = "Customer",
|
|
1359
|
-
description:
|
|
1360
|
-
industry:
|
|
1361
|
-
website:
|
|
1362
|
-
phone:
|
|
1363
|
-
**kwargs:
|
|
1458
|
+
description: str | None = None,
|
|
1459
|
+
industry: str | None = None,
|
|
1460
|
+
website: str | None = None,
|
|
1461
|
+
phone: str | None = None,
|
|
1462
|
+
**kwargs: dict[str, str],
|
|
1364
1463
|
) -> dict | None:
|
|
1365
1464
|
"""Add a new Account object to Salesforce.
|
|
1366
1465
|
|
|
@@ -1372,10 +1471,11 @@ class Salesforce(object):
|
|
|
1372
1471
|
industry (str, optional): Industry of the new Salesforce account. Defaults to None.
|
|
1373
1472
|
website (str, optional): Website of the new Salesforce account. Defaults to None.
|
|
1374
1473
|
phone (str, optional): Phone number of the new Salesforce account. Defaults to None.
|
|
1375
|
-
kwargs (
|
|
1474
|
+
kwargs (dict): Additional values (e.g. custom fields)
|
|
1376
1475
|
|
|
1377
1476
|
Returns:
|
|
1378
1477
|
dict | None: Dictionary with the Salesforce Account data or None if the request fails.
|
|
1478
|
+
|
|
1379
1479
|
"""
|
|
1380
1480
|
|
|
1381
1481
|
if not self._access_token or not self._instance_url:
|
|
@@ -1395,7 +1495,7 @@ class Salesforce(object):
|
|
|
1395
1495
|
}
|
|
1396
1496
|
payload.update(kwargs) # Add additional fields from kwargs
|
|
1397
1497
|
|
|
1398
|
-
logger.debug(
|
|
1498
|
+
self.logger.debug(
|
|
1399
1499
|
"Adding Salesforce account -> '%s' (%s); calling -> %s",
|
|
1400
1500
|
account_name,
|
|
1401
1501
|
account_number,
|
|
@@ -1409,7 +1509,8 @@ class Salesforce(object):
|
|
|
1409
1509
|
data=json.dumps(payload),
|
|
1410
1510
|
timeout=REQUEST_TIMEOUT,
|
|
1411
1511
|
failure_message="Failed to add Salesforce account -> '{}' ({})".format(
|
|
1412
|
-
account_name,
|
|
1512
|
+
account_name,
|
|
1513
|
+
account_number,
|
|
1413
1514
|
),
|
|
1414
1515
|
)
|
|
1415
1516
|
|
|
@@ -1421,18 +1522,25 @@ class Salesforce(object):
|
|
|
1421
1522
|
product_code: str,
|
|
1422
1523
|
description: str,
|
|
1423
1524
|
price: float,
|
|
1424
|
-
**kwargs:
|
|
1525
|
+
**kwargs: dict[str, str],
|
|
1425
1526
|
) -> dict | None:
|
|
1426
1527
|
"""Add a new Product object to Salesforce.
|
|
1427
1528
|
|
|
1428
1529
|
Args:
|
|
1429
|
-
product_name (str):
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1530
|
+
product_name (str):
|
|
1531
|
+
Name of the Salesforce Product.
|
|
1532
|
+
product_code (str):
|
|
1533
|
+
Code of the Salesforce Product.
|
|
1534
|
+
description (str):
|
|
1535
|
+
Description of the Salesforce Product.
|
|
1536
|
+
price (float):
|
|
1537
|
+
Price of the Salesforce Product.
|
|
1538
|
+
kwargs:
|
|
1539
|
+
Additional keyword arguments.
|
|
1433
1540
|
|
|
1434
1541
|
Returns:
|
|
1435
1542
|
dict | None: Dictionary with the Salesforce Product data or None if the request fails.
|
|
1543
|
+
|
|
1436
1544
|
"""
|
|
1437
1545
|
|
|
1438
1546
|
if not self._access_token or not self._instance_url:
|
|
@@ -1449,7 +1557,7 @@ class Salesforce(object):
|
|
|
1449
1557
|
}
|
|
1450
1558
|
payload.update(kwargs) # Add additional fields from kwargs
|
|
1451
1559
|
|
|
1452
|
-
logger.debug(
|
|
1560
|
+
self.logger.debug(
|
|
1453
1561
|
"Add Salesforce product -> '%s' (%s); calling -> %s",
|
|
1454
1562
|
product_name,
|
|
1455
1563
|
product_code,
|
|
@@ -1463,7 +1571,8 @@ class Salesforce(object):
|
|
|
1463
1571
|
data=json.dumps(payload),
|
|
1464
1572
|
timeout=REQUEST_TIMEOUT,
|
|
1465
1573
|
failure_message="Failed to add Salesforce product -> '{}' ({})".format(
|
|
1466
|
-
product_name,
|
|
1574
|
+
product_name,
|
|
1575
|
+
product_code,
|
|
1467
1576
|
),
|
|
1468
1577
|
)
|
|
1469
1578
|
|
|
@@ -1474,25 +1583,38 @@ class Salesforce(object):
|
|
|
1474
1583
|
name: str,
|
|
1475
1584
|
stage: str,
|
|
1476
1585
|
close_date: str,
|
|
1477
|
-
amount:
|
|
1586
|
+
amount: float,
|
|
1478
1587
|
account_id: str,
|
|
1479
|
-
description: str = None,
|
|
1480
|
-
**kwargs:
|
|
1588
|
+
description: str | None = None,
|
|
1589
|
+
**kwargs: dict[str, str],
|
|
1481
1590
|
) -> dict | None:
|
|
1482
1591
|
"""Add a new Opportunity object to Salesfoce.
|
|
1483
1592
|
|
|
1484
1593
|
Args:
|
|
1485
1594
|
name (str): Name of the Opportunity.
|
|
1486
|
-
stage (str):
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1595
|
+
stage (str):
|
|
1596
|
+
Stage of the Opportunity. Typical Value:
|
|
1597
|
+
- "Prospecting"
|
|
1598
|
+
- "Qualification"
|
|
1599
|
+
- "Value Proposition"
|
|
1600
|
+
- "Negotiation/Review",
|
|
1601
|
+
- "Closed Won"
|
|
1602
|
+
- "Closed Lost"
|
|
1603
|
+
close_date (str):
|
|
1604
|
+
Close date of the Opportunity. Should be in format YYYY-MM-DD.
|
|
1605
|
+
amount (Union[int, float]):
|
|
1606
|
+
Amount (expected revenue) of the opportunity.
|
|
1607
|
+
Can either be an integer or a float value.
|
|
1608
|
+
account_id (str):
|
|
1609
|
+
The technical ID of the related Salesforce Account.
|
|
1610
|
+
description (str | None, optional):
|
|
1611
|
+
A description of the opportunity.
|
|
1612
|
+
kwargs (dict):
|
|
1613
|
+
Additional keyword arguments.
|
|
1493
1614
|
|
|
1494
1615
|
Returns:
|
|
1495
1616
|
dict | None: Dictionary with the Salesforce Opportunity data or None if the request fails.
|
|
1617
|
+
|
|
1496
1618
|
"""
|
|
1497
1619
|
|
|
1498
1620
|
if not self._access_token or not self._instance_url:
|
|
@@ -1512,8 +1634,10 @@ class Salesforce(object):
|
|
|
1512
1634
|
payload["Description"] = description
|
|
1513
1635
|
payload.update(kwargs) # Add additional fields from kwargs
|
|
1514
1636
|
|
|
1515
|
-
logger.debug(
|
|
1516
|
-
"Add Salesforce opportunity -> '%s'; calling -> %s",
|
|
1637
|
+
self.logger.debug(
|
|
1638
|
+
"Add Salesforce opportunity -> '%s'; calling -> %s",
|
|
1639
|
+
name,
|
|
1640
|
+
request_url,
|
|
1517
1641
|
)
|
|
1518
1642
|
|
|
1519
1643
|
return self.do_request(
|
|
@@ -1536,27 +1660,40 @@ class Salesforce(object):
|
|
|
1536
1660
|
origin: str,
|
|
1537
1661
|
account_id: str,
|
|
1538
1662
|
owner_id: str,
|
|
1539
|
-
asset_id:
|
|
1540
|
-
product_id:
|
|
1541
|
-
**kwargs:
|
|
1663
|
+
asset_id: str | None = None,
|
|
1664
|
+
product_id: str | None = None,
|
|
1665
|
+
**kwargs: dict[str, str],
|
|
1542
1666
|
) -> dict | None:
|
|
1543
|
-
"""Add a new Case object to Salesforce.
|
|
1544
|
-
|
|
1667
|
+
"""Add a new Case object to Salesforce.
|
|
1668
|
+
|
|
1669
|
+
The case number is automatically created and can not be provided.
|
|
1545
1670
|
|
|
1546
1671
|
Args:
|
|
1547
|
-
subject (str):
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1672
|
+
subject (str):
|
|
1673
|
+
Subject (title) of the case. It's like the name.
|
|
1674
|
+
description (str):
|
|
1675
|
+
Description of the case
|
|
1676
|
+
status (str):
|
|
1677
|
+
Status of the case. Typecal values: "New", "On Hold", "Escalated"
|
|
1678
|
+
priority (str):
|
|
1679
|
+
Priority of the case. Typical values: "High", "Medium", "Low".
|
|
1680
|
+
origin (str):
|
|
1681
|
+
Origin (source) of the case. Typical values: "Email", "Phone", "Web"
|
|
1682
|
+
account_id (str):
|
|
1683
|
+
Technical ID of the related Account
|
|
1684
|
+
owner_id (str):
|
|
1685
|
+
Owner of the case
|
|
1686
|
+
asset_id (str):
|
|
1687
|
+
Technical ID of the related Asset.
|
|
1688
|
+
product_id (str):
|
|
1689
|
+
Technical ID of the related Product.
|
|
1690
|
+
kwargs (dict):
|
|
1691
|
+
Additional values (e.g. custom fields)
|
|
1557
1692
|
|
|
1558
1693
|
Returns:
|
|
1559
|
-
dict | None:
|
|
1694
|
+
dict | None:
|
|
1695
|
+
Dictionary with the Salesforce Case data or None if the request fails.
|
|
1696
|
+
|
|
1560
1697
|
"""
|
|
1561
1698
|
|
|
1562
1699
|
if not self._access_token or not self._instance_url:
|
|
@@ -1581,7 +1718,11 @@ class Salesforce(object):
|
|
|
1581
1718
|
payload["ProductId"] = product_id
|
|
1582
1719
|
payload.update(kwargs) # Add additional fields from kwargs
|
|
1583
1720
|
|
|
1584
|
-
logger.debug(
|
|
1721
|
+
self.logger.debug(
|
|
1722
|
+
"Add Salesforce case -> '%s'; calling -> %s",
|
|
1723
|
+
subject,
|
|
1724
|
+
request_url,
|
|
1725
|
+
)
|
|
1585
1726
|
|
|
1586
1727
|
return self.do_request(
|
|
1587
1728
|
method="POST",
|
|
@@ -1603,22 +1744,33 @@ class Salesforce(object):
|
|
|
1603
1744
|
purchase_date: str,
|
|
1604
1745
|
install_date: str,
|
|
1605
1746
|
description: str | None = None,
|
|
1606
|
-
**kwargs:
|
|
1747
|
+
**kwargs: dict[str, str],
|
|
1607
1748
|
) -> dict | None:
|
|
1608
1749
|
"""Add a new Asset object to Salesforce.
|
|
1609
1750
|
|
|
1610
1751
|
Args:
|
|
1611
|
-
asset_name (str):
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1752
|
+
asset_name (str):
|
|
1753
|
+
The name of the Asset.
|
|
1754
|
+
product_id (str):
|
|
1755
|
+
Related Product ID.
|
|
1756
|
+
serial_number (str):
|
|
1757
|
+
Serial Number of the Asset.
|
|
1758
|
+
status (str):
|
|
1759
|
+
The status of the Asset.
|
|
1760
|
+
Typical values are "Purchased", "Shipped", "Installed", "Registered", "Obsolete"
|
|
1761
|
+
purchase_date (str):
|
|
1762
|
+
Purchase date of the Asset.
|
|
1763
|
+
install_date (str):
|
|
1764
|
+
Install date of the Asset.
|
|
1765
|
+
description (str):
|
|
1766
|
+
Description of the Asset.
|
|
1767
|
+
kwargs (dict):
|
|
1768
|
+
Additional values (e.g. custom fields)
|
|
1619
1769
|
|
|
1620
1770
|
Returns:
|
|
1621
|
-
dict | None:
|
|
1771
|
+
dict | None:
|
|
1772
|
+
Dictionary with the Salesforce Asset data or None if the request fails.
|
|
1773
|
+
|
|
1622
1774
|
"""
|
|
1623
1775
|
|
|
1624
1776
|
if not self._access_token or not self._instance_url:
|
|
@@ -1639,8 +1791,10 @@ class Salesforce(object):
|
|
|
1639
1791
|
payload["Description"] = description
|
|
1640
1792
|
payload.update(kwargs) # Add additional fields from kwargs
|
|
1641
1793
|
|
|
1642
|
-
logger.debug(
|
|
1643
|
-
"Add Salesforce asset -> '%s'; calling -> %s",
|
|
1794
|
+
self.logger.debug(
|
|
1795
|
+
"Add Salesforce asset -> '%s'; calling -> %s",
|
|
1796
|
+
asset_name,
|
|
1797
|
+
request_url,
|
|
1644
1798
|
)
|
|
1645
1799
|
|
|
1646
1800
|
return self.do_request(
|
|
@@ -1660,23 +1814,43 @@ class Salesforce(object):
|
|
|
1660
1814
|
start_date: str,
|
|
1661
1815
|
contract_term: int,
|
|
1662
1816
|
status: str = "Draft",
|
|
1663
|
-
description:
|
|
1664
|
-
contract_type:
|
|
1665
|
-
**kwargs:
|
|
1817
|
+
description: str | None = None,
|
|
1818
|
+
contract_type: str | None = None,
|
|
1819
|
+
**kwargs: dict[str, str],
|
|
1666
1820
|
) -> dict | None:
|
|
1667
1821
|
"""Add a new Contract object to Salesforce.
|
|
1668
1822
|
|
|
1669
1823
|
Args:
|
|
1670
|
-
account_id (str):
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1824
|
+
account_id (str):
|
|
1825
|
+
The technical ID of the related Salesforce Account object.
|
|
1826
|
+
start_date (str):
|
|
1827
|
+
Start date of the contract. Use YYYY-MM-DD notation.
|
|
1828
|
+
contract_term (int):
|
|
1829
|
+
Term of the contract in number of months, e.g. 48 for 4 years term.
|
|
1830
|
+
The end date of the contract will be calculated from start date + term.
|
|
1831
|
+
contract_type (str):
|
|
1832
|
+
Type of the Contract. Typical values are:
|
|
1833
|
+
- "Subscription"
|
|
1834
|
+
- "Maintenance"
|
|
1835
|
+
- "Support"
|
|
1836
|
+
- "Lease"
|
|
1837
|
+
- "Service"
|
|
1838
|
+
status (str):
|
|
1839
|
+
Status of the Contract. Typical values are:
|
|
1840
|
+
- "Draft"
|
|
1841
|
+
- "Activated"
|
|
1842
|
+
- "In Approval Process"
|
|
1843
|
+
description (str, optional):
|
|
1844
|
+
Description of the contract.
|
|
1845
|
+
contract_type:
|
|
1846
|
+
Type name of the contract.
|
|
1847
|
+
kwargs:
|
|
1848
|
+
Additional keyword arguments.
|
|
1677
1849
|
|
|
1678
1850
|
Returns:
|
|
1679
|
-
dict | None:
|
|
1851
|
+
dict | None:
|
|
1852
|
+
Dictionary with the Salesforce contract data or None if the request fails.
|
|
1853
|
+
|
|
1680
1854
|
"""
|
|
1681
1855
|
|
|
1682
1856
|
if not self._access_token or not self._instance_url:
|
|
@@ -1697,7 +1871,7 @@ class Salesforce(object):
|
|
|
1697
1871
|
payload["ContractType"] = contract_type
|
|
1698
1872
|
payload.update(kwargs) # Add additional fields from kwargs
|
|
1699
1873
|
|
|
1700
|
-
logger.debug(
|
|
1874
|
+
self.logger.debug(
|
|
1701
1875
|
"Adding Salesforce contract for account with ID -> %s; calling -> %s",
|
|
1702
1876
|
account_id,
|
|
1703
1877
|
request_url,
|
|
@@ -1710,7 +1884,7 @@ class Salesforce(object):
|
|
|
1710
1884
|
data=json.dumps(payload),
|
|
1711
1885
|
timeout=REQUEST_TIMEOUT,
|
|
1712
1886
|
failure_message="Failed to add Salesforce contract for account with ID -> {}".format(
|
|
1713
|
-
account_id
|
|
1887
|
+
account_id,
|
|
1714
1888
|
),
|
|
1715
1889
|
)
|
|
1716
1890
|
|