salespyforce 1.4.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.
- salespyforce/__init__.py +53 -0
- salespyforce/api.py +272 -0
- salespyforce/chatter.py +192 -0
- salespyforce/core.py +1347 -0
- salespyforce/decorators.py +53 -0
- salespyforce/errors/__init__.py +12 -0
- salespyforce/errors/exceptions.py +389 -0
- salespyforce/errors/handlers.py +53 -0
- salespyforce/knowledge.py +679 -0
- salespyforce/utils/__init__.py +10 -0
- salespyforce/utils/core_utils.py +236 -0
- salespyforce/utils/helper.py +141 -0
- salespyforce/utils/log_utils.py +265 -0
- salespyforce/utils/tests/__init__.py +8 -0
- salespyforce/utils/tests/conftest.py +202 -0
- salespyforce/utils/tests/resources.py +157 -0
- salespyforce/utils/tests/test_core_utils.py +234 -0
- salespyforce/utils/tests/test_instantiate_object.py +62 -0
- salespyforce/utils/tests/test_log_utils.py +79 -0
- salespyforce/utils/tests/test_sobjects.py +65 -0
- salespyforce/utils/tests/test_soql.py +23 -0
- salespyforce/utils/tests/test_sosl.py +29 -0
- salespyforce/utils/tests/test_version_utils.py +70 -0
- salespyforce/utils/version.py +52 -0
- salespyforce-1.4.0.dist-info/LICENSE +21 -0
- salespyforce-1.4.0.dist-info/METADATA +252 -0
- salespyforce-1.4.0.dist-info/RECORD +28 -0
- salespyforce-1.4.0.dist-info/WHEEL +4 -0
salespyforce/__init__.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
:Module: salespyforce
|
|
4
|
+
:Synopsis: This is the ``__init__`` module for the salespyforce package
|
|
5
|
+
:Created By: Jeff Shurtliff
|
|
6
|
+
:Last Modified: Jeff Shurtliff
|
|
7
|
+
:Modified Date: 08 May 2023
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from . import core
|
|
11
|
+
from .core import Salesforce
|
|
12
|
+
from .utils import version
|
|
13
|
+
|
|
14
|
+
__all__ = ['core', 'Salesforce']
|
|
15
|
+
|
|
16
|
+
# Define the package version by pulling from the highspot.utils.version module
|
|
17
|
+
__version__ = version.get_full_version()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Allow the core.define_connection_info() function to be executed directly
|
|
21
|
+
def define_connection_info():
|
|
22
|
+
"""This function prompts the user for the connection information.
|
|
23
|
+
|
|
24
|
+
:returns: The connection info in a dictionary
|
|
25
|
+
"""
|
|
26
|
+
return core.define_connection_info()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Allow the core.compile_connection_info() function to be executed directly
|
|
30
|
+
def compile_connection_info(base_url, org_id, username, password, endpoint_url,
|
|
31
|
+
client_id, client_secret, security_token):
|
|
32
|
+
"""This function compiles the connection info into a dictionary that can be consumed by the core object.
|
|
33
|
+
|
|
34
|
+
:param base_url: The base URL of the Salesforce instance
|
|
35
|
+
:type base_url: str
|
|
36
|
+
:param org_id: The Org ID of the Salesforce instance
|
|
37
|
+
:type org_id: str
|
|
38
|
+
:param username: The username of the API user
|
|
39
|
+
:type username: str
|
|
40
|
+
:param password: The password of the API user
|
|
41
|
+
:type password: str
|
|
42
|
+
:param endpoint_url: The endpoint URL for the Salesforce instance
|
|
43
|
+
:type endpoint_url: str
|
|
44
|
+
:param client_id: The Client ID for the Salesforce instance
|
|
45
|
+
:type client_id: str
|
|
46
|
+
:param client_secret: The Client Secret for the Salesforce instance
|
|
47
|
+
:type client_secret: str
|
|
48
|
+
:param security_token: The Security Token for the Salesforce instance
|
|
49
|
+
:type security_token: str
|
|
50
|
+
:returns: The connection info in a dictionary
|
|
51
|
+
"""
|
|
52
|
+
return core.compile_connection_info(base_url, org_id, username, password, endpoint_url,
|
|
53
|
+
client_id, client_secret, security_token)
|
salespyforce/api.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
:Module: salespyforce.api
|
|
4
|
+
:Synopsis: Defines the basic functions associated with the Salesforce API
|
|
5
|
+
:Created By: Jeff Shurtliff
|
|
6
|
+
:Last Modified: Jeff Shurtliff
|
|
7
|
+
:Modified Date: 03 Feb 2026
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
import requests
|
|
15
|
+
|
|
16
|
+
from . import errors
|
|
17
|
+
from .utils import core_utils, log_utils
|
|
18
|
+
|
|
19
|
+
# Define constants
|
|
20
|
+
DEFAULT_API_REQUEST_TIMEOUT = 30
|
|
21
|
+
|
|
22
|
+
# Initialize logging
|
|
23
|
+
logger = log_utils.initialize_logging(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get(
|
|
27
|
+
sfdc_object,
|
|
28
|
+
endpoint: str,
|
|
29
|
+
params: Optional[dict] = None,
|
|
30
|
+
headers: Optional[dict] = None,
|
|
31
|
+
timeout: Optional[int] = None,
|
|
32
|
+
show_full_error: bool = True,
|
|
33
|
+
return_json: bool = True,
|
|
34
|
+
):
|
|
35
|
+
"""This method performs a GET request against the Salesforce instance.
|
|
36
|
+
(`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
|
|
37
|
+
|
|
38
|
+
.. versionchanged:: 1.4.0
|
|
39
|
+
The full URL for the API call is now constructed prior to making the call. The provided URL is also
|
|
40
|
+
now evaluated to ensure it is a valid Salesforce URL. Additionally, a global constant is now leveraged
|
|
41
|
+
for the API timeout value instead of hardcoding the value. (Timeout is still **30** seconds in this version)
|
|
42
|
+
|
|
43
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
44
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
45
|
+
:param endpoint: The API endpoint to query
|
|
46
|
+
:type endpoint: str
|
|
47
|
+
:param params: The query parameters (where applicable)
|
|
48
|
+
:type params: dict, None
|
|
49
|
+
:param headers: Specific API headers to use when performing the API call
|
|
50
|
+
:type headers: dict, None
|
|
51
|
+
:param timeout: The timeout period in seconds (defaults to ``30``)
|
|
52
|
+
:type timeout: int, None
|
|
53
|
+
:param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
|
|
54
|
+
:type show_full_error: bool
|
|
55
|
+
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
56
|
+
:returns: The API response in JSON format or as a ``requests`` object
|
|
57
|
+
:raises: :py:exc:`TypeError`,
|
|
58
|
+
:py:exc:`RuntimeError`,
|
|
59
|
+
:py:exc:`salespyforce.errors.exceptions.InvalidURLError`
|
|
60
|
+
"""
|
|
61
|
+
# Define the parameters as an empty dictionary if none are provided
|
|
62
|
+
params = {} if params is None else params
|
|
63
|
+
|
|
64
|
+
# Define the headers
|
|
65
|
+
default_headers = _get_headers(sfdc_object.access_token)
|
|
66
|
+
headers = default_headers if not headers else headers
|
|
67
|
+
|
|
68
|
+
# Construct the request URL
|
|
69
|
+
url = _construct_full_query_url(endpoint, sfdc_object.instance_url)
|
|
70
|
+
|
|
71
|
+
# Define the API request timeout (using default value if not explicitly defined with parameter)
|
|
72
|
+
timeout = DEFAULT_API_REQUEST_TIMEOUT if not timeout else timeout
|
|
73
|
+
|
|
74
|
+
# Perform the API call
|
|
75
|
+
response = requests.get(url, headers=headers, params=params, timeout=timeout)
|
|
76
|
+
if response.status_code >= 300:
|
|
77
|
+
# TODO: Functionalize this segment and figure out how to improve on the approach somehow
|
|
78
|
+
if show_full_error:
|
|
79
|
+
raise RuntimeError(f'The GET request failed with a {response.status_code} status code.\n'
|
|
80
|
+
f'{response.text}')
|
|
81
|
+
else:
|
|
82
|
+
raise RuntimeError(f'The GET request failed with a {response.status_code} status code.')
|
|
83
|
+
# TODO: Leverage private function for this section across all API call functions (see TODO in api_call_with_payload)
|
|
84
|
+
if return_json:
|
|
85
|
+
response = response.json()
|
|
86
|
+
return response
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def api_call_with_payload(
|
|
90
|
+
sfdc_object,
|
|
91
|
+
method: str,
|
|
92
|
+
endpoint: str,
|
|
93
|
+
payload: dict,
|
|
94
|
+
params: Optional[dict] = None,
|
|
95
|
+
headers: Optional[dict] = None,
|
|
96
|
+
timeout: Optional[int] = None,
|
|
97
|
+
show_full_error: bool = True,
|
|
98
|
+
return_json: bool = True,
|
|
99
|
+
):
|
|
100
|
+
"""This method performs a POST call against the Salesforce instance.
|
|
101
|
+
(`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
|
|
102
|
+
|
|
103
|
+
.. versionchanged:: 1.4.0
|
|
104
|
+
The full URL for the API call is now constructed prior to making the call. The provided URL is also
|
|
105
|
+
now evaluated to ensure it is a valid Salesforce URL. Additionally, a global constant is now leveraged
|
|
106
|
+
for the API timeout value instead of hardcoding the value. (Timeout is still **30** seconds in this version)
|
|
107
|
+
|
|
108
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
109
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
110
|
+
:param method: The API method (``post``, ``put``, or ``patch``)
|
|
111
|
+
:type method: str
|
|
112
|
+
:param endpoint: The API endpoint to query
|
|
113
|
+
:type endpoint: str
|
|
114
|
+
:param payload: The payload to leverage in the API call
|
|
115
|
+
:type payload: dict
|
|
116
|
+
:param params: The query parameters (where applicable)
|
|
117
|
+
:type params: dict, None
|
|
118
|
+
:param headers: Specific API headers to use when performing the API call
|
|
119
|
+
:type headers: dict, None
|
|
120
|
+
:param timeout: The timeout period in seconds (defaults to ``30``)
|
|
121
|
+
:type timeout: int, None
|
|
122
|
+
:param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
|
|
123
|
+
:type show_full_error: bool
|
|
124
|
+
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
125
|
+
:returns: The API response in JSON format or as a ``requests`` object
|
|
126
|
+
:raises: :py:exc:`TypeError`,
|
|
127
|
+
:py:exc:`RuntimeError`,
|
|
128
|
+
:py:exc:`ValueError`,
|
|
129
|
+
:py:exc:`salespyforce.errors.exceptions.InvalidURLError`
|
|
130
|
+
"""
|
|
131
|
+
# Define the parameters as an empty dictionary if none are provided
|
|
132
|
+
params = {} if params is None else params
|
|
133
|
+
|
|
134
|
+
# Define the headers
|
|
135
|
+
default_headers = _get_headers(sfdc_object.access_token)
|
|
136
|
+
headers = default_headers if not headers else headers
|
|
137
|
+
|
|
138
|
+
# Construct the request URL
|
|
139
|
+
url = _construct_full_query_url(endpoint, sfdc_object.instance_url)
|
|
140
|
+
|
|
141
|
+
# Define the API request timeout (using default value if not explicitly defined with parameter)
|
|
142
|
+
timeout = DEFAULT_API_REQUEST_TIMEOUT if not timeout else timeout
|
|
143
|
+
|
|
144
|
+
# Perform the API call
|
|
145
|
+
if method.lower() == 'post':
|
|
146
|
+
response = requests.post(url, json=payload, headers=headers, params=params, timeout=timeout)
|
|
147
|
+
elif method.lower() == 'patch':
|
|
148
|
+
response = requests.patch(url, json=payload, headers=headers, params=params, timeout=timeout)
|
|
149
|
+
elif method.lower() == 'put':
|
|
150
|
+
response = requests.put(url, json=payload, headers=headers, params=params, timeout=timeout)
|
|
151
|
+
else:
|
|
152
|
+
raise ValueError('The API call method (POST or PATCH or PUT) must be defined')
|
|
153
|
+
|
|
154
|
+
# Examine the result
|
|
155
|
+
if response.status_code >= 300:
|
|
156
|
+
if show_full_error:
|
|
157
|
+
# TODO: Functionalize this segment and figure out how to improve on the approach somehow
|
|
158
|
+
raise RuntimeError(f'The POST request failed with a {response.status_code} status code.\n'
|
|
159
|
+
f'{response.text}')
|
|
160
|
+
else:
|
|
161
|
+
raise RuntimeError(f'The POST request failed with a {response.status_code} status code.')
|
|
162
|
+
# TODO: Break this out into a separate private function so it can be reused and standardized
|
|
163
|
+
if return_json:
|
|
164
|
+
try:
|
|
165
|
+
response = response.json()
|
|
166
|
+
except Exception as exc:
|
|
167
|
+
# TODO: log the exception rather than using a print statement
|
|
168
|
+
print(f'Failed to convert the API response to JSON format due to the following exception: {exc}')
|
|
169
|
+
return response
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def delete(
|
|
173
|
+
sfdc_object,
|
|
174
|
+
endpoint: str,
|
|
175
|
+
params: Optional[dict] = None,
|
|
176
|
+
headers: Optional[dict] = None,
|
|
177
|
+
timeout: Optional[int] = None,
|
|
178
|
+
show_full_error: bool = True,
|
|
179
|
+
return_json: bool = True):
|
|
180
|
+
"""This method performs a DELETE request against the Salesforce instance.
|
|
181
|
+
|
|
182
|
+
.. versionadded:: 1.4.0
|
|
183
|
+
|
|
184
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
185
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
186
|
+
:param endpoint: The API endpoint to query
|
|
187
|
+
:type endpoint: str
|
|
188
|
+
:param params: The query parameters (where applicable)
|
|
189
|
+
:type params: dict, None
|
|
190
|
+
:param headers: Specific API headers to use when performing the API call
|
|
191
|
+
:type headers: dict, None
|
|
192
|
+
:param timeout: The timeout period in seconds (defaults to ``30``)
|
|
193
|
+
:type timeout: int, None
|
|
194
|
+
:param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
|
|
195
|
+
:type show_full_error: bool
|
|
196
|
+
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
197
|
+
:returns: The API response in JSON format or as a ``requests`` object
|
|
198
|
+
:raises: :py:exc:`TypeError`,
|
|
199
|
+
:py:exc:`RuntimeError`,
|
|
200
|
+
:py:exc:`salespyforce.errors.exceptions.InvalidURLError`
|
|
201
|
+
"""
|
|
202
|
+
# Define the parameters as an empty dictionary if none are provided
|
|
203
|
+
params = {} if params is None else params
|
|
204
|
+
|
|
205
|
+
# Define the headers
|
|
206
|
+
default_headers = _get_headers(sfdc_object.access_token)
|
|
207
|
+
headers = default_headers if not headers else headers
|
|
208
|
+
|
|
209
|
+
# Construct the request URL
|
|
210
|
+
url = _construct_full_query_url(endpoint, sfdc_object.instance_url)
|
|
211
|
+
|
|
212
|
+
# Define the API request timeout (using default value if not explicitly defined with parameter)
|
|
213
|
+
timeout = DEFAULT_API_REQUEST_TIMEOUT if not timeout else timeout
|
|
214
|
+
|
|
215
|
+
# Perform the API call
|
|
216
|
+
response = requests.delete(url, headers=headers, params=params, timeout=timeout)
|
|
217
|
+
if response.status_code >= 300:
|
|
218
|
+
if show_full_error:
|
|
219
|
+
# TODO: Functionalize this segment and figure out how to improve on the approach somehow
|
|
220
|
+
raise RuntimeError(f'The DELETE request failed with a {response.status_code} status code.\n'
|
|
221
|
+
f'{response.text}')
|
|
222
|
+
else:
|
|
223
|
+
raise RuntimeError(f'The DELETE request failed with a {response.status_code} status code.')
|
|
224
|
+
# TODO: Leverage private function for this section across all API call functions (see TODO in api_call_with_payload)
|
|
225
|
+
if return_json:
|
|
226
|
+
response = response.json()
|
|
227
|
+
return response
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _get_headers(_access_token: str, _header_type: str = 'default') -> dict:
|
|
231
|
+
"""This function returns the appropriate HTTP headers to use for different types of API calls."""
|
|
232
|
+
headers = {
|
|
233
|
+
'content-type': 'application/json',
|
|
234
|
+
'accept-encoding': 'gzip',
|
|
235
|
+
'authorization': f'Bearer {_access_token}'
|
|
236
|
+
}
|
|
237
|
+
if _header_type == 'articles':
|
|
238
|
+
headers['accept-language'] = 'en-US'
|
|
239
|
+
return headers
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _construct_full_query_url(_endpoint: str, _instance_url: str) -> str:
|
|
243
|
+
"""This function constructs the URL to use in an API call to the Salesforce REST API.
|
|
244
|
+
|
|
245
|
+
.. versionadded:: 1.4.0
|
|
246
|
+
|
|
247
|
+
:param _endpoint: The endpoint provided when calling an API call method or function
|
|
248
|
+
:type _endpoint: str
|
|
249
|
+
:param _instance_url: The Salesforce instance URL defined when the core object was instantiated
|
|
250
|
+
:type _instance_url: str
|
|
251
|
+
:returns: The fully qualified URL
|
|
252
|
+
:raises: :py:exc:`TypeError`,
|
|
253
|
+
:py:exc:`salespyforce.errors.exceptions.InvalidURLError`
|
|
254
|
+
"""
|
|
255
|
+
# Raise an exception if the endpoint is not a string
|
|
256
|
+
if not isinstance(_endpoint, str):
|
|
257
|
+
_exc_msg = 'The provided URL must be a string and a valid Salesforce URL'
|
|
258
|
+
logger.critical(_exc_msg)
|
|
259
|
+
raise TypeError(_exc_msg)
|
|
260
|
+
|
|
261
|
+
# Construct the URL as needed by prepending the instance URL
|
|
262
|
+
if _endpoint.startswith('https://'):
|
|
263
|
+
# Only permit valid Salesforce URLs
|
|
264
|
+
if not core_utils.is_valid_salesforce_url(_endpoint):
|
|
265
|
+
raise errors.exceptions.InvalidURLError(url=_endpoint)
|
|
266
|
+
_url = _endpoint
|
|
267
|
+
else:
|
|
268
|
+
_endpoint = f'/{_endpoint}' if not _endpoint.startswith('/') else _endpoint
|
|
269
|
+
_url = f'{_instance_url}{_endpoint}'
|
|
270
|
+
|
|
271
|
+
# Return the constructed URL
|
|
272
|
+
return _url
|
salespyforce/chatter.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
:Module: salespyforce.chatter
|
|
4
|
+
:Synopsis: Defines the Chatter-related functions associated with the Salesforce Connect API
|
|
5
|
+
:Created By: Jeff Shurtliff
|
|
6
|
+
:Last Modified: Jeff Shurtliff
|
|
7
|
+
:Modified Date: 03 Feb 2026
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from . import errors
|
|
15
|
+
from .utils import log_utils
|
|
16
|
+
|
|
17
|
+
# Initialize logging
|
|
18
|
+
logger = log_utils.initialize_logging(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_site_endpoint_segment(_site_id: Optional[str] = None) -> str:
|
|
22
|
+
"""This function constructs the endpoint segment when querying a specific Experience Cloud site.
|
|
23
|
+
|
|
24
|
+
:param _site_id: The Site ID of the Experience Cloud site
|
|
25
|
+
:type _site_id: str, None
|
|
26
|
+
:returns: The API endpoint segment (or a blank string if no Site ID was provided)
|
|
27
|
+
"""
|
|
28
|
+
_endpoint_segment = f'/connect/communities/{_site_id}' if _site_id else ''
|
|
29
|
+
return _endpoint_segment
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_my_news_feed(sfdc_object, site_id: Optional[str] = None):
|
|
33
|
+
"""This function retrieves the news feed for the user calling the function.
|
|
34
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_news_feed.htm>`_)
|
|
35
|
+
|
|
36
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
37
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
38
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
39
|
+
:type site_id: str, None
|
|
40
|
+
:returns: The news feed data
|
|
41
|
+
:raises: :py:exc:`RuntimeError`
|
|
42
|
+
"""
|
|
43
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
44
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feeds/news/me/feed-elements'
|
|
45
|
+
return sfdc_object.get(endpoint)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_user_news_feed(sfdc_object, user_id: str, site_id: Optional[str] = None):
|
|
49
|
+
"""This function retrieves another user's news feed.
|
|
50
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_user_profile_feed.htm>`_)
|
|
51
|
+
|
|
52
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
53
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
54
|
+
:param user_id: The ID of the user whose feed you wish to return
|
|
55
|
+
:type user_id: str
|
|
56
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
57
|
+
:type site_id: str, None
|
|
58
|
+
:returns: The news feed data
|
|
59
|
+
:raises: :py:exc:`RuntimeError`
|
|
60
|
+
"""
|
|
61
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
62
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feeds/user-profile/{user_id}/feed-elements'
|
|
63
|
+
return sfdc_object.get(endpoint)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_group_feed(sfdc_object, group_id: str, site_id: Optional[str] = None):
|
|
67
|
+
"""This function retrieves a group's news feed.
|
|
68
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_group_feed.htm>`_)
|
|
69
|
+
|
|
70
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
71
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
72
|
+
:param group_id: The ID of the group whose feed you wish to return
|
|
73
|
+
:type group_id: str
|
|
74
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
75
|
+
:type site_id: str, None
|
|
76
|
+
:returns: The news feed data
|
|
77
|
+
:raises: :py:exc:`RuntimeError`
|
|
78
|
+
"""
|
|
79
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
80
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feeds/record/{group_id}/feed-elements'
|
|
81
|
+
return sfdc_object.get(endpoint)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def post_feed_item(
|
|
85
|
+
sfdc_object,
|
|
86
|
+
subject_id: str,
|
|
87
|
+
message_text: Optional[str] = None,
|
|
88
|
+
message_segments: Optional[list] = None,
|
|
89
|
+
site_id: Optional[str] = None,
|
|
90
|
+
created_by_id: Optional[str] = None,
|
|
91
|
+
):
|
|
92
|
+
"""This function publishes a new Chatter feed item.
|
|
93
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_post_feed_item.htm>`_)
|
|
94
|
+
|
|
95
|
+
.. versionchanged:: 1.4.0
|
|
96
|
+
The function now raises the :py:exc:`salespyforce.errors.exceptions.MissingRequiredDataError` exception rather than the
|
|
97
|
+
generic :py:exc:`RuntimeError` exception.
|
|
98
|
+
|
|
99
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
100
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
101
|
+
:param subject_id: The Subject ID against which to publish the feed item (e.g. ``0F9B000000000W2``)
|
|
102
|
+
:type subject_id: str
|
|
103
|
+
:param message_text: Plaintext to be used as the message body
|
|
104
|
+
:type message_segments: str, None
|
|
105
|
+
:param message_segments: Collection of message segments to use instead of a plaintext message
|
|
106
|
+
:type message_segments: list, None
|
|
107
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
108
|
+
:type site_id: str, None
|
|
109
|
+
:param created_by_id: The ID of the user to impersonate (**Experimental**)
|
|
110
|
+
:type created_by_id: str, None
|
|
111
|
+
:returns: The response of the POST request
|
|
112
|
+
:raises: :py:exc:`RuntimeError`,
|
|
113
|
+
:py:exc:`salespyforce.errors.exceptions.MissingRequiredDataError`
|
|
114
|
+
"""
|
|
115
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
116
|
+
if not any((message_text, message_segments)):
|
|
117
|
+
raise errors.exceptions.MissingRequiredDataError('Message text or message segments are required to post a feed item.')
|
|
118
|
+
if not message_segments:
|
|
119
|
+
message_segments = _construct_simple_message_segment(message_text)
|
|
120
|
+
body = {
|
|
121
|
+
'body': {
|
|
122
|
+
'messageSegments': message_segments
|
|
123
|
+
},
|
|
124
|
+
'feedElementType': 'FeedItem',
|
|
125
|
+
'subjectId': subject_id,
|
|
126
|
+
}
|
|
127
|
+
if created_by_id:
|
|
128
|
+
body['createdById'] = created_by_id
|
|
129
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feed-elements?' \
|
|
130
|
+
f'feedElementType=FeedItem&subjectId={subject_id}'
|
|
131
|
+
return sfdc_object.post(endpoint=endpoint, payload=body)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def post_comment(
|
|
135
|
+
sfdc_object,
|
|
136
|
+
feed_element_id: str,
|
|
137
|
+
message_text: Optional[str] = None,
|
|
138
|
+
message_segments: Optional[list] = None,
|
|
139
|
+
site_id: Optional[str] = None,
|
|
140
|
+
created_by_id: Optional[str] = None,
|
|
141
|
+
):
|
|
142
|
+
"""This function publishes a comment on a Chatter feed item.
|
|
143
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_post_comment_to_feed_element.htm>`_)
|
|
144
|
+
|
|
145
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
146
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
147
|
+
:param feed_element_id: The ID of the feed element on which to post the comment
|
|
148
|
+
:type feed_element_id: str
|
|
149
|
+
:param message_text: Plaintext to be used as the message body
|
|
150
|
+
:type message_text: str, None
|
|
151
|
+
:param message_segments: Collection of message segments to use instead of a plaintext message
|
|
152
|
+
:type message_segments: list, None
|
|
153
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
154
|
+
:type site_id: str, None
|
|
155
|
+
:param created_by_id: The ID of the user to impersonate (**Experimental**)
|
|
156
|
+
:type created_by_id: str, None
|
|
157
|
+
:returns: The response of the POST request
|
|
158
|
+
:raises: :py:exc:`RuntimeError`
|
|
159
|
+
"""
|
|
160
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
161
|
+
if not any((message_text, message_segments)):
|
|
162
|
+
raise RuntimeError('Message text or message segments are required to post a feed comment.')
|
|
163
|
+
|
|
164
|
+
if not message_segments:
|
|
165
|
+
message_segments = _construct_simple_message_segment(message_text)
|
|
166
|
+
body = {
|
|
167
|
+
'body': {
|
|
168
|
+
'messageSegments': message_segments
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if created_by_id:
|
|
172
|
+
# noinspection PyTypeChecker
|
|
173
|
+
body['createdById'] = created_by_id
|
|
174
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feed-elements/' \
|
|
175
|
+
f'{feed_element_id}/capabilities/comments/items'
|
|
176
|
+
return sfdc_object.post(endpoint=endpoint, payload=body)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _construct_simple_message_segment(_message_text: str) -> list:
|
|
180
|
+
"""This function constructs a simple message segments collection to be used in an API payload.
|
|
181
|
+
|
|
182
|
+
:param _message_text: The plaintext message to be embedded in a message segment.
|
|
183
|
+
:type _message_text: str
|
|
184
|
+
:returns: The constructed message segments payload
|
|
185
|
+
"""
|
|
186
|
+
_message_segments = [
|
|
187
|
+
{
|
|
188
|
+
'type': 'text',
|
|
189
|
+
'text': _message_text
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
return _message_segments
|