salespyforce 1.4.0.dev0__tar.gz → 1.4.0.dev2__tar.gz
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.
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/PKG-INFO +6 -10
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/README.md +5 -9
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/pyproject.toml +17 -17
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/api.py +44 -1
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/core.py +94 -13
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/knowledge.py +21 -5
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/core_utils.py +44 -2
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/log_utils.py +17 -16
- salespyforce-1.4.0.dev2/src/salespyforce/utils/tests/conftest.py +202 -0
- salespyforce-1.4.0.dev2/src/salespyforce/utils/tests/test_core_utils.py +234 -0
- salespyforce-1.4.0.dev2/src/salespyforce/utils/tests/test_instantiate_object.py +62 -0
- salespyforce-1.4.0.dev2/src/salespyforce/utils/tests/test_log_utils.py +79 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/tests/test_sobjects.py +22 -15
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/tests/test_soql.py +8 -8
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/tests/test_sosl.py +9 -9
- salespyforce-1.4.0.dev2/src/salespyforce/utils/tests/test_version_utils.py +70 -0
- salespyforce-1.4.0.dev0/src/salespyforce/utils/tests/test_instantiate_object.py +0 -49
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/LICENSE +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/__init__.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/chatter.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/errors/__init__.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/errors/exceptions.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/errors/handlers.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/__init__.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/helper.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/tests/__init__.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/tests/resources.py +0 -0
- {salespyforce-1.4.0.dev0 → salespyforce-1.4.0.dev2}/src/salespyforce/utils/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: salespyforce
|
|
3
|
-
Version: 1.4.0.
|
|
3
|
+
Version: 1.4.0.dev2
|
|
4
4
|
Summary: A Python toolset for performing Salesforce API calls
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -63,7 +63,7 @@ A Python toolset for performing Salesforce API calls
|
|
|
63
63
|
<td>Latest Beta/RC Release</td>
|
|
64
64
|
<td>
|
|
65
65
|
<a href='https://pypi.org/project/salespyforce/#history'>
|
|
66
|
-
<img alt="PyPI" src="https://img.shields.io/badge/pypi-1.
|
|
66
|
+
<img alt="PyPI" src="https://img.shields.io/badge/pypi-1.4.0.dev1-blue">
|
|
67
67
|
</a>
|
|
68
68
|
</td>
|
|
69
69
|
</tr>
|
|
@@ -140,6 +140,10 @@ A Python toolset for performing Salesforce API calls
|
|
|
140
140
|
</tr>
|
|
141
141
|
</table>
|
|
142
142
|
|
|
143
|
+
## Overview
|
|
144
|
+
salespyforce is a Python toolkit focused on interacting with Salesforce APIs, with a primary `Salesforce` class
|
|
145
|
+
that centralizes authentication, version selection, and access to helper feature sets (Chatter, Knowledge).
|
|
146
|
+
|
|
143
147
|
## Installation
|
|
144
148
|
The package can be installed via pip using the syntax below.
|
|
145
149
|
|
|
@@ -147,14 +151,6 @@ The package can be installed via pip using the syntax below.
|
|
|
147
151
|
pip install salespyforce --upgrade
|
|
148
152
|
```
|
|
149
153
|
|
|
150
|
-
You may also clone the repository and install from source using below.
|
|
151
|
-
|
|
152
|
-
```sh
|
|
153
|
-
git clone git://github.com/jeffshurtliff/salespyforce.git
|
|
154
|
-
cd salespyforce/
|
|
155
|
-
python setup.py install
|
|
156
|
-
```
|
|
157
|
-
|
|
158
154
|
## Change Log
|
|
159
155
|
The change log can be found in the [documentation](https://salespyforce.readthedocs.io/en/latest/changelog.html).
|
|
160
156
|
|
|
@@ -14,7 +14,7 @@ A Python toolset for performing Salesforce API calls
|
|
|
14
14
|
<td>Latest Beta/RC Release</td>
|
|
15
15
|
<td>
|
|
16
16
|
<a href='https://pypi.org/project/salespyforce/#history'>
|
|
17
|
-
<img alt="PyPI" src="https://img.shields.io/badge/pypi-1.
|
|
17
|
+
<img alt="PyPI" src="https://img.shields.io/badge/pypi-1.4.0.dev1-blue">
|
|
18
18
|
</a>
|
|
19
19
|
</td>
|
|
20
20
|
</tr>
|
|
@@ -91,6 +91,10 @@ A Python toolset for performing Salesforce API calls
|
|
|
91
91
|
</tr>
|
|
92
92
|
</table>
|
|
93
93
|
|
|
94
|
+
## Overview
|
|
95
|
+
salespyforce is a Python toolkit focused on interacting with Salesforce APIs, with a primary `Salesforce` class
|
|
96
|
+
that centralizes authentication, version selection, and access to helper feature sets (Chatter, Knowledge).
|
|
97
|
+
|
|
94
98
|
## Installation
|
|
95
99
|
The package can be installed via pip using the syntax below.
|
|
96
100
|
|
|
@@ -98,14 +102,6 @@ The package can be installed via pip using the syntax below.
|
|
|
98
102
|
pip install salespyforce --upgrade
|
|
99
103
|
```
|
|
100
104
|
|
|
101
|
-
You may also clone the repository and install from source using below.
|
|
102
|
-
|
|
103
|
-
```sh
|
|
104
|
-
git clone git://github.com/jeffshurtliff/salespyforce.git
|
|
105
|
-
cd salespyforce/
|
|
106
|
-
python setup.py install
|
|
107
|
-
```
|
|
108
|
-
|
|
109
105
|
## Change Log
|
|
110
106
|
The change log can be found in the [documentation](https://salespyforce.readthedocs.io/en/latest/changelog.html).
|
|
111
107
|
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "salespyforce"
|
|
3
|
-
version = "1.4.0.
|
|
3
|
+
version = "1.4.0.dev2"
|
|
4
4
|
description = "A Python toolset for performing Salesforce API calls"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.9,<4.0"
|
|
7
7
|
license = { file = "LICENSE" }
|
|
8
8
|
authors = [
|
|
9
|
-
|
|
9
|
+
{ name = "Jeff Shurtliff", email = "jeffshurtliff@gmail.com" }
|
|
10
10
|
]
|
|
11
11
|
keywords = ["salesforce", "sfdc", "api", "rest", "tooling", "python"]
|
|
12
12
|
|
|
13
13
|
classifiers = [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
"Topic :: Internet :: WWW/HTTP"
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
dependencies = [
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
"PyYAML>=6.0.3,<7",
|
|
30
|
+
"requests>=2.32.5",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
@@ -41,7 +41,7 @@ Documentation = "https://salespyforce.readthedocs.io/en/latest/"
|
|
|
41
41
|
# --------------------------------------------------------------------
|
|
42
42
|
[tool.poetry]
|
|
43
43
|
packages = [
|
|
44
|
-
|
|
44
|
+
{ include = "salespyforce", from = "src" }
|
|
45
45
|
]
|
|
46
46
|
|
|
47
47
|
include = ["LICENSE", "README.md"]
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
:Synopsis: Defines the basic functions associated with the Salesforce API
|
|
5
5
|
:Created By: Jeff Shurtliff
|
|
6
6
|
:Last Modified: Jeff Shurtliff
|
|
7
|
-
:Modified Date:
|
|
7
|
+
:Modified Date: 29 Jan 2026
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import requests
|
|
@@ -117,6 +117,49 @@ def api_call_with_payload(sfdc_object, method, endpoint, payload, params=None, h
|
|
|
117
117
|
return response
|
|
118
118
|
|
|
119
119
|
|
|
120
|
+
def delete(sfdc_object, endpoint, params=None, headers=None, timeout=30, show_full_error=True, return_json=True):
|
|
121
|
+
"""This method performs a DELETE request against the Salesforce instance.
|
|
122
|
+
|
|
123
|
+
.. version-added:: 1.4.0
|
|
124
|
+
|
|
125
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
126
|
+
:param endpoint: The API endpoint to query
|
|
127
|
+
:type endpoint: str
|
|
128
|
+
:param params: The query parameters (where applicable)
|
|
129
|
+
:type params: dict, None
|
|
130
|
+
:param headers: Specific API headers to use when performing the API call
|
|
131
|
+
:type headers: dict, None
|
|
132
|
+
:param timeout: The timeout period in seconds (defaults to ``30``)
|
|
133
|
+
:type timeout: int, str, None
|
|
134
|
+
:param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
|
|
135
|
+
:type show_full_error: bool
|
|
136
|
+
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
137
|
+
:returns: The API response in JSON format or as a ``requests`` object
|
|
138
|
+
"""
|
|
139
|
+
# Define the parameters as an empty dictionary if none are provided
|
|
140
|
+
params = {} if params is None else params
|
|
141
|
+
|
|
142
|
+
# Define the headers
|
|
143
|
+
default_headers = _get_headers(sfdc_object.access_token)
|
|
144
|
+
headers = default_headers if not headers else headers
|
|
145
|
+
|
|
146
|
+
# Make sure the endpoint begins with a slash
|
|
147
|
+
endpoint = f'/{endpoint}' if not endpoint.startswith('/') else endpoint
|
|
148
|
+
|
|
149
|
+
# Perform the API call
|
|
150
|
+
response = requests.delete(f'{sfdc_object.instance_url}{endpoint}', headers=headers, params=params,
|
|
151
|
+
timeout=timeout)
|
|
152
|
+
if response.status_code >= 300:
|
|
153
|
+
if show_full_error:
|
|
154
|
+
raise RuntimeError(f'The DELETE request failed with a {response.status_code} status code.\n'
|
|
155
|
+
f'{response.text}')
|
|
156
|
+
else:
|
|
157
|
+
raise RuntimeError(f'The DELETE request failed with a {response.status_code} status code.')
|
|
158
|
+
if return_json:
|
|
159
|
+
response = response.json()
|
|
160
|
+
return response
|
|
161
|
+
|
|
162
|
+
|
|
120
163
|
def _get_headers(_access_token, _header_type='default'):
|
|
121
164
|
"""This function returns the appropriate HTTP headers to use for different types of API calls."""
|
|
122
165
|
headers = {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:Example: ``sfdc = Salesforce(helper=helper_file_path)``
|
|
7
7
|
:Created By: Jeff Shurtliff
|
|
8
8
|
:Last Modified: Jeff Shurtliff
|
|
9
|
-
:Modified Date:
|
|
9
|
+
:Modified Date: 30 Jan 2026
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import re
|
|
@@ -60,7 +60,8 @@ class Salesforce(object):
|
|
|
60
60
|
:param helper: The file path of a helper file
|
|
61
61
|
:type helper: str, None
|
|
62
62
|
:returns: The instantiated object
|
|
63
|
-
:raises: :py:exc:`TypeError
|
|
63
|
+
:raises: :py:exc:`TypeError`,
|
|
64
|
+
:py:exc:`RuntimeError`
|
|
64
65
|
"""
|
|
65
66
|
# Define the default settings
|
|
66
67
|
self._helper_settings = {}
|
|
@@ -147,6 +148,7 @@ class Salesforce(object):
|
|
|
147
148
|
(`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
|
|
148
149
|
|
|
149
150
|
:returns: The API call response with the authorization information
|
|
151
|
+
:raises: :py:exc:`RuntimeError`
|
|
150
152
|
"""
|
|
151
153
|
params = {
|
|
152
154
|
'grant_type': 'password',
|
|
@@ -176,6 +178,7 @@ class Salesforce(object):
|
|
|
176
178
|
:type show_full_error: bool
|
|
177
179
|
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
178
180
|
:returns: The API response in JSON format or as a ``requests`` object
|
|
181
|
+
:raises: :py:exc:`RuntimeError`
|
|
179
182
|
"""
|
|
180
183
|
return api.get(self, endpoint=endpoint, params=params, headers=headers, timeout=timeout,
|
|
181
184
|
show_full_error=show_full_error, return_json=return_json)
|
|
@@ -201,6 +204,7 @@ class Salesforce(object):
|
|
|
201
204
|
:type show_full_error: bool
|
|
202
205
|
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
203
206
|
:returns: The API response in JSON format or as a ``requests`` object
|
|
207
|
+
:raises: :py:exc:`RuntimeError`
|
|
204
208
|
"""
|
|
205
209
|
return api.api_call_with_payload(self, method=method, endpoint=endpoint, payload=payload, params=params,
|
|
206
210
|
headers=headers, timeout=timeout, show_full_error=show_full_error,
|
|
@@ -224,6 +228,7 @@ class Salesforce(object):
|
|
|
224
228
|
:type show_full_error: bool
|
|
225
229
|
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
226
230
|
:returns: The API response in JSON format or as a ``requests`` object
|
|
231
|
+
:raises: :py:exc:`RuntimeError`
|
|
227
232
|
"""
|
|
228
233
|
return api.api_call_with_payload(self, 'post', endpoint=endpoint, payload=payload, params=params,
|
|
229
234
|
headers=headers, timeout=timeout, show_full_error=show_full_error,
|
|
@@ -247,6 +252,7 @@ class Salesforce(object):
|
|
|
247
252
|
:type show_full_error: bool
|
|
248
253
|
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
249
254
|
:returns: The API response in JSON format or as a ``requests`` object
|
|
255
|
+
:raises: :py:exc:`RuntimeError`
|
|
250
256
|
"""
|
|
251
257
|
return api.api_call_with_payload(self, 'patch', endpoint=endpoint, payload=payload, params=params,
|
|
252
258
|
headers=headers, timeout=timeout, show_full_error=show_full_error,
|
|
@@ -270,16 +276,41 @@ class Salesforce(object):
|
|
|
270
276
|
:type show_full_error: bool
|
|
271
277
|
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
272
278
|
:returns: The API response in JSON format or as a ``requests`` object
|
|
279
|
+
:raises: :py:exc:`RuntimeError`
|
|
273
280
|
"""
|
|
274
281
|
return api.api_call_with_payload(self, 'put', endpoint=endpoint, payload=payload, params=params,
|
|
275
282
|
headers=headers, timeout=timeout, show_full_error=show_full_error,
|
|
276
283
|
return_json=return_json)
|
|
277
284
|
|
|
285
|
+
def delete(self, endpoint, params=None, headers=None, timeout=30, show_full_error=True, return_json=True):
|
|
286
|
+
"""This method performs a DELETE request against the Salesforce instance.
|
|
287
|
+
(`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
|
|
288
|
+
|
|
289
|
+
.. version-added:: 1.4.0
|
|
290
|
+
|
|
291
|
+
:param endpoint: The API endpoint to query
|
|
292
|
+
:type endpoint: str
|
|
293
|
+
:param params: The query parameters (where applicable)
|
|
294
|
+
:type params: dict, None
|
|
295
|
+
:param headers: Specific API headers to use when performing the API call
|
|
296
|
+
:type headers: dict, None
|
|
297
|
+
:param timeout: The timeout period in seconds (defaults to ``30``)
|
|
298
|
+
:type timeout: int, str, None
|
|
299
|
+
:param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
|
|
300
|
+
:type show_full_error: bool
|
|
301
|
+
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
302
|
+
:returns: The API response in JSON format or as a ``requests`` object
|
|
303
|
+
:raises: :py:exc:`RuntimeError`
|
|
304
|
+
"""
|
|
305
|
+
return api.delete(self, endpoint=endpoint, params=params, headers=headers, timeout=timeout,
|
|
306
|
+
show_full_error=show_full_error, return_json=return_json)
|
|
307
|
+
|
|
278
308
|
def get_api_versions(self) -> list:
|
|
279
309
|
"""This method returns the API versions for the Salesforce releases.
|
|
280
310
|
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_versions.htm>`_)
|
|
281
311
|
|
|
282
312
|
:returns: A list containing the API metadata from the ``/services/data`` endpoint.
|
|
313
|
+
:raises: :py:exc:`RuntimeError`
|
|
283
314
|
"""
|
|
284
315
|
return self.get('/services/data')
|
|
285
316
|
|
|
@@ -305,13 +336,19 @@ class Salesforce(object):
|
|
|
305
336
|
def get_org_limits(self):
|
|
306
337
|
"""This method returns a list of all org limits.
|
|
307
338
|
|
|
308
|
-
..
|
|
339
|
+
.. version-added:: 1.1.0
|
|
340
|
+
|
|
341
|
+
:returns: The Salesforce org governor limits data
|
|
342
|
+
:raises: :py:exc:`RuntimeError`
|
|
309
343
|
"""
|
|
310
344
|
return self.get(f'/services/data/{self.version}/limits')
|
|
311
345
|
|
|
312
346
|
def get_all_sobjects(self):
|
|
313
347
|
"""This method returns a list of all Salesforce objects. (i.e. sObjects)
|
|
314
348
|
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_describeGlobal.htm>`_)
|
|
349
|
+
|
|
350
|
+
:returns: The list of all Salesforce objects
|
|
351
|
+
:raises: :py:exc:`RuntimeError`
|
|
315
352
|
"""
|
|
316
353
|
return self.get(f'/services/data/{self.version}/sobjects')
|
|
317
354
|
|
|
@@ -325,6 +362,7 @@ class Salesforce(object):
|
|
|
325
362
|
:param describe: Determines if the full (i.e. ``describe``) data should be returned (defaults to ``False``)
|
|
326
363
|
:type describe: bool
|
|
327
364
|
:returns: The Salesforce object data
|
|
365
|
+
:raises: :py:exc:`RuntimeError`
|
|
328
366
|
"""
|
|
329
367
|
uri = f'/services/data/{self.version}/sobjects/{object_name}'
|
|
330
368
|
uri = f'{uri}/describe' if describe else uri
|
|
@@ -337,15 +375,32 @@ class Salesforce(object):
|
|
|
337
375
|
:param object_name: The name of the Salesforce object
|
|
338
376
|
:type object_name: str
|
|
339
377
|
:returns: The Salesforce object data
|
|
378
|
+
:raises: :py:exc:`RuntimeError`
|
|
340
379
|
"""
|
|
341
380
|
return self.get_sobject(object_name, describe=True)
|
|
342
381
|
|
|
343
382
|
def get_rest_resources(self):
|
|
344
383
|
"""This method returns a list of all available REST resources.
|
|
345
384
|
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_discoveryresource.htm>`_)
|
|
385
|
+
|
|
386
|
+
:returns: The list of all available REST resources for the Salesforce org
|
|
387
|
+
:raises: :py:exc:`RuntimeError`
|
|
346
388
|
"""
|
|
347
389
|
return self.get(f'/services/data/{self.version}')
|
|
348
390
|
|
|
391
|
+
@staticmethod
|
|
392
|
+
def get_18_char_id(record_id: str) -> str:
|
|
393
|
+
"""This method converts a 15-character Salesforce record ID to its 18-character case-insensitive form.
|
|
394
|
+
|
|
395
|
+
.. version-added:: 1.4.0
|
|
396
|
+
|
|
397
|
+
:param record_id: The Salesforce record ID to convert (or return unchanged if already 18 characters)
|
|
398
|
+
:type record_id: str
|
|
399
|
+
:returns: The 18-character Salesforce record ID
|
|
400
|
+
:raises: :py:exc:`ValueError`
|
|
401
|
+
"""
|
|
402
|
+
return core_utils.get_18_char_id(record_id=record_id)
|
|
403
|
+
|
|
349
404
|
def soql_query(self, query, replace_quotes=True, next_records_url=False):
|
|
350
405
|
"""This method performs a SOQL query and returns the results in JSON format.
|
|
351
406
|
(`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_,
|
|
@@ -358,6 +413,7 @@ class Salesforce(object):
|
|
|
358
413
|
:param next_records_url: Indicates that the ``query`` parameter is a ``nextRecordsUrl`` value.
|
|
359
414
|
:type next_records_url: bool
|
|
360
415
|
:returns: The result of the SOQL query
|
|
416
|
+
:raises: :py:exc:`RuntimeError`
|
|
361
417
|
"""
|
|
362
418
|
if next_records_url:
|
|
363
419
|
query = re.sub(r'^.*/', '', query) if '/' in query else query
|
|
@@ -372,11 +428,12 @@ class Salesforce(object):
|
|
|
372
428
|
"""This method performs a SOSL query to search for a given string.
|
|
373
429
|
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_search.htm>`_)
|
|
374
430
|
|
|
375
|
-
..
|
|
431
|
+
.. version-added:: 1.1.0
|
|
376
432
|
|
|
377
433
|
:param string_to_search: The string for which to search
|
|
378
434
|
:type string_to_search: str
|
|
379
435
|
:returns: The SOSL response data in JSON format
|
|
436
|
+
:raises: :py:exc:`RuntimeError`
|
|
380
437
|
"""
|
|
381
438
|
query = 'FIND {' + string_to_search + '}'
|
|
382
439
|
query = core_utils.url_encode(query)
|
|
@@ -391,7 +448,8 @@ class Salesforce(object):
|
|
|
391
448
|
:param payload: The JSON payload with the record details
|
|
392
449
|
:type payload: dict
|
|
393
450
|
:returns: The API response from the POST request
|
|
394
|
-
:raises: :py:exc:`RuntimeError`,
|
|
451
|
+
:raises: :py:exc:`RuntimeError`,
|
|
452
|
+
:py:exc:`TypeError`
|
|
395
453
|
"""
|
|
396
454
|
# Ensure the payload is in the appropriate format
|
|
397
455
|
if not isinstance(payload, dict):
|
|
@@ -412,7 +470,8 @@ class Salesforce(object):
|
|
|
412
470
|
:param payload: The JSON payload with the record details to be updated
|
|
413
471
|
:type payload: dict
|
|
414
472
|
:returns: The API response from the PATCH request
|
|
415
|
-
:raises: :py:exc:`RuntimeError`,
|
|
473
|
+
:raises: :py:exc:`RuntimeError`,
|
|
474
|
+
:py:exc:`TypeError`
|
|
416
475
|
"""
|
|
417
476
|
# Ensure the payload is in the appropriate format
|
|
418
477
|
if not isinstance(payload, dict):
|
|
@@ -596,7 +655,8 @@ class Salesforce(object):
|
|
|
596
655
|
:param return_uri: Determines if the URI of the article should be returned rather than the ID (``False`` by default)
|
|
597
656
|
:type return_uri: bool
|
|
598
657
|
:returns: The Article ID or Article URI, or a blank string if no article is found
|
|
599
|
-
:raises: :py:exc:`ValueError
|
|
658
|
+
:raises: :py:exc:`ValueError`,
|
|
659
|
+
:py:exc:`RuntimeError`
|
|
600
660
|
"""
|
|
601
661
|
return knowledge_module.get_article_id_from_number(self.sfdc_object, article_number=article_number,
|
|
602
662
|
sobject=sobject, return_uri=return_uri)
|
|
@@ -616,6 +676,7 @@ class Salesforce(object):
|
|
|
616
676
|
:param page_num: The starting page number (``1`` by default)
|
|
617
677
|
:type page_num: int
|
|
618
678
|
:returns: The list of retrieved knowledge articles
|
|
679
|
+
:raises: :py:exc:`RuntimeError`
|
|
619
680
|
"""
|
|
620
681
|
return knowledge_module.get_articles_list(self.sfdc_object, query=query, sort=sort, order=order,
|
|
621
682
|
page_size=page_size, page_num=page_num)
|
|
@@ -629,6 +690,7 @@ class Salesforce(object):
|
|
|
629
690
|
:param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
|
|
630
691
|
:type sobject: str, None
|
|
631
692
|
:returns: The details for the knowledge article
|
|
693
|
+
:raises: :py:exc:`RuntimeError`
|
|
632
694
|
"""
|
|
633
695
|
return knowledge_module.get_article_details(self.sfdc_object, article_id=article_id, sobject=sobject)
|
|
634
696
|
|
|
@@ -680,7 +742,8 @@ class Salesforce(object):
|
|
|
680
742
|
:param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
|
|
681
743
|
:type sobject: str, None
|
|
682
744
|
:returns: The article URL as a string
|
|
683
|
-
:raises: :py:exc:`ValueError
|
|
745
|
+
:raises: :py:exc:`ValueError`,
|
|
746
|
+
:py:exc:`RuntimeError`
|
|
684
747
|
"""
|
|
685
748
|
return knowledge_module.get_article_url(self.sfdc_object, article_id=article_id,
|
|
686
749
|
article_number=article_number, sobject=sobject)
|
|
@@ -696,7 +759,9 @@ class Salesforce(object):
|
|
|
696
759
|
:param full_response: Determines if the full API response should be returned instead of the article ID (``False`` by default)
|
|
697
760
|
:type full_response: bool
|
|
698
761
|
:returns: The API response or the ID of the article draft
|
|
699
|
-
:raises: :py:exc:`ValueError`,
|
|
762
|
+
:raises: :py:exc:`ValueError`,
|
|
763
|
+
:py:exc:`TypeError`,
|
|
764
|
+
:py:exc:`RuntimeError`
|
|
700
765
|
"""
|
|
701
766
|
return knowledge_module.create_article(self.sfdc_object, article_data=article_data, sobject=sobject,
|
|
702
767
|
full_response=full_response)
|
|
@@ -714,7 +779,9 @@ class Salesforce(object):
|
|
|
714
779
|
:param include_status_code: Determines if the API response status code should be returned (``False`` by default)
|
|
715
780
|
:type include_status_code: bool
|
|
716
781
|
:returns: A Boolean indicating if the update operation was successful, and optionally the API response status code
|
|
717
|
-
:raises: :py:exc:`ValueError`,
|
|
782
|
+
:raises: :py:exc:`ValueError`,
|
|
783
|
+
:py:exc:`TypeError`,
|
|
784
|
+
:py:exc:`RuntimeError`
|
|
718
785
|
"""
|
|
719
786
|
return knowledge_module.update_article(self.sfdc_object, record_id=record_id, article_data=article_data,
|
|
720
787
|
sobject=sobject, include_status_code=include_status_code)
|
|
@@ -781,7 +848,9 @@ class Salesforce(object):
|
|
|
781
848
|
:param major_version: Determines if the published article should be a major version (``True`` by default)
|
|
782
849
|
:type major_version: bool
|
|
783
850
|
:returns: The API response from the POST request
|
|
784
|
-
:raises: :py:exc:`RuntimeError`,
|
|
851
|
+
:raises: :py:exc:`RuntimeError`,
|
|
852
|
+
:py:exc:`TypeError`,
|
|
853
|
+
:py:exc:`ValueError`
|
|
785
854
|
"""
|
|
786
855
|
return knowledge_module.publish_multiple_articles(self.sfdc_object, article_id_list=article_id_list,
|
|
787
856
|
major_version=major_version)
|
|
@@ -790,7 +859,7 @@ class Salesforce(object):
|
|
|
790
859
|
"""This method assigns a single data category for a knowledge article.
|
|
791
860
|
(`Reference <https://itsmemohit.medium.com/quick-win-15-salesforce-knowledge-rest-apis-bb0725b2040e>`_)
|
|
792
861
|
|
|
793
|
-
..
|
|
862
|
+
.. version-added:: 1.2.0
|
|
794
863
|
|
|
795
864
|
:param article_id: The ID of the article to update
|
|
796
865
|
:type article_id: str
|
|
@@ -809,7 +878,7 @@ class Salesforce(object):
|
|
|
809
878
|
"""This function archives a published knowledge article.
|
|
810
879
|
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_archive_master_version.htm>`_)
|
|
811
880
|
|
|
812
|
-
..
|
|
881
|
+
.. version-added:: 1.3.0
|
|
813
882
|
|
|
814
883
|
:param article_id: The ID of the article to archive
|
|
815
884
|
:type article_id: str
|
|
@@ -818,6 +887,18 @@ class Salesforce(object):
|
|
|
818
887
|
"""
|
|
819
888
|
return knowledge_module.archive_article(self.sfdc_object, article_id=article_id)
|
|
820
889
|
|
|
890
|
+
def delete_article_draft(self, version_id):
|
|
891
|
+
"""This function deletes an unpublished knowledge article draft.
|
|
892
|
+
|
|
893
|
+
.. version-added:: 1.4.0
|
|
894
|
+
|
|
895
|
+
:param version_id: The 15-character or 18-character ``Id`` (Knowledge Article Version ID) value
|
|
896
|
+
:type version_id: str
|
|
897
|
+
:returns: The API response from the DELETE request
|
|
898
|
+
:raises: :py:exc:`RuntimeError`
|
|
899
|
+
"""
|
|
900
|
+
return knowledge_module.delete_article_draft(self.sfdc_object, version_id=version_id)
|
|
901
|
+
|
|
821
902
|
|
|
822
903
|
def define_connection_info():
|
|
823
904
|
"""This function prompts the user for the connection information.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
:Synopsis: Defines the Knowledge-related functions associated with the Salesforce API
|
|
5
5
|
:Created By: Jeff Shurtliff
|
|
6
6
|
:Last Modified: Jeff Shurtliff
|
|
7
|
-
:Modified Date:
|
|
7
|
+
:Modified Date: 30 Jan 2026
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from . import errors
|
|
@@ -20,7 +20,7 @@ def check_for_existing_article(sfdc_object, title, sobject=None, return_id=False
|
|
|
20
20
|
(`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_,
|
|
21
21
|
`Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_)
|
|
22
22
|
|
|
23
|
-
..
|
|
23
|
+
.. version-changed:: 1.2.2
|
|
24
24
|
You can now specify whether archived articles are included in the query results.
|
|
25
25
|
|
|
26
26
|
:param sfdc_object: The instantiated SalesPyForce object
|
|
@@ -230,7 +230,7 @@ def get_article_version(sfdc_object, article_id):
|
|
|
230
230
|
def get_article_url(sfdc_object, article_id=None, article_number=None, sobject=None):
|
|
231
231
|
"""This function constructs the URL to view a knowledge article in Lightning or Classic.
|
|
232
232
|
|
|
233
|
-
..
|
|
233
|
+
.. version-changed:: 1.2.0
|
|
234
234
|
Changed when lightning URLs are defined and fixed an issue with extraneous slashes.
|
|
235
235
|
|
|
236
236
|
:param sfdc_object: The instantiated SalesPyForce object
|
|
@@ -483,7 +483,7 @@ def assign_data_category(sfdc_object, article_id, category_group_name, category_
|
|
|
483
483
|
"""This function assigns a single data category for a knowledge article.
|
|
484
484
|
(`Reference <https://itsmemohit.medium.com/quick-win-15-salesforce-knowledge-rest-apis-bb0725b2040e>`_)
|
|
485
485
|
|
|
486
|
-
..
|
|
486
|
+
.. version-added:: 1.2.0
|
|
487
487
|
|
|
488
488
|
:param sfdc_object: The instantiated SalesPyForce object
|
|
489
489
|
:type sfdc_object: class[salespyforce.Salesforce]
|
|
@@ -512,7 +512,7 @@ def archive_article(sfdc_object, article_id):
|
|
|
512
512
|
"""This function archives a published knowledge article.
|
|
513
513
|
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_archive_master_version.htm>`_)
|
|
514
514
|
|
|
515
|
-
..
|
|
515
|
+
.. version-added:: 1.3.0
|
|
516
516
|
|
|
517
517
|
:param sfdc_object: The instantiated SalesPyForce object
|
|
518
518
|
:type sfdc_object: class[salespyforce.Salesforce]
|
|
@@ -529,3 +529,19 @@ def archive_article(sfdc_object, article_id):
|
|
|
529
529
|
# Perform the API call
|
|
530
530
|
endpoint = f'/services/data/{sfdc_object.version}/knowledgeManagement/articleVersions/masterVersions/{article_id}'
|
|
531
531
|
return sfdc_object.patch(endpoint, payload)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def delete_article_draft(sfdc_object, version_id):
|
|
535
|
+
"""This function deletes an unpublished knowledge article draft.
|
|
536
|
+
|
|
537
|
+
.. version-added:: 1.4.0
|
|
538
|
+
|
|
539
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
540
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
541
|
+
:param version_id: The 15-character or 18-character ``Id`` (Knowledge Article Version ID) value
|
|
542
|
+
:type version_id: str
|
|
543
|
+
:returns: The API response from the DELETE request
|
|
544
|
+
:raises: :py:exc:`RuntimeError`
|
|
545
|
+
"""
|
|
546
|
+
endpoint = f'/services/data/{sfdc_object.version}/sobjects/Knowledge__kav/{version_id}'
|
|
547
|
+
return sfdc_object.delete(endpoint)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:Example: ``encoded_string = core_utils.encode_url(decoded_string)``
|
|
7
7
|
:Created By: Jeff Shurtliff
|
|
8
8
|
:Last Modified: Jeff Shurtliff
|
|
9
|
-
:Modified Date:
|
|
9
|
+
:Modified Date: 30 Jan 2026
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import random
|
|
@@ -23,6 +23,9 @@ from .. import errors
|
|
|
23
23
|
# Initialize the logger for this module
|
|
24
24
|
logger = log_utils.initialize_logging(__name__)
|
|
25
25
|
|
|
26
|
+
# Define constants
|
|
27
|
+
SALESFORCE_ID_SUFFIX_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'
|
|
28
|
+
|
|
26
29
|
|
|
27
30
|
def url_encode(raw_string):
|
|
28
31
|
"""This function encodes a string for use in URLs.
|
|
@@ -60,7 +63,8 @@ def get_file_type(file_path):
|
|
|
60
63
|
:param file_path: The full path to the file
|
|
61
64
|
:type file_path: str
|
|
62
65
|
:returns: The file type in string format (e.g. ``yaml`` or ``json``)
|
|
63
|
-
:raises: :py:exc:`FileNotFoundError`,
|
|
66
|
+
:raises: :py:exc:`FileNotFoundError`,
|
|
67
|
+
:py:exc:`salespyforce.errors.exceptions.UnknownFileTypeError`
|
|
64
68
|
"""
|
|
65
69
|
file_type = 'unknown'
|
|
66
70
|
if os.path.isfile(file_path):
|
|
@@ -97,6 +101,44 @@ def get_random_string(length=32, prefix_string=""):
|
|
|
97
101
|
return f"{prefix_string}{''.join([random.choice(string.ascii_letters + string.digits) for _ in range(length)])}"
|
|
98
102
|
|
|
99
103
|
|
|
104
|
+
def get_18_char_id(record_id: str) -> str:
|
|
105
|
+
"""This function converts a 15-character Salesforce record ID to its 18-character case-insensitive form.
|
|
106
|
+
|
|
107
|
+
.. version-added:: 1.4.0
|
|
108
|
+
|
|
109
|
+
:param record_id: The Salesforce record ID to convert (or return unchanged if already 18 characters)
|
|
110
|
+
:type record_id: str
|
|
111
|
+
:returns: The 18-character Salesforce record ID
|
|
112
|
+
:raises: :py:exc:`ValueError`
|
|
113
|
+
"""
|
|
114
|
+
# Ensure the provided record ID is a string
|
|
115
|
+
if not isinstance(record_id, str):
|
|
116
|
+
raise ValueError("Salesforce ID must be a string")
|
|
117
|
+
|
|
118
|
+
# Return the record ID unchanged if it is already 18 characters in length
|
|
119
|
+
if len(record_id) == 18:
|
|
120
|
+
return record_id
|
|
121
|
+
|
|
122
|
+
# Ensure the record ID is a valid 15-character value
|
|
123
|
+
if len(record_id) != 15:
|
|
124
|
+
raise ValueError("Salesforce ID must be 15 or 18 characters long")
|
|
125
|
+
|
|
126
|
+
# Define the checksum suffix (additional 3 characters)
|
|
127
|
+
suffix = ""
|
|
128
|
+
for i in range(0, 15, 5):
|
|
129
|
+
chunk = record_id[i:i + 5]
|
|
130
|
+
bitmask = 0
|
|
131
|
+
|
|
132
|
+
for index, char in enumerate(chunk):
|
|
133
|
+
if "A" <= char <= "Z":
|
|
134
|
+
bitmask |= 1 << index
|
|
135
|
+
|
|
136
|
+
suffix += SALESFORCE_ID_SUFFIX_ALPHABET[bitmask]
|
|
137
|
+
|
|
138
|
+
# Return the 18-character ID value
|
|
139
|
+
return record_id + suffix
|
|
140
|
+
|
|
141
|
+
|
|
100
142
|
def get_image_ref_id(image_url):
|
|
101
143
|
"""This function parses an image URL to identify the reference ID (refid) value.
|
|
102
144
|
(`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_rich_text_image_retrieve.htm>`_,
|