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/core.py ADDED
@@ -0,0 +1,872 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ :Module: salespyforce.core
4
+ :Synopsis: This module performs the core Salesforce-related operations
5
+ :Usage: ``from salespyforce import Salesforce``
6
+ :Example: ``sfdc = Salesforce(helper=helper_file_path)``
7
+ :Created By: Jeff Shurtliff
8
+ :Last Modified: Jeff Shurtliff
9
+ :Modified Date: 17 Nov 2025
10
+ """
11
+
12
+ import re
13
+
14
+ import requests
15
+
16
+ from . import api, errors
17
+ from . import chatter as chatter_module
18
+ from . import knowledge as knowledge_module
19
+ from .utils import core_utils, log_utils
20
+ from .utils.helper import get_helper_settings
21
+
22
+ # Define constants
23
+ FALLBACK_SFDC_API_VERSION = '65.0' # Used if querying the org for the version fails
24
+
25
+ # Initialize logging
26
+ logger = log_utils.initialize_logging(__name__)
27
+
28
+
29
+ class Salesforce(object):
30
+ """This is the class for the core object leveraged in this module."""
31
+ # Define the function that initializes the object instance (i.e. instantiates the object)
32
+ def __init__(self, connection_info=None, version=None, base_url=None, org_id=None, username=None,
33
+ password=None, endpoint_url=None, client_id=None, client_secret=None, security_token=None, helper=None):
34
+ """This method instantiates the core Salesforce object.
35
+
36
+ .. version-changed:: 1.4.0
37
+ The authorized Salesforce org is now queried to determine the latest API version to leverage unless
38
+ explicitly defined with the ``version`` parameter when instantiating the object.
39
+
40
+ :param connection_info: The information for connecting to the Salesforce instance
41
+ :type connection_info: dict, None
42
+ :param version: The Salesforce API version to utilize (uses latest version from org if not explicitly defined)
43
+ :type version: str, None
44
+ :param base_url: The base URL of the Salesforce instance
45
+ :type base_url: str, None
46
+ :param org_id: The Org ID of the Salesforce instance
47
+ :type org_id: str, None
48
+ :param username: The username of the API user
49
+ :type username: str, None
50
+ :param password: The password of the API user
51
+ :type password: str, None
52
+ :param endpoint_url: The endpoint URL for the Salesforce instance
53
+ :type endpoint_url: str, None
54
+ :param client_id: The Client ID for the Salesforce instance
55
+ :type client_id: str, None
56
+ :param client_secret: The Client Secret for the Salesforce instance
57
+ :type client_secret: str, None
58
+ :param security_token: The Security Token for the Salesforce instance
59
+ :type security_token: str, None
60
+ :param helper: The file path of a helper file
61
+ :type helper: str, None
62
+ :returns: The instantiated object
63
+ :raises: :py:exc:`TypeError`
64
+ """
65
+ # Define the default settings
66
+ self._helper_settings = {}
67
+
68
+ # Check for provided connection info
69
+ if connection_info is None:
70
+ # Check for a supplied helper file
71
+ if helper:
72
+ # Parse the helper file contents
73
+ self.helper_path = helper
74
+ if any((isinstance(helper, tuple), isinstance(helper, list), isinstance(helper, set))):
75
+ helper_file_path, helper_file_type = helper
76
+ elif isinstance(helper, str):
77
+ helper_file_path, helper_file_type = (helper, 'yaml')
78
+ elif isinstance(helper, dict):
79
+ helper_file_path, helper_file_type = helper.values()
80
+ else:
81
+ error_msg = "The 'helper' argument can only be supplied as tuple, string, list, set or dict."
82
+ logger.error(error_msg)
83
+ raise TypeError(error_msg)
84
+ self._helper_settings = get_helper_settings(helper_file_path, helper_file_type)
85
+ connection_info = self._parse_helper_connection_info()
86
+ elif not any((base_url, org_id, username, password, endpoint_url, client_id, client_secret, security_token)):
87
+ # Prompt for the connection info if not defined
88
+ connection_info = define_connection_info()
89
+ else:
90
+ # Compile the connection info from the provided parameters
91
+ connection_info = compile_connection_info(base_url, org_id, username, password, endpoint_url,
92
+ client_id, client_secret, security_token)
93
+
94
+ # Get the connection information used to connect to the instance
95
+ self.connection_info = connection_info if connection_info is not None else self._get_empty_connection_info()
96
+
97
+ # Define the base URL value
98
+ self.base_url = self.connection_info.get('base_url')
99
+
100
+ # Define the connection response data variables
101
+ auth_response = self.connect()
102
+ self.access_token = auth_response.get('access_token')
103
+ self.instance_url = auth_response.get('instance_url')
104
+ self.signature = auth_response.get('signature')
105
+
106
+ # Define the version with explicitly provided version or by querying the Salesforce org
107
+ self.version = f'v{version}' if version else f'v{self.get_latest_api_version()}'
108
+
109
+ # Import inner object classes so their methods can be called from the primary object
110
+ self.chatter = self._import_chatter_class()
111
+ self.knowledge = self._import_knowledge_class()
112
+
113
+ def _import_chatter_class(self):
114
+ """This method allows the :py:class:`salespyforce.core.Salesforce.Chatter` class to be utilized in the core object."""
115
+ return Salesforce.Chatter(self)
116
+
117
+ def _import_knowledge_class(self):
118
+ """This method allows the :py:class:`salespyforce.core.Salesforce.Knowledge` class to be utilized in the core object."""
119
+ return Salesforce.Knowledge(self)
120
+
121
+ @staticmethod
122
+ def _get_empty_connection_info():
123
+ """This method returns an empty connection_info dictionary with all blank values."""
124
+ _connection_info = {}
125
+ _fields = ['username', 'password', 'base_url', 'endpoint_url',
126
+ 'client_key', 'client_secret', 'org_id', 'security_token']
127
+ for _field in _fields:
128
+ _connection_info[_field] = ''
129
+ return _connection_info
130
+
131
+ def _parse_helper_connection_info(self):
132
+ """This method parses the helper content to populate the connection info."""
133
+ _connection_info = {}
134
+ _fields = ['username', 'password', 'base_url', 'endpoint_url',
135
+ 'client_key', 'client_secret', 'org_id', 'security_token']
136
+ for _field in _fields:
137
+ if _field in self._helper_settings['connection']:
138
+ _connection_info[_field] = self._helper_settings['connection'][_field]
139
+ return _connection_info
140
+
141
+ def _get_headers(self, _header_type='default'):
142
+ """This method returns the appropriate HTTP headers to use for different types of API calls."""
143
+ return api._get_headers(_access_token=self.access_token, _header_type=_header_type)
144
+
145
+ def connect(self):
146
+ """This method connects to the Salesforce instance to obtain the access token.
147
+ (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
148
+
149
+ :returns: The API call response with the authorization information
150
+ """
151
+ params = {
152
+ 'grant_type': 'password',
153
+ 'client_id': self.connection_info.get('client_key'),
154
+ 'client_secret': self.connection_info.get('client_secret'),
155
+ 'username': self.connection_info.get('username'),
156
+ 'password': f'{self.connection_info.get("password")}{self.connection_info.get("security_token")}'
157
+ }
158
+ response = requests.post(self.connection_info.get('endpoint_url'), params=params)
159
+ if response.status_code != 200:
160
+ raise RuntimeError(f'Failed to connect to the Salesforce instance.\n{response.text}')
161
+ return response.json()
162
+
163
+ def get(self, endpoint, params=None, headers=None, timeout=30, show_full_error=True, return_json=True):
164
+ """This method performs a GET request against the Salesforce instance.
165
+ (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
166
+
167
+ :param endpoint: The API endpoint to query
168
+ :type endpoint: str
169
+ :param params: The query parameters (where applicable)
170
+ :type params: dict, None
171
+ :param headers: Specific API headers to use when performing the API call
172
+ :type headers: dict, None
173
+ :param timeout: The timeout period in seconds (defaults to ``30``)
174
+ :type timeout: int, str, None
175
+ :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
176
+ :type show_full_error: bool
177
+ :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
178
+ :returns: The API response in JSON format or as a ``requests`` object
179
+ """
180
+ return api.get(self, endpoint=endpoint, params=params, headers=headers, timeout=timeout,
181
+ show_full_error=show_full_error, return_json=return_json)
182
+
183
+ def api_call_with_payload(self, method, endpoint, payload, params=None, headers=None, timeout=30,
184
+ show_full_error=True, return_json=True):
185
+ """This method performs a POST call against the Salesforce instance.
186
+ (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
187
+
188
+ :param method: The API method (``post``, ``put``, or ``patch``)
189
+ :type method: str
190
+ :param endpoint: The API endpoint to query
191
+ :type endpoint: str
192
+ :param payload: The payload to leverage in the API call
193
+ :type payload: dict
194
+ :param params: The query parameters (where applicable)
195
+ :type params: dict, None
196
+ :param headers: Specific API headers to use when performing the API call
197
+ :type headers: dict, None
198
+ :param timeout: The timeout period in seconds (defaults to ``30``)
199
+ :type timeout: int, str, None
200
+ :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
201
+ :type show_full_error: bool
202
+ :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
203
+ :returns: The API response in JSON format or as a ``requests`` object
204
+ """
205
+ return api.api_call_with_payload(self, method=method, endpoint=endpoint, payload=payload, params=params,
206
+ headers=headers, timeout=timeout, show_full_error=show_full_error,
207
+ return_json=return_json)
208
+
209
+ def post(self, endpoint, payload, params=None, headers=None, timeout=30, show_full_error=True, return_json=True):
210
+ """This method performs a POST call against the Salesforce instance.
211
+ (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
212
+
213
+ :param endpoint: The API endpoint to query
214
+ :type endpoint: str
215
+ :param payload: The payload to leverage in the API call
216
+ :type payload: dict
217
+ :param params: The query parameters (where applicable)
218
+ :type params: dict, None
219
+ :param headers: Specific API headers to use when performing the API call
220
+ :type headers: dict, None
221
+ :param timeout: The timeout period in seconds (defaults to ``30``)
222
+ :type timeout: int, str, None
223
+ :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
224
+ :type show_full_error: bool
225
+ :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
226
+ :returns: The API response in JSON format or as a ``requests`` object
227
+ """
228
+ return api.api_call_with_payload(self, 'post', endpoint=endpoint, payload=payload, params=params,
229
+ headers=headers, timeout=timeout, show_full_error=show_full_error,
230
+ return_json=return_json)
231
+
232
+ def patch(self, endpoint, payload, params=None, headers=None, timeout=30, show_full_error=True, return_json=False):
233
+ """This method performs a PATCH call against the Salesforce instance.
234
+ (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
235
+
236
+ :param endpoint: The API endpoint to query
237
+ :type endpoint: str
238
+ :param payload: The payload to leverage in the API call
239
+ :type payload: dict
240
+ :param params: The query parameters (where applicable)
241
+ :type params: dict, None
242
+ :param headers: Specific API headers to use when performing the API call
243
+ :type headers: dict, None
244
+ :param timeout: The timeout period in seconds (defaults to ``30``)
245
+ :type timeout: int, str, None
246
+ :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
247
+ :type show_full_error: bool
248
+ :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
249
+ :returns: The API response in JSON format or as a ``requests`` object
250
+ """
251
+ return api.api_call_with_payload(self, 'patch', endpoint=endpoint, payload=payload, params=params,
252
+ headers=headers, timeout=timeout, show_full_error=show_full_error,
253
+ return_json=return_json)
254
+
255
+ def put(self, endpoint, payload, params=None, headers=None, timeout=30, show_full_error=True, return_json=True):
256
+ """This method performs a PUT call against the Salesforce instance.
257
+ (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_)
258
+
259
+ :param endpoint: The API endpoint to query
260
+ :type endpoint: str
261
+ :param payload: The payload to leverage in the API call
262
+ :type payload: dict
263
+ :param params: The query parameters (where applicable)
264
+ :type params: dict, None
265
+ :param headers: Specific API headers to use when performing the API call
266
+ :type headers: dict, None
267
+ :param timeout: The timeout period in seconds (defaults to ``30``)
268
+ :type timeout: int, str, None
269
+ :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``)
270
+ :type show_full_error: bool
271
+ :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``)
272
+ :returns: The API response in JSON format or as a ``requests`` object
273
+ """
274
+ return api.api_call_with_payload(self, 'put', endpoint=endpoint, payload=payload, params=params,
275
+ headers=headers, timeout=timeout, show_full_error=show_full_error,
276
+ return_json=return_json)
277
+
278
+ def get_api_versions(self) -> list:
279
+ """This method returns the API versions for the Salesforce releases.
280
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_versions.htm>`_)
281
+
282
+ :returns: A list containing the API metadata from the ``/services/data`` endpoint.
283
+ """
284
+ return self.get('/services/data')
285
+
286
+ def get_latest_api_version(self) -> str:
287
+ """This method returns the latest Salesforce API version by querying the authorized org.
288
+
289
+ .. version-added:: 1.4.0
290
+
291
+ :returns: The latest Salesforce API version for the authorized org as a string (e.g. ``65.0``)
292
+ """
293
+ versions = self.get_api_versions()
294
+ try:
295
+ latest_version = versions[-1]['version']
296
+ except Exception as exc:
297
+ exc_type = type(exc).__name__
298
+ logger.warning(
299
+ f"Failed to retrieve API version due to a(n) {exc_type} exception; defaulting to "
300
+ f"the fallback version {FALLBACK_SFDC_API_VERSION}"
301
+ )
302
+ latest_version = FALLBACK_SFDC_API_VERSION
303
+ return latest_version
304
+
305
+ def get_org_limits(self):
306
+ """This method returns a list of all org limits.
307
+
308
+ .. versionadded:: 1.1.0
309
+ """
310
+ return self.get(f'/services/data/{self.version}/limits')
311
+
312
+ def get_all_sobjects(self):
313
+ """This method returns a list of all Salesforce objects. (i.e. sObjects)
314
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_describeGlobal.htm>`_)
315
+ """
316
+ return self.get(f'/services/data/{self.version}/sobjects')
317
+
318
+ def get_sobject(self, object_name, describe=False):
319
+ """This method returns basic information or the full (describe) information for a specific sObject.
320
+ (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_basic_info_get.htm>`_,
321
+ `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm>`_)
322
+
323
+ :param object_name: The name of the Salesforce object
324
+ :type object_name: str
325
+ :param describe: Determines if the full (i.e. ``describe``) data should be returned (defaults to ``False``)
326
+ :type describe: bool
327
+ :returns: The Salesforce object data
328
+ """
329
+ uri = f'/services/data/{self.version}/sobjects/{object_name}'
330
+ uri = f'{uri}/describe' if describe else uri
331
+ return self.get(uri)
332
+
333
+ def describe_object(self, object_name):
334
+ """This method returns the full (describe) information for a specific sObject.
335
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm>`_)
336
+
337
+ :param object_name: The name of the Salesforce object
338
+ :type object_name: str
339
+ :returns: The Salesforce object data
340
+ """
341
+ return self.get_sobject(object_name, describe=True)
342
+
343
+ def get_rest_resources(self):
344
+ """This method returns a list of all available REST resources.
345
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_discoveryresource.htm>`_)
346
+ """
347
+ return self.get(f'/services/data/{self.version}')
348
+
349
+ def soql_query(self, query, replace_quotes=True, next_records_url=False):
350
+ """This method performs a SOQL query and returns the results in JSON format.
351
+ (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_,
352
+ `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_)
353
+
354
+ :param query: The SOQL query to perform
355
+ :type query: str
356
+ :param replace_quotes: Determines if double-quotes should be replaced with single-quotes (``True`` by default)
357
+ :type replace_quotes: bool
358
+ :param next_records_url: Indicates that the ``query`` parameter is a ``nextRecordsUrl`` value.
359
+ :type next_records_url: bool
360
+ :returns: The result of the SOQL query
361
+ """
362
+ if next_records_url:
363
+ query = re.sub(r'^.*/', '', query) if '/' in query else query
364
+ else:
365
+ if replace_quotes:
366
+ query = query.replace('"', "'")
367
+ query = core_utils.url_encode(query)
368
+ query = f'?q={query}'
369
+ return self.get(f'/services/data/{self.version}/query/{query}')
370
+
371
+ def search_string(self, string_to_search):
372
+ """This method performs a SOSL query to search for a given string.
373
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_search.htm>`_)
374
+
375
+ .. versionadded:: 1.1.0
376
+
377
+ :param string_to_search: The string for which to search
378
+ :type string_to_search: str
379
+ :returns: The SOSL response data in JSON format
380
+ """
381
+ query = 'FIND {' + string_to_search + '}'
382
+ query = core_utils.url_encode(query)
383
+ return self.get(f'/services/data/{self.version}/search/?q={query}')
384
+
385
+ def create_sobject_record(self, sobject, payload):
386
+ """This method creates a new record for a specific sObject.
387
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm>`_)
388
+
389
+ :param sobject: The sObject under which to create the new record
390
+ :type sobject: str
391
+ :param payload: The JSON payload with the record details
392
+ :type payload: dict
393
+ :returns: The API response from the POST request
394
+ :raises: :py:exc:`RuntimeError`, :py:exc:`TypeError`
395
+ """
396
+ # Ensure the payload is in the appropriate format
397
+ if not isinstance(payload, dict):
398
+ raise TypeError('The sObject payload must be provided as a dictionary.')
399
+
400
+ # Perform the API call and return the response
401
+ response = self.post(f'/services/data/{self.version}/sobjects/{sobject}', payload=payload)
402
+ return response
403
+
404
+ def update_sobject_record(self, sobject, record_id, payload):
405
+ """This method updates an existing sObject record.
406
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm>`_)
407
+
408
+ :param sobject: The sObject under which to update the record
409
+ :type sobject: str
410
+ :param record_id: The ID of the record to be updated
411
+ :type record_id: str
412
+ :param payload: The JSON payload with the record details to be updated
413
+ :type payload: dict
414
+ :returns: The API response from the PATCH request
415
+ :raises: :py:exc:`RuntimeError`, :py:exc:`TypeError`
416
+ """
417
+ # Ensure the payload is in the appropriate format
418
+ if not isinstance(payload, dict):
419
+ raise TypeError('The sObject payload must be provided as a dictionary.')
420
+
421
+ # Perform the API call and return the response
422
+ response = self.patch(f'/services/data/{self.version}/sobjects/{sobject}/{record_id}', payload=payload)
423
+ return response
424
+
425
+ def download_image(self, image_url, record_id, field_name, file_path=None, sobject=None):
426
+ """This method downloads an image using the sObject Rich Text Image Retrieve functionality.
427
+ (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_rich_text_image_retrieve.htm>`_,
428
+ `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_rich_text_image_retrieve.htm>`_)
429
+
430
+ :param image_url: The URL for the image to be downloaded
431
+ :type image_url: str
432
+ :param record_id: The Record ID where the image is found
433
+ :type record_id: str
434
+ :param field_name: The field name within the record where the image is found
435
+ :type field_name: str
436
+ :param file_path: The path to the directory where the image should be saved (current directory if not defined)
437
+ :type file_path: str, None
438
+ :param sobject: The sObject for the record where the image is found (``Knowledge__kav`` by default)
439
+ :type sobject: str
440
+ :returns: The full path to the downloaded image
441
+ :raises: :py:exc:`RuntimeError`
442
+ """
443
+ # Ensure a valid sObject is defined (SFDC Knowledge unless otherwise specified)
444
+ sobject = 'Knowledge__kav' if not sobject else sobject
445
+
446
+ # Retrieve the reference ID for the image
447
+ ref_id = core_utils.get_image_ref_id(image_url)
448
+
449
+ # Define the URI and perform the API call
450
+ image_path = None
451
+ try:
452
+ uri = f'/services/data/{self.version}/sobjects/{sobject}/{record_id}/richTextImageFields/{field_name}/{ref_id}'
453
+ response = self.get(uri, return_json=False)
454
+
455
+ # Save the image as an image file
456
+ try:
457
+ image_path = core_utils.download_image(file_name=f'{ref_id}.jpeg', file_path=file_path,
458
+ response=response)
459
+ except RuntimeError:
460
+ errors.handlers.eprint(f'Failed to download the image with refid {ref_id}.')
461
+ except RuntimeError as exc:
462
+ errors.handlers.eprint(exc)
463
+ return image_path
464
+
465
+ class Chatter(object):
466
+ """This class includes methods associated with Salesforce Chatter."""
467
+ def __init__(self, sfdc_object):
468
+ """This method initializes the :py:class:`salespyforce.core.Salesforce.Chatter` inner class object.
469
+
470
+ :param sfdc_object: The core :py:class:`salespyforce.Salesforce` object
471
+ :type sfdc_object: class[salespyforce.Salesforce]
472
+ """
473
+ self.sfdc_object = sfdc_object
474
+
475
+ def get_my_news_feed(self, site_id=None):
476
+ """This method retrieves the news feed for the user calling the function.
477
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_news_feed.htm>`_)
478
+
479
+ :param site_id: The ID of an Experience Cloud site against which to query (optional)
480
+ :type site_id: str, None
481
+ :returns: The news feed data
482
+ :raises: :py:exc:`RuntimeError`
483
+ """
484
+ return chatter_module.get_my_news_feed(self.sfdc_object, site_id=site_id)
485
+
486
+ def get_user_news_feed(self, user_id, site_id=None):
487
+ """This method retrieves another user's news feed.
488
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_user_profile_feed.htm>`_)
489
+
490
+ :param user_id: The ID of the user whose feed you wish to return
491
+ :type user_id: str
492
+ :param site_id: The ID of an Experience Cloud site against which to query (optional)
493
+ :type site_id: str, None
494
+ :returns: The news feed data
495
+ :raises: :py:exc:`RuntimeError`
496
+ """
497
+ return chatter_module.get_user_news_feed(self.sfdc_object, user_id=user_id, site_id=site_id)
498
+
499
+ def get_group_feed(self, group_id, site_id=None):
500
+ """This method retrieves a group's news feed.
501
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_group_feed.htm>`_)
502
+
503
+ :param group_id: The ID of the group whose feed you wish to return
504
+ :type group_id: str
505
+ :param site_id: The ID of an Experience Cloud site against which to query (optional)
506
+ :type site_id: str, None
507
+ :returns: The news feed data
508
+ :raises: :py:exc:`RuntimeError`
509
+ """
510
+ return chatter_module.get_group_feed(self.sfdc_object, group_id=group_id, site_id=site_id)
511
+
512
+ def post_feed_item(self, subject_id, message_text=None, message_segments=None, site_id=None, created_by_id=None):
513
+ """This method publishes a new Chatter feed item.
514
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_post_feed_item.htm>`_)
515
+
516
+ :param subject_id: The Subject ID against which to publish the feed item (e.g. ``0F9B000000000W2``)
517
+ :type subject_id: str
518
+ :param message_text: Plaintext to be used as the message body
519
+ :type message_segments: str, None
520
+ :param message_segments: Collection of message segments to use instead of a plaintext message
521
+ :type message_segments: list, None
522
+ :param site_id: The ID of an Experience Cloud site against which to query (optional)
523
+ :type site_id: str, None
524
+ :param created_by_id: The ID of the user to impersonate (**Experimental**)
525
+ :type created_by_id: str, None
526
+ :returns: The response of the POST request
527
+ :raises: :py:exc:`RuntimeError`
528
+ """
529
+ return chatter_module.post_feed_item(self.sfdc_object, subject_id=subject_id, message_text=message_text,
530
+ message_segments=message_segments, site_id=site_id,
531
+ created_by_id=created_by_id)
532
+
533
+ def post_comment(self, feed_element_id, message_text=None, message_segments=None, site_id=None, created_by_id=None):
534
+ """This method publishes a comment on a Chatter feed item.
535
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_post_comment_to_feed_element.htm>`_)
536
+
537
+ :param feed_element_id: The ID of the feed element on which to post the comment
538
+ :type feed_element_id: str
539
+ :param message_text: Plaintext to be used as the message body
540
+ :type message_segments: str, None
541
+ :param message_segments: Collection of message segments to use instead of a plaintext message
542
+ :type message_segments: list, None
543
+ :param site_id: The ID of an Experience Cloud site against which to query (optional)
544
+ :type site_id: str, None
545
+ :param created_by_id: The ID of the user to impersonate (**Experimental**)
546
+ :type created_by_id: str, None
547
+ :returns: The response of the POST request
548
+ :raises: :py:exc:`RuntimeError`
549
+ """
550
+ return chatter_module.post_comment(self.sfdc_object, feed_element_id=feed_element_id,
551
+ message_text=message_text, message_segments=message_segments,
552
+ site_id=site_id, created_by_id=created_by_id)
553
+
554
+ class Knowledge(object):
555
+ """This class includes methods associated with Salesforce Knowledge."""
556
+ def __init__(self, sfdc_object):
557
+ """This method initializes the :py:class:`salespyforce.core.Salesforce.Knowledge` inner class object.
558
+
559
+ :param sfdc_object: The core :py:class:`salespyforce.Salesforce` object
560
+ :type sfdc_object: class[salespyforce.Salesforce]
561
+ """
562
+ self.sfdc_object = sfdc_object
563
+
564
+ def check_for_existing_article(self, title, sobject=None, return_id=False, return_id_and_number=False,
565
+ include_archived=False):
566
+ """This method checks to see if an article already exists with a given title and returns its article number.
567
+ (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_.
568
+ `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_)
569
+
570
+ :param title: The title of the knowledge article for which to check
571
+ :type title: str
572
+ :param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
573
+ :type sobject: str, None
574
+ :param return_id: Determines if the Article ID should be returned (``False`` by default)
575
+ :type return_id: bool
576
+ :param return_id_and_number: Determines if Article ID and Article Number should be returned (``False`` by default)
577
+ :type return_id_and_number: bool
578
+ :param include_archived: Determines if archived articles should be included (``False`` by default)
579
+ :type include_archived: bool
580
+ :returns: The Article Number, Article ID, or both, if found, or a blank string if not found
581
+ """
582
+ return knowledge_module.check_for_existing_article(self.sfdc_object, title=title,
583
+ sobject=sobject, return_id=return_id,
584
+ return_id_and_number=return_id_and_number,
585
+ include_archived=include_archived)
586
+
587
+ def get_article_id_from_number(self, article_number, sobject=None, return_uri=False):
588
+ """This method returns the Article ID when an article number is provided.
589
+ (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_,
590
+ `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_)
591
+
592
+ :param article_number: The Article Number to query
593
+ :type article_number: str, int
594
+ :param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
595
+ :type sobject: str, None
596
+ :param return_uri: Determines if the URI of the article should be returned rather than the ID (``False`` by default)
597
+ :type return_uri: bool
598
+ :returns: The Article ID or Article URI, or a blank string if no article is found
599
+ :raises: :py:exc:`ValueError`
600
+ """
601
+ return knowledge_module.get_article_id_from_number(self.sfdc_object, article_number=article_number,
602
+ sobject=sobject, return_uri=return_uri)
603
+
604
+ def get_articles_list(self, query=None, sort=None, order=None, page_size=20, page_num=1):
605
+ """This method retrieves a list of knowledge articles.
606
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artlist.htm>`_)
607
+
608
+ :param query: A SOQL query with which to filter the results (optional)
609
+ :type query: str, None
610
+ :param sort: One of the following optional values: ``LastPublishedDate``, ``CreatedDate``, ``Title``, or ``ViewScore``
611
+ :type sort: str, None
612
+ :param order: Optionally define the ORDER BY as ``ASC`` or ``DESC``
613
+ :type order: str, None
614
+ :param page_size: The number of results per page (``20`` by default)
615
+ :type page_size: int
616
+ :param page_num: The starting page number (``1`` by default)
617
+ :type page_num: int
618
+ :returns: The list of retrieved knowledge articles
619
+ """
620
+ return knowledge_module.get_articles_list(self.sfdc_object, query=query, sort=sort, order=order,
621
+ page_size=page_size, page_num=page_num)
622
+
623
+ def get_article_details(self, article_id, sobject=None):
624
+ """This method retrieves details for a single knowledge article.
625
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artdetails.htm>`_)
626
+
627
+ :param article_id: The Article ID for which to retrieve details
628
+ :type article_id: str
629
+ :param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
630
+ :type sobject: str, None
631
+ :returns: The details for the knowledge article
632
+ """
633
+ return knowledge_module.get_article_details(self.sfdc_object, article_id=article_id, sobject=sobject)
634
+
635
+ def get_validation_status(self, article_id=None, article_details=None, sobject=None):
636
+ """This method retrieves the Validation Status for a given Article ID.
637
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artdetails.htm>`_)
638
+
639
+ :param article_id: The Article ID for which to retrieve details
640
+ :type article_id: str, None
641
+ :param article_details: The dictionary of article details for the given article
642
+ :type article_details: dict, None
643
+ :param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
644
+ :type sobject: str, None
645
+ :returns: The validation status as a text string
646
+ :raises: :py:exc:`RuntimeError`
647
+ """
648
+ return knowledge_module.get_validation_status(self.sfdc_object, article_id=article_id,
649
+ article_details=article_details, sobject=sobject)
650
+
651
+ def get_article_metadata(self, article_id):
652
+ """This method retrieves metadata for a specific knowledge article.
653
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_retrieve_article_metadata.htm>`_)
654
+
655
+ :param article_id: The Article ID for which to retrieve details
656
+ :type article_id: str
657
+ :returns: The article metadata as a dictionary
658
+ :raises: :py:exc:`RuntimeError`
659
+ """
660
+ return knowledge_module.get_article_metadata(self.sfdc_object, article_id=article_id)
661
+
662
+ def get_article_version(self, article_id):
663
+ """This method retrieves the version ID for a given master article ID.
664
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_retrieve_article_version.htm>`_)
665
+
666
+ :param article_id: The Article ID for which to retrieve details
667
+ :type article_id: str
668
+ :returns: The version ID for the given master article ID
669
+ :raises: :py:exc:`RuntimeError`
670
+ """
671
+ return knowledge_module.get_article_version(self.sfdc_object, article_id=article_id)
672
+
673
+ def get_article_url(self, article_id=None, article_number=None, sobject=None):
674
+ """This function constructs the URL to view a knowledge article in Lightning or Classic.
675
+
676
+ :param article_id: The Article ID for which to retrieve details
677
+ :type article_id: str, None
678
+ :param article_number: The article number for which to retrieve details
679
+ :type article_number: str, int, None
680
+ :param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
681
+ :type sobject: str, None
682
+ :returns: The article URL as a string
683
+ :raises: :py:exc:`ValueError`
684
+ """
685
+ return knowledge_module.get_article_url(self.sfdc_object, article_id=article_id,
686
+ article_number=article_number, sobject=sobject)
687
+
688
+ def create_article(self, article_data, sobject=None, full_response=False):
689
+ """This method creates a new knowledge article draft.
690
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm>`_)
691
+
692
+ :param article_data: The article data used to populate the article
693
+ :type article_data: dict
694
+ :param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
695
+ :type sobject: str, None
696
+ :param full_response: Determines if the full API response should be returned instead of the article ID (``False`` by default)
697
+ :type full_response: bool
698
+ :returns: The API response or the ID of the article draft
699
+ :raises: :py:exc:`ValueError`, :py:exc:`TypeError`
700
+ """
701
+ return knowledge_module.create_article(self.sfdc_object, article_data=article_data, sobject=sobject,
702
+ full_response=full_response)
703
+
704
+ def update_article(self, record_id, article_data, sobject=None, include_status_code=False):
705
+ """This method updates an existing knowledge article draft.
706
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm>`_)
707
+
708
+ :param record_id: The ID of the article draft record to be updated
709
+ :type record_id: str
710
+ :param article_data: The article data used to update the article
711
+ :type article_data: dict
712
+ :param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
713
+ :type sobject: str, None
714
+ :param include_status_code: Determines if the API response status code should be returned (``False`` by default)
715
+ :type include_status_code: bool
716
+ :returns: A Boolean indicating if the update operation was successful, and optionally the API response status code
717
+ :raises: :py:exc:`ValueError`, :py:exc:`TypeError`, :py:exc:`RuntimeError`
718
+ """
719
+ return knowledge_module.update_article(self.sfdc_object, record_id=record_id, article_data=article_data,
720
+ sobject=sobject, include_status_code=include_status_code)
721
+
722
+ def create_draft_from_online_article(self, article_id, unpublish=False):
723
+ """This method creates a draft knowledge article from an online article.
724
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/actions_obj_knowledge.htm#createDraftFromOnlineKnowledgeArticle>`_)
725
+
726
+ :param article_id: The ID of the online article from which to create the draft
727
+ :type article_id: str
728
+ :param unpublish: Determines if the online article should be unpublished when the draft is created (``False`` by default)
729
+ :type unpublish: bool
730
+ :returns: The API response from the POST request
731
+ :raises: :py:exc:`RuntimeError`
732
+ """
733
+ return knowledge_module.create_draft_from_online_article(self.sfdc_object, article_id=article_id,
734
+ unpublish=unpublish)
735
+
736
+ def create_draft_from_master_version(self, article_id=None, knowledge_article_id=None, article_data=None,
737
+ sobject=None, full_response=False):
738
+ """This method creates an online version of a master article.
739
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.198.0.knowledge_dev.meta/knowledge_dev/knowledge_REST_edit_online_master.htm>`_)
740
+
741
+ :param article_id: The Article ID from which to create the draft
742
+ :type article_id: str, None
743
+ :param knowledge_article_id: The Knowledge Article ID (``KnowledgeArticleId``) from which to create the draft
744
+ :type knowledge_article_id: str, None
745
+ :param article_data: The article data associated with the article from which to create the draft
746
+ :type article_data: dict, None
747
+ :param sobject: The Salesforce object to query (``Knowledge__kav`` by default)
748
+ :type sobject: str, None
749
+ :param full_response: Determines if the full API response should be returned instead of the article ID (``False`` by default)
750
+ :type full_response: bool
751
+ :returns: The API response or the ID of the article draft
752
+ :raises: :py:exc:`RuntimeError`
753
+ """
754
+ return knowledge_module.create_draft_from_master_version(self.sfdc_object, article_id=article_id,
755
+ knowledge_article_id=knowledge_article_id,
756
+ article_data=article_data, sobject=sobject,
757
+ full_response=full_response)
758
+
759
+ def publish_article(self, article_id, major_version=True, full_response=False):
760
+ """This method publishes a draft knowledge article as a major or minor version.
761
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_publish_master_version.htm>`_)
762
+
763
+ :param article_id: The Article ID to publish
764
+ :type article_id: str
765
+ :param major_version: Determines if the published article should be a major version (``True`` by default)
766
+ :type major_version: bool
767
+ :param full_response: Determines if the full API response should be returned (``False`` by default)
768
+ :type full_response: bool
769
+ :returns: A Boolean value indicating the success of the action or the API response from the PATCH request
770
+ :raises: :py:exc:`RuntimeError`
771
+ """
772
+ return knowledge_module.publish_article(self.sfdc_object, article_id=article_id,
773
+ major_version=major_version, full_response=full_response)
774
+
775
+ def publish_multiple_articles(self, article_id_list, major_version=True):
776
+ """This method publishes multiple knowledge article drafts at one time.
777
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/actions_obj_knowledge.htm#publishKnowledgeArticles>`_)
778
+
779
+ :param article_id_list: A list of Article IDs to be published
780
+ :type article_id_list: list
781
+ :param major_version: Determines if the published article should be a major version (``True`` by default)
782
+ :type major_version: bool
783
+ :returns: The API response from the POST request
784
+ :raises: :py:exc:`RuntimeError`, :py:exc:`TypeError`, :py:exc:`ValueError`
785
+ """
786
+ return knowledge_module.publish_multiple_articles(self.sfdc_object, article_id_list=article_id_list,
787
+ major_version=major_version)
788
+
789
+ def assign_data_category(self, article_id, category_group_name, category_name):
790
+ """This method assigns a single data category for a knowledge article.
791
+ (`Reference <https://itsmemohit.medium.com/quick-win-15-salesforce-knowledge-rest-apis-bb0725b2040e>`_)
792
+
793
+ .. versionadded:: 1.2.0
794
+
795
+ :param article_id: The ID of the article to update
796
+ :type article_id: str
797
+ :param category_group_name: The unique Data Category Group Name
798
+ :type category_group_name: str
799
+ :param category_name: The unique Data Category Name
800
+ :type category_name: str
801
+ :returns: The API response from the POST request
802
+ :raises: :py:exc:`RuntimeError`
803
+ """
804
+ return knowledge_module.assign_data_category(self.sfdc_object, article_id=article_id,
805
+ category_group_name=category_group_name,
806
+ category_name=category_name)
807
+
808
+ def archive_article(self, article_id):
809
+ """This function archives a published knowledge article.
810
+ (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_archive_master_version.htm>`_)
811
+
812
+ .. versionadded:: 1.3.0
813
+
814
+ :param article_id: The ID of the article to archive
815
+ :type article_id: str
816
+ :returns: The API response from the POST request
817
+ :raises: :py:exc:`RuntimeError`
818
+ """
819
+ return knowledge_module.archive_article(self.sfdc_object, article_id=article_id)
820
+
821
+
822
+ def define_connection_info():
823
+ """This function prompts the user for the connection information.
824
+
825
+ :returns: The connection info in a dictionary
826
+ """
827
+ base_url = input('Enter your instance URL: [] ')
828
+ org_id = input('Enter the Org ID for your instance: [] ')
829
+ username = input('Enter the username of your API user: [] ')
830
+ password = input('Enter the password of your API user: [] ')
831
+ endpoint_url = input('Enter the endpoint URL: [] ')
832
+ client_id = input('Enter the Client ID: [] ')
833
+ client_secret = input('Enter the Client Secret: [] ')
834
+ security_token = input('Enter the Security Token: [] ')
835
+ connection_info = compile_connection_info(base_url, org_id, username, password, endpoint_url,
836
+ client_id, client_secret, security_token)
837
+ return connection_info
838
+
839
+
840
+ def compile_connection_info(base_url, org_id, username, password, endpoint_url,
841
+ client_id, client_secret, security_token):
842
+ """This function compiles the connection info into a dictionary that can be consumed by the core object.
843
+
844
+ :param base_url: The base URL of the Salesforce instance
845
+ :type base_url: str
846
+ :param org_id: The Org ID of the Salesforce instance
847
+ :type org_id: str
848
+ :param username: The username of the API user
849
+ :type username: str
850
+ :param password: The password of the API user
851
+ :type password: str
852
+ :param endpoint_url: The endpoint URL for the Salesforce instance
853
+ :type endpoint_url: str
854
+ :param client_id: The Client ID for the Salesforce instance
855
+ :type client_id: str
856
+ :param client_secret: The Client Secret for the Salesforce instance
857
+ :type client_secret: str
858
+ :param security_token: The Security Token for the Salesforce instance
859
+ :type security_token: str
860
+ :returns: The connection info in a dictionary
861
+ """
862
+ connection_info = {
863
+ 'base_url': base_url,
864
+ 'org_id': org_id,
865
+ 'username': username,
866
+ 'password': password,
867
+ 'endpoint_url': endpoint_url,
868
+ 'client_id': client_id,
869
+ 'client_secret': client_secret,
870
+ 'security_token': security_token,
871
+ }
872
+ return connection_info