salespyforce 1.4.0.dev0__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 +129 -0
- salespyforce/chatter.py +167 -0
- salespyforce/core.py +872 -0
- salespyforce/errors/__init__.py +12 -0
- salespyforce/errors/exceptions.py +389 -0
- salespyforce/errors/handlers.py +15 -0
- salespyforce/knowledge.py +531 -0
- salespyforce/utils/__init__.py +10 -0
- salespyforce/utils/core_utils.py +152 -0
- salespyforce/utils/helper.py +140 -0
- salespyforce/utils/log_utils.py +264 -0
- salespyforce/utils/tests/__init__.py +8 -0
- salespyforce/utils/tests/resources.py +157 -0
- salespyforce/utils/tests/test_instantiate_object.py +49 -0
- salespyforce/utils/tests/test_sobjects.py +58 -0
- salespyforce/utils/tests/test_soql.py +23 -0
- salespyforce/utils/tests/test_sosl.py +29 -0
- salespyforce/utils/version.py +52 -0
- salespyforce-1.4.0.dev0.dist-info/LICENSE +21 -0
- salespyforce-1.4.0.dev0.dist-info/METADATA +253 -0
- salespyforce-1.4.0.dev0.dist-info/RECORD +23 -0
- salespyforce-1.4.0.dev0.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,129 @@
|
|
|
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: 17 Feb 2023
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
from .utils import log_utils
|
|
13
|
+
|
|
14
|
+
# Initialize logging
|
|
15
|
+
logger = log_utils.initialize_logging(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get(sfdc_object, endpoint, params=None, headers=None, timeout=30, show_full_error=True, return_json=True):
|
|
19
|
+
"""This method performs a GET request against the Salesforce instance.
|
|
20
|
+
(`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
|
|
21
|
+
|
|
22
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
23
|
+
:param endpoint: The API endpoint to query
|
|
24
|
+
:type endpoint: str
|
|
25
|
+
:param params: The query parameters (where applicable)
|
|
26
|
+
:type params: dict, None
|
|
27
|
+
:param headers: Specific API headers to use when performing the API call
|
|
28
|
+
:type headers: dict, None
|
|
29
|
+
:param timeout: The timeout period in seconds (defaults to ``30``)
|
|
30
|
+
:type timeout: int, str, None
|
|
31
|
+
:param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
|
|
32
|
+
:type show_full_error: bool
|
|
33
|
+
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
34
|
+
:returns: The API response in JSON format or as a ``requests`` object
|
|
35
|
+
"""
|
|
36
|
+
# Define the parameters as an empty dictionary if none are provided
|
|
37
|
+
params = {} if params is None else params
|
|
38
|
+
|
|
39
|
+
# Define the headers
|
|
40
|
+
default_headers = _get_headers(sfdc_object.access_token)
|
|
41
|
+
headers = default_headers if not headers else headers
|
|
42
|
+
|
|
43
|
+
# Make sure the endpoint begins with a slash
|
|
44
|
+
endpoint = f'/{endpoint}' if not endpoint.startswith('/') else endpoint
|
|
45
|
+
|
|
46
|
+
# Perform the API call
|
|
47
|
+
response = requests.get(f'{sfdc_object.instance_url}{endpoint}', headers=headers, params=params, timeout=timeout)
|
|
48
|
+
if response.status_code >= 300:
|
|
49
|
+
if show_full_error:
|
|
50
|
+
raise RuntimeError(f'The GET request failed with a {response.status_code} status code.\n'
|
|
51
|
+
f'{response.text}')
|
|
52
|
+
else:
|
|
53
|
+
raise RuntimeError(f'The GET request failed with a {response.status_code} status code.')
|
|
54
|
+
if return_json:
|
|
55
|
+
response = response.json()
|
|
56
|
+
return response
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def api_call_with_payload(sfdc_object, method, endpoint, payload, params=None, headers=None, timeout=30,
|
|
60
|
+
show_full_error=True, return_json=True):
|
|
61
|
+
"""This method performs a POST call against the Salesforce instance.
|
|
62
|
+
(`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
|
|
63
|
+
|
|
64
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
65
|
+
:param method: The API method (``post``, ``put``, or ``patch``)
|
|
66
|
+
:type method: str
|
|
67
|
+
:param endpoint: The API endpoint to query
|
|
68
|
+
:type endpoint: str
|
|
69
|
+
:param payload: The payload to leverage in the API call
|
|
70
|
+
:type payload: dict
|
|
71
|
+
:param params: The query parameters (where applicable)
|
|
72
|
+
:type params: dict, None
|
|
73
|
+
:param headers: Specific API headers to use when performing the API call
|
|
74
|
+
:type headers: dict, None
|
|
75
|
+
:param timeout: The timeout period in seconds (defaults to ``30``)
|
|
76
|
+
:type timeout: int, str, None
|
|
77
|
+
:param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
|
|
78
|
+
:type show_full_error: bool
|
|
79
|
+
:param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
|
|
80
|
+
:returns: The API response in JSON format or as a ``requests`` object
|
|
81
|
+
"""
|
|
82
|
+
# Define the parameters as an empty dictionary if none are provided
|
|
83
|
+
params = {} if params is None else params
|
|
84
|
+
|
|
85
|
+
# Define the headers
|
|
86
|
+
default_headers = _get_headers(sfdc_object.access_token)
|
|
87
|
+
headers = default_headers if not headers else headers
|
|
88
|
+
|
|
89
|
+
# Make sure the endpoint begins with a slash
|
|
90
|
+
endpoint = f'/{endpoint}' if not endpoint.startswith('/') else endpoint
|
|
91
|
+
|
|
92
|
+
# Perform the API call
|
|
93
|
+
if method.lower() == 'post':
|
|
94
|
+
response = requests.post(f'{sfdc_object.instance_url}{endpoint}', json=payload, headers=headers, params=params,
|
|
95
|
+
timeout=timeout)
|
|
96
|
+
elif method.lower() == 'patch':
|
|
97
|
+
response = requests.patch(f'{sfdc_object.instance_url}{endpoint}', json=payload, headers=headers, params=params,
|
|
98
|
+
timeout=timeout)
|
|
99
|
+
elif method.lower() == 'put':
|
|
100
|
+
response = requests.put(f'{sfdc_object.instance_url}{endpoint}', json=payload, headers=headers, params=params,
|
|
101
|
+
timeout=timeout)
|
|
102
|
+
else:
|
|
103
|
+
raise ValueError('The API call method (POST or PATCH OR PUT) must be defined.')
|
|
104
|
+
|
|
105
|
+
# Examine the result
|
|
106
|
+
if response.status_code >= 300:
|
|
107
|
+
if show_full_error:
|
|
108
|
+
raise RuntimeError(f'The POST request failed with a {response.status_code} status code.\n'
|
|
109
|
+
f'{response.text}')
|
|
110
|
+
else:
|
|
111
|
+
raise RuntimeError(f'The POST request failed with a {response.status_code} status code.')
|
|
112
|
+
if return_json:
|
|
113
|
+
try:
|
|
114
|
+
response = response.json()
|
|
115
|
+
except Exception as exc:
|
|
116
|
+
print(f'Failed to convert the API response to JSON format due to the following exception: {exc}')
|
|
117
|
+
return response
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _get_headers(_access_token, _header_type='default'):
|
|
121
|
+
"""This function returns the appropriate HTTP headers to use for different types of API calls."""
|
|
122
|
+
headers = {
|
|
123
|
+
'content-type': 'application/json',
|
|
124
|
+
'accept-encoding': 'gzip',
|
|
125
|
+
'authorization': f'Bearer {_access_token}'
|
|
126
|
+
}
|
|
127
|
+
if _header_type == 'articles':
|
|
128
|
+
headers['accept-language'] = 'en-US'
|
|
129
|
+
return headers
|
salespyforce/chatter.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
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: 13 Mar 2023
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .utils import log_utils
|
|
11
|
+
|
|
12
|
+
# Initialize logging
|
|
13
|
+
logger = log_utils.initialize_logging(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _get_site_endpoint_segment(_site_id=None):
|
|
17
|
+
"""This function constructs the endpoint segment when querying a specific Experience Cloud site.
|
|
18
|
+
|
|
19
|
+
:param _site_id: The Site ID of the Experience Cloud site
|
|
20
|
+
:type _site_id: str
|
|
21
|
+
:returns: The API endpoint segment (or a blank string if no Site ID was provided)
|
|
22
|
+
"""
|
|
23
|
+
_endpoint_segment = f'/connect/communities/{_site_id}' if _site_id else ''
|
|
24
|
+
return _endpoint_segment
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_my_news_feed(sfdc_object, site_id=None):
|
|
28
|
+
"""This function retrieves the news feed for the user calling the function.
|
|
29
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_news_feed.htm>`_)
|
|
30
|
+
|
|
31
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
32
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
33
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
34
|
+
:type site_id: str, None
|
|
35
|
+
:returns: The news feed data
|
|
36
|
+
:raises: :py:exc:`RuntimeError`
|
|
37
|
+
"""
|
|
38
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
39
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feeds/news/me/feed-elements'
|
|
40
|
+
return sfdc_object.get(endpoint)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_user_news_feed(sfdc_object, user_id, site_id=None):
|
|
44
|
+
"""This function retrieves another user's news feed.
|
|
45
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_user_profile_feed.htm>`_)
|
|
46
|
+
|
|
47
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
48
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
49
|
+
:param user_id: The ID of the user whose feed you wish to return
|
|
50
|
+
:type user_id: str
|
|
51
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
52
|
+
:type site_id: str, None
|
|
53
|
+
:returns: The news feed data
|
|
54
|
+
:raises: :py:exc:`RuntimeError`
|
|
55
|
+
"""
|
|
56
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
57
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feeds/user-profile/{user_id}/feed-elements'
|
|
58
|
+
return sfdc_object.get(endpoint)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_group_feed(sfdc_object, group_id, site_id=None):
|
|
62
|
+
"""This function retrieves a group's news feed.
|
|
63
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_group_feed.htm>`_)
|
|
64
|
+
|
|
65
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
66
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
67
|
+
:param group_id: The ID of the group whose feed you wish to return
|
|
68
|
+
:type group_id: str
|
|
69
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
70
|
+
:type site_id: str, None
|
|
71
|
+
:returns: The news feed data
|
|
72
|
+
:raises: :py:exc:`RuntimeError`
|
|
73
|
+
"""
|
|
74
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
75
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feeds/record/{group_id}/feed-elements'
|
|
76
|
+
return sfdc_object.get(endpoint)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def post_feed_item(sfdc_object, subject_id, message_text=None, message_segments=None, site_id=None, created_by_id=None):
|
|
80
|
+
"""This function publishes a new Chatter feed item.
|
|
81
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_post_feed_item.htm>`_)
|
|
82
|
+
|
|
83
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
84
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
85
|
+
:param subject_id: The Subject ID against which to publish the feed item (e.g. ``0F9B000000000W2``)
|
|
86
|
+
:type subject_id: str
|
|
87
|
+
:param message_text: Plaintext to be used as the message body
|
|
88
|
+
:type message_segments: str, None
|
|
89
|
+
:param message_segments: Collection of message segments to use instead of a plaintext message
|
|
90
|
+
:type message_segments: list, None
|
|
91
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
92
|
+
:type site_id: str, None
|
|
93
|
+
:param created_by_id: The ID of the user to impersonate (**Experimental**)
|
|
94
|
+
:type created_by_id: str, None
|
|
95
|
+
:returns: The response of the POST request
|
|
96
|
+
:raises: :py:exc:`RuntimeError`
|
|
97
|
+
"""
|
|
98
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
99
|
+
if not any((message_text, message_segments)):
|
|
100
|
+
raise RuntimeError('Message text or message segments are required to post a feed item.')
|
|
101
|
+
if not message_segments:
|
|
102
|
+
message_segments = _construct_simple_message_segment(message_text)
|
|
103
|
+
body = {
|
|
104
|
+
'body': {
|
|
105
|
+
'messageSegments': message_segments
|
|
106
|
+
},
|
|
107
|
+
'feedElementType': 'FeedItem',
|
|
108
|
+
'subjectId': subject_id,
|
|
109
|
+
}
|
|
110
|
+
if created_by_id:
|
|
111
|
+
body['createdById'] = created_by_id
|
|
112
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feed-elements?' \
|
|
113
|
+
f'feedElementType=FeedItem&subjectId={subject_id}'
|
|
114
|
+
return sfdc_object.post(endpoint=endpoint, payload=body)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def post_comment(sfdc_object, feed_element_id, message_text=None, message_segments=None, site_id=None, created_by_id=None):
|
|
118
|
+
"""This function publishes a comment on a Chatter feed item.
|
|
119
|
+
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_post_comment_to_feed_element.htm>`_)
|
|
120
|
+
|
|
121
|
+
:param sfdc_object: The instantiated SalesPyForce object
|
|
122
|
+
:type sfdc_object: class[salespyforce.Salesforce]
|
|
123
|
+
:param feed_element_id: The ID of the feed element on which to post the comment
|
|
124
|
+
:type feed_element_id: str
|
|
125
|
+
:param message_text: Plaintext to be used as the message body
|
|
126
|
+
:type message_segments: str, None
|
|
127
|
+
:param message_segments: Collection of message segments to use instead of a plaintext message
|
|
128
|
+
:type message_segments: list, None
|
|
129
|
+
:param site_id: The ID of an Experience Cloud site against which to query (optional)
|
|
130
|
+
:type site_id: str, None
|
|
131
|
+
:param created_by_id: The ID of the user to impersonate (**Experimental**)
|
|
132
|
+
:type created_by_id: str, None
|
|
133
|
+
:returns: The response of the POST request
|
|
134
|
+
:raises: :py:exc:`RuntimeError`
|
|
135
|
+
"""
|
|
136
|
+
site_segment = _get_site_endpoint_segment(site_id)
|
|
137
|
+
if not any((message_text, message_segments)):
|
|
138
|
+
raise RuntimeError('Message text or message segments are required to post a feed comment.')
|
|
139
|
+
if not message_segments:
|
|
140
|
+
message_segments = _construct_simple_message_segment(message_text)
|
|
141
|
+
body = {
|
|
142
|
+
'body': {
|
|
143
|
+
'messageSegments': message_segments
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if created_by_id:
|
|
147
|
+
body['createdById'] = created_by_id
|
|
148
|
+
endpoint = f'/services/data/{sfdc_object.version}{site_segment}/chatter/feed-elements/' \
|
|
149
|
+
f'{feed_element_id}/capabilities/comments/items'
|
|
150
|
+
return sfdc_object.post(endpoint=endpoint, payload=body)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _construct_simple_message_segment(_message_text):
|
|
154
|
+
"""This function constructs a simple message segments collection to be used in an API payload.
|
|
155
|
+
|
|
156
|
+
:param _message_text: The plaintext message to be embedded in a message segment.
|
|
157
|
+
:type _message_text: str
|
|
158
|
+
:returns: The constructed message segments payload
|
|
159
|
+
"""
|
|
160
|
+
_message_segments = [
|
|
161
|
+
{
|
|
162
|
+
'type': 'text',
|
|
163
|
+
'text': _message_text
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
return _message_segments
|
|
167
|
+
|