Source code for salespyforce.core

# -*- coding: utf-8 -*-
"""
:Module:            salespyforce.core
:Synopsis:          This module performs the core Salesforce-related operations
:Usage:             ``from salespyforce import Salesforce``
:Example:           ``sfdc = Salesforce(helper=helper_file_path)``
:Created By:        Jeff Shurtliff
:Last Modified:     Jeff Shurtliff
:Modified Date:     03 Feb 2026
"""

from __future__ import annotations

import re
from typing import Optional

import requests

from . import api, errors
from . import chatter as chatter_module
from . import knowledge as knowledge_module
from .utils import core_utils, log_utils
from .utils.helper import get_helper_settings

# Define constants
FALLBACK_SFDC_API_VERSION = '65.0'      # Used if querying the org for the version fails
VALID_ACCESS_CONTROL_FIELDS = {'HasReadAccess', 'HasEditAccess', 'HasDeleteAccess'}

# Initialize logging
logger = log_utils.initialize_logging(__name__)


[docs] class Salesforce(object): """This is the class for the core object leveraged in this module.""" # Define the function that initializes the object instance (i.e. instantiates the object)
[docs] def __init__(self, connection_info=None, version=None, base_url=None, org_id=None, username=None, password=None, endpoint_url=None, client_id=None, client_secret=None, security_token=None, helper=None): """This method instantiates the core Salesforce object. .. versionchanged:: 1.4.0 The authorized Salesforce org is now queried to determine the latest API version to leverage unless explicitly defined with the ``version`` parameter when instantiating the object. :param connection_info: The information for connecting to the Salesforce instance :type connection_info: dict, None :param version: The Salesforce API version to utilize (uses latest version from org if not explicitly defined) :type version: str, None :param base_url: The base URL of the Salesforce instance :type base_url: str, None :param org_id: The Org ID of the Salesforce instance :type org_id: str, None :param username: The username of the API user :type username: str, None :param password: The password of the API user :type password: str, None :param endpoint_url: The endpoint URL for the Salesforce instance :type endpoint_url: str, None :param client_id: The Client ID for the Salesforce instance :type client_id: str, None :param client_secret: The Client Secret for the Salesforce instance :type client_secret: str, None :param security_token: The Security Token for the Salesforce instance :type security_token: str, None :param helper: The file path of a helper file :type helper: str, None :returns: The instantiated object :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError` """ # Define the default settings self._helper_settings = {} # Check for provided connection info if connection_info is None: # Check for a supplied helper file if helper: # Parse the helper file contents self.helper_path = helper if any((isinstance(helper, tuple), isinstance(helper, list), isinstance(helper, set))): helper_file_path, helper_file_type = helper elif isinstance(helper, str): helper_file_path, helper_file_type = (helper, 'yaml') elif isinstance(helper, dict): helper_file_path, helper_file_type = helper.values() else: error_msg = "The 'helper' argument can only be supplied as tuple, string, list, set or dict." logger.error(error_msg) raise TypeError(error_msg) self._helper_settings = get_helper_settings(helper_file_path, helper_file_type) connection_info = self._parse_helper_connection_info() elif not any((base_url, org_id, username, password, endpoint_url, client_id, client_secret, security_token)): # Prompt for the connection info if not defined connection_info = define_connection_info() else: # Compile the connection info from the provided parameters connection_info = compile_connection_info(base_url, org_id, username, password, endpoint_url, client_id, client_secret, security_token) # Get the connection information used to connect to the instance self.connection_info = connection_info if connection_info is not None else self._get_empty_connection_info() # Define the base URL and Org ID self.base_url = self.connection_info.get('base_url', '') self.org_id = self.connection_info.get('org_id', '') # Define the connection response data variables auth_response = self.connect() self.access_token = auth_response.get('access_token') self.instance_url = auth_response.get('instance_url') self.signature = auth_response.get('signature') # Define the version with explicitly provided version or by querying the Salesforce org self.version = f'v{version}' if version else f'v{self.get_latest_api_version()}' # Retrieve info about current user self.current_user_info = self.retrieve_current_user_info(on_init=True, raise_exc_on_error=False) # Import inner object classes so their methods can be called from the primary object self.chatter = self._import_chatter_class() self.knowledge = self._import_knowledge_class()
def _import_chatter_class(self): """This method allows the :py:class:`salespyforce.core.Salesforce.Chatter` class to be utilized in the core object.""" return Salesforce.Chatter(self) def _import_knowledge_class(self): """This method allows the :py:class:`salespyforce.core.Salesforce.Knowledge` class to be utilized in the core object.""" return Salesforce.Knowledge(self) @staticmethod def _get_empty_connection_info(): """This method returns an empty connection_info dictionary with all blank values.""" _connection_info = {} _fields = ['username', 'password', 'base_url', 'endpoint_url', 'client_key', 'client_secret', 'org_id', 'security_token'] for _field in _fields: _connection_info[_field] = '' return _connection_info def _parse_helper_connection_info(self): """This method parses the helper content to populate the connection info.""" _connection_info = {} _fields = ['username', 'password', 'base_url', 'endpoint_url', 'client_key', 'client_secret', 'org_id', 'security_token'] for _field in _fields: if _field in self._helper_settings['connection']: _connection_info[_field] = self._helper_settings['connection'][_field] return _connection_info def _get_headers(self, _header_type='default'): """This method returns the appropriate HTTP headers to use for different types of API calls.""" return api._get_headers(_access_token=self.access_token, _header_type=_header_type) def _get_cached_user_info(self, _field: str, _retrieve_if_missing: bool = False): """This method attempts to retrieve a value for a given field in the cached ``userinfo`` data and optionally queries the API as needed to retrieve the data when not found. .. versionadded:: 1.4.0 :param _field: The name of the field for which the value is needed :type _field: str :param _retrieve_if_missing: Will query the Salesforce REST API for the data when missing if True (``False`` by default) :type _retrieve_if_missing: bool :returns: The field value when found (or retrieved), or a None value if the field value could not be obtained :raises: :py:exc:`salespyforce.errors.exceptions.APIRequestError` """ _field_value = None _not_present_msg = f"The '{_field}' field is not present in the current user info data" if self.current_user_info and _field in self.current_user_info: _field_value = self.current_user_info[_field] else: logger.warning(_not_present_msg) if _retrieve_if_missing: self.current_user_info = self.retrieve_current_user_info(raise_exc_on_error=True) if self.current_user_info and _field in self.current_user_info: _field_value = self.current_user_info[_field] else: logger.error(f'{_not_present_msg} even after refreshing cached current user info') return _field_value
[docs] def connect(self): """This method connects to the Salesforce instance to obtain the access token. (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_) :returns: The API call response with the authorization information :raises: :py:exc:`RuntimeError` """ params = { 'grant_type': 'password', 'client_id': self.connection_info.get('client_key'), 'client_secret': self.connection_info.get('client_secret'), 'username': self.connection_info.get('username'), 'password': f'{self.connection_info.get("password")}{self.connection_info.get("security_token")}' } response = requests.post(self.connection_info.get('endpoint_url'), params=params) if response.status_code != 200: raise RuntimeError(f'Failed to connect to the Salesforce instance.\n{response.text}') return response.json()
[docs] def retrieve_current_user_info(self, all_data=False, raise_exc_on_error=False, on_init=False) -> dict: """This method retrieves the ``userinfo`` data for the current/running user. .. versionadded:: 1.4.0 :param all_data: Returns all ``userinfo`` data from the API when True instead of only the relevant fields/values (``False`` by default) :type all_data: bool :param raise_exc_on_error: Raises an exception if the API retrieval attempt fails when True (``False`` by default) :type raise_exc_on_error: bool :param on_init: Indicates if the method is being called during the core object instantiation (``False`` by default) :type on_init: bool :returns: The user info data within a dictionary :raises: :py:exc:`RuntimeError`, :py:exc:`salespyforce.errors.exceptions.APIRequestError` """ user_info = {'user_id': '', 'nickname': '', 'name': '', 'email': '', 'user_type': '', 'language': '', 'locale': '', 'utcOffset': '', 'is_salesforce_integration_user': None} bool_fields = ['is_salesforce_integration_user'] endpoint = '/services/oauth2/userinfo' base_error_msg = 'Failed to retrieve current user info' msg_init_segment = 'on core object instantiation' if on_init: base_error_msg = f'{base_error_msg} {msg_init_segment}' try: response = self.get(endpoint) if isinstance(response, dict) and all_data: user_info = response elif isinstance(response, dict): for field in user_info.keys(): if field in response: default_val = None if field in bool_fields else '' user_info[field] = response.get(field, default_val) else: logger.error(f'{base_error_msg} with a usable format') except Exception as exc: exc_type = errors.handlers.get_exception_type(exc) exc_msg = f'{base_error_msg} due to {exc_type} exception: {exc}' logger.error(exc_msg) if raise_exc_on_error: raise errors.exceptions.APIRequestError(f'{exc_type}: {exc}') # Return the populated user info return user_info
[docs] def get(self, endpoint, params=None, headers=None, timeout=None, show_full_error=True, return_json=True): """This method performs a GET request against the Salesforce instance. (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_) .. versionchanged:: 1.4.0 A global constant is now leveraged for the API timeout value instead of hardcoding the value. (Timeout is still **30** seconds in this version) :param endpoint: The API endpoint to query :type endpoint: str :param params: The query parameters (where applicable) :type params: dict, None :param headers: Specific API headers to use when performing the API call :type headers: dict, None :param timeout: The timeout period in seconds (defaults to ``30``) :type timeout: int, None :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``) :type show_full_error: bool :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``) :returns: The API response in JSON format or as a ``requests`` object :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError`, :py:exc:`salespyforce.errors.exceptions.InvalidURLError` """ return api.get(self, endpoint=endpoint, params=params, headers=headers, timeout=timeout, show_full_error=show_full_error, return_json=return_json)
[docs] def api_call_with_payload(self, method, endpoint, payload, params=None, headers=None, timeout=None, show_full_error=True, return_json=True): """This method performs a POST call against the Salesforce instance. (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_) .. versionchanged:: 1.4.0 A global constant is now leveraged for the API timeout value instead of hardcoding the value. (Timeout is still **30** seconds in this version) :param method: The API method (``post``, ``put``, or ``patch``) :type method: str :param endpoint: The API endpoint to query :type endpoint: str :param payload: The payload to leverage in the API call :type payload: dict :param params: The query parameters (where applicable) :type params: dict, None :param headers: Specific API headers to use when performing the API call :type headers: dict, None :param timeout: The timeout period in seconds (defaults to ``30``) :type timeout: int, None :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``) :type show_full_error: bool :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``) :returns: The API response in JSON format or as a ``requests`` object :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError`, :py:exc:`ValueError`, :py:exc:`salespyforce.errors.exceptions.InvalidURLError` """ return api.api_call_with_payload(self, method=method, endpoint=endpoint, payload=payload, params=params, headers=headers, timeout=timeout, show_full_error=show_full_error, return_json=return_json)
[docs] def post(self, endpoint, payload, params=None, headers=None, timeout=None, show_full_error=True, return_json=True): """This method performs a POST call against the Salesforce instance. (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_) .. versionchanged:: 1.4.0 A global constant is now leveraged for the API timeout value instead of hardcoding the value. (Timeout is still **30** seconds in this version) :param endpoint: The API endpoint to query :type endpoint: str :param payload: The payload to leverage in the API call :type payload: dict :param params: The query parameters (where applicable) :type params: dict, None :param headers: Specific API headers to use when performing the API call :type headers: dict, None :param timeout: The timeout period in seconds (defaults to ``30``) :type timeout: int, None :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``) :type show_full_error: bool :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``) :returns: The API response in JSON format or as a ``requests`` object :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError`, :py:exc:`ValueError`, :py:exc:`salespyforce.errors.exceptions.InvalidURLError` """ return api.api_call_with_payload(self, 'post', endpoint=endpoint, payload=payload, params=params, headers=headers, timeout=timeout, show_full_error=show_full_error, return_json=return_json)
[docs] def patch(self, endpoint, payload, params=None, headers=None, timeout=None, show_full_error=True, return_json=False): """This method performs a PATCH call against the Salesforce instance. (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_) .. versionchanged:: 1.4.0 A global constant is now leveraged for the API timeout value instead of hardcoding the value. (Timeout is still **30** seconds in this version) :param endpoint: The API endpoint to query :type endpoint: str :param payload: The payload to leverage in the API call :type payload: dict :param params: The query parameters (where applicable) :type params: dict, None :param headers: Specific API headers to use when performing the API call :type headers: dict, None :param timeout: The timeout period in seconds (defaults to ``30``) :type timeout: int, None :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``) :type show_full_error: bool :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``) :returns: The API response in JSON format or as a ``requests`` object :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError`, :py:exc:`ValueError`, :py:exc:`salespyforce.errors.exceptions.InvalidURLError` """ return api.api_call_with_payload(self, 'patch', endpoint=endpoint, payload=payload, params=params, headers=headers, timeout=timeout, show_full_error=show_full_error, return_json=return_json)
[docs] def put(self, endpoint, payload, params=None, headers=None, timeout=None, show_full_error=True, return_json=True): """This method performs a PUT call against the Salesforce instance. (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_) .. versionchanged:: 1.4.0 A global constant is now leveraged for the API timeout value instead of hardcoding the value. (Timeout is still **30** seconds in this version) :param endpoint: The API endpoint to query :type endpoint: str :param payload: The payload to leverage in the API call :type payload: dict :param params: The query parameters (where applicable) :type params: dict, None :param headers: Specific API headers to use when performing the API call :type headers: dict, None :param timeout: The timeout period in seconds (defaults to ``30``) :type timeout: int, None :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``) :type show_full_error: bool :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``) :returns: The API response in JSON format or as a ``requests`` object :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError`, :py:exc:`ValueError`, :py:exc:`salespyforce.errors.exceptions.InvalidURLError` """ return api.api_call_with_payload(self, 'put', endpoint=endpoint, payload=payload, params=params, headers=headers, timeout=timeout, show_full_error=show_full_error, return_json=return_json)
[docs] def delete(self, endpoint, params=None, headers=None, timeout=None, show_full_error=True, return_json=True): """This method performs a DELETE request against the Salesforce instance. (`Reference <https://jereze.com/code/authentification-salesforce-rest-api-python/>`_) .. versionadded:: 1.4.0 :param endpoint: The API endpoint to query :type endpoint: str :param params: The query parameters (where applicable) :type params: dict, None :param headers: Specific API headers to use when performing the API call :type headers: dict, None :param timeout: The timeout period in seconds (defaults to ``30``) :type timeout: int, None :param show_full_error: Determines if the full error message should be displayed (defaults to ``True``) :type show_full_error: bool :param return_json: Determines if the response should be returned in JSON format (defaults to ``True``) :returns: The API response in JSON format or as a ``requests`` object :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError`, :py:exc:`salespyforce.errors.exceptions.InvalidURLError` """ return api.delete(self, endpoint=endpoint, params=params, headers=headers, timeout=timeout, show_full_error=show_full_error, return_json=return_json)
[docs] def get_api_versions(self) -> list: """This method returns the API versions for the Salesforce releases. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_versions.htm>`_) :returns: A list containing the API metadata from the ``/services/data`` endpoint. :raises: :py:exc:`RuntimeError` """ return self.get('/services/data')
[docs] def get_latest_api_version(self) -> str: """This method returns the latest Salesforce API version by querying the authorized org. .. versionadded:: 1.4.0 :returns: The latest Salesforce API version for the authorized org as a string (e.g. ``65.0``) """ versions = self.get_api_versions() try: latest_version = versions[-1]['version'] except Exception as exc: exc_type = type(exc).__name__ logger.warning( f"Failed to retrieve API version due to a(n) {exc_type} exception; defaulting to " f"the fallback version {FALLBACK_SFDC_API_VERSION}" ) latest_version = FALLBACK_SFDC_API_VERSION return latest_version
[docs] def get_org_limits(self): """This method returns a list of all org limits. .. versionadded:: 1.1.0 :returns: The Salesforce org governor limits data :raises: :py:exc:`RuntimeError` """ return self.get(f'/services/data/{self.version}/limits')
[docs] def get_all_sobjects(self): """This method returns a list of all Salesforce objects. (i.e. sObjects) (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_describeGlobal.htm>`_) :returns: The list of all Salesforce objects :raises: :py:exc:`RuntimeError` """ return self.get(f'/services/data/{self.version}/sobjects')
[docs] def get_sobject(self, object_name, describe=False): """This method returns basic information or the full (describe) information for a specific sObject. (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_basic_info_get.htm>`_, `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm>`_) :param object_name: The name of the Salesforce object :type object_name: str :param describe: Determines if the full (i.e. ``describe``) data should be returned (defaults to ``False``) :type describe: bool :returns: The Salesforce object data :raises: :py:exc:`RuntimeError` """ uri = f'/services/data/{self.version}/sobjects/{object_name}' uri = f'{uri}/describe' if describe else uri return self.get(uri)
[docs] def describe_object(self, object_name): """This method returns the full (describe) information for a specific sObject. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm>`_) :param object_name: The name of the Salesforce object :type object_name: str :returns: The Salesforce object data :raises: :py:exc:`RuntimeError` """ return self.get_sobject(object_name, describe=True)
[docs] def get_rest_resources(self): """This method returns a list of all available REST resources. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_discoveryresource.htm>`_) :returns: The list of all available REST resources for the Salesforce org :raises: :py:exc:`RuntimeError` """ return self.get(f'/services/data/{self.version}')
[docs] @staticmethod def get_18_char_id(record_id: str) -> str: """This method converts a 15-character Salesforce record ID to its 18-character case-insensitive form. .. versionadded:: 1.4.0 :param record_id: The Salesforce record ID to convert (or return unchanged if already 18 characters) :type record_id: str :returns: The 18-character Salesforce record ID :raises: :py:exc:`ValueError` """ return core_utils.get_18_char_id(record_id=record_id)
[docs] def soql_query(self, query, replace_quotes=True, next_records_url=False): """This method performs a SOQL query and returns the results in JSON format. (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_, `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_) :param query: The SOQL query to perform :type query: str :param replace_quotes: Determines if double-quotes should be replaced with single-quotes (``True`` by default) :type replace_quotes: bool :param next_records_url: Indicates that the ``query`` parameter is a ``nextRecordsUrl`` value. :type next_records_url: bool :returns: The result of the SOQL query :raises: :py:exc:`RuntimeError` """ if next_records_url: query = re.sub(r'^.*/', '', query) if '/' in query else query else: if replace_quotes: query = query.replace('"', "'") query = core_utils.url_encode(query) query = f'?q={query}' return self.get(f'/services/data/{self.version}/query/{query}')
[docs] def search_string(self, string_to_search): """This method performs a SOSL query to search for a given string. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_search.htm>`_) .. versionadded:: 1.1.0 :param string_to_search: The string for which to search :type string_to_search: str :returns: The SOSL response data in JSON format :raises: :py:exc:`RuntimeError` """ query = 'FIND {' + string_to_search + '}' query = core_utils.url_encode(query) return self.get(f'/services/data/{self.version}/search/?q={query}')
[docs] def check_user_record_access(self, record_id: str, user_id=None) -> dict: """This method checks the Read, Edit, and Delete access for a given record and user. .. versionadded:: 1.4.0 :param record_id: The ``Id`` value of the record against which to check the user access :type record_id: str :param user_id: The ``Id`` of the user to evaluate (or the current user's ID if not explicitly defined) :type user_id: str, None :returns: Dictionary with Boolean values for ``HasReadAccess``, ``HasEditAccess``, and ``HasDeleteAccess`` :raises: :py:exc:`RuntimeError`, :py:exc:`salespyforce.errors.exceptions.APIRequestError` """ record_access = {'HasReadAccess': None, 'HasEditAccess': None, 'HasDeleteAccess': None} # Use the current/running user's ID if an ID wasn't explicitly provided if not user_id: user_id = self._get_cached_user_info(_field='user_id', _retrieve_if_missing=True) if user_id: logger.debug(f'Using the User Id {user_id} for the running user as an Id was not specified') # Raise an exception if the User ID is still undefined if not user_id: error_msg = f'The user access for record Id {record_id} cannot be checked as the User Id is undefined' logger.error(error_msg) raise errors.exceptions.MissingRequiredDataError(error_msg) # Perform SOQL query for the access data query = f""" SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess FROM UserRecordAccess WHERE UserId = '{user_id}' AND RecordId = '{record_id}' """ response = self.soql_query(query=query) # Parse the response to extract the relevant field values if 'records' in response and response['records']: response = response['records'][0] for field in record_access.keys(): record_access[field] = response.get(field, None) # Return the record access data return record_access
@staticmethod def _eval_user_record_access(_field: str, _record_id: str, _record_access_data: dict, _raise_exc_on_failure: bool = True) -> bool: """This private method checks for an access level given the field and the record access data. .. versionadded:: 1.4.0 :param _field: The access level field to evaluate (``HasReadAccess``, ``HasEditAccess``, ``HasDeleteAccess``) :type _field: str :param _record_id: The ID value for the record whose access is being checked :type _record_id: str :param _record_access_data: The user record access data that has already been retrieved :type _record_access_data: dict :param _raise_exc_on_failure: Raises an exception rather than returning a ``None`` value (``True`` by default) :type _raise_exc_on_failure: bool :returns: Boolean value indicating the access level for the given field :raises: :py:exc:`salespyforce.errors.exceptions.InvalidFieldError` """ # Raise an exception if a valid access control field is not provided if _field not in VALID_ACCESS_CONTROL_FIELDS: _error_msg = f"The field '{_field}' is not a valid record access level field" raise errors.exceptions.InvalidFieldError(_error_msg) # Identify the access level value if possible (API retrievals should be handled in a parent method) _has_access = _record_access_data.get(_field) if _field in _record_access_data else None if _has_access is None: _error_msg = f"The value for the '{_field}' is undefined for the given Record Id '{_record_id}'" logger.error(_error_msg) if _raise_exc_on_failure: raise errors.exceptions.MissingRequiredDataError(_error_msg) # Return the identified access level value return _has_access
[docs] def can_access_record(self, access_type, record_id, user_id=None, record_access_data=None, raise_exc_on_failure=True): """This method evaluates if a user can access a specific record given the access type. .. versionadded:: 1.4.0 :param access_type: The type of access to evaluate (``read``, ``edit``, or ``delete``) :type access_type: str :param record_id: The ID of the record :type record_id: str :param user_id: The ID of the user to evaluate (defaults to the current/running user if not defined) :type user_id: str, None :param record_access_data: The user record access data that has already been retrieved (optional) :type record_access_data: dict, None :param raise_exc_on_failure: Raises an exception rather than returning a ``None`` value (``True`` by default) :type raise_exc_on_failure: bool :returns: Boolean value indicating the access level for the given field :raises: :py:exc:`salespyforce.errors.exceptions.InvalidFieldError` """ # Define the initial value for the result can_access = None # Identify the correct field to query based on access type access_type_field_mapping = { 'read': 'HasReadAccess', 'edit': 'HasEditAccess', 'delete': 'HasDeleteAccess', } if access_type.lower() not in access_type_field_mapping: error_msg = f"The access_type '{access_type}' is invalid (must use 'read', 'edit', or 'delete')" logger.error(error_msg) if raise_exc_on_failure: raise errors.exceptions.InvalidParameterError(error_msg) else: # Retrieve the field name to check read_access_field = access_type_field_mapping.get(access_type.lower()) # Check to see if record access data was provided and validate that it is a dictionary if record_access_data and not isinstance(record_access_data, dict): error_msg = f"The record_access_data provided is Type {type(read_access_field)} but must be a dict" logger.error(error_msg) if raise_exc_on_failure: raise errors.exceptions.DataMismatchError(error_msg) # Perform the API all to check the record access for the user if data not provided if not record_access_data: record_access_data = self.check_user_record_access(record_id=record_id, user_id=user_id) # Return the access level value can_access = self._eval_user_record_access(_field=read_access_field, _record_id=record_id, _record_access_data=record_access_data, _raise_exc_on_failure=raise_exc_on_failure) # Emit a warning if the value is None rather than a boolean if can_access is None: warn_msg = 'The record access check could not be completed and the function will return a None value' errors.handlers.display_warning(warn_msg) # Return the result return can_access
[docs] def can_read_record(self, record_id, user_id=None, record_access_data=None, raise_exc_on_failure=True): """This method evaluates if a user has access to read a specific record. .. versionadded:: 1.4.0 :param record_id: The ID of the record :type record_id: str :param user_id: The ID of the user to evaluate (defaults to the current/running user if not defined) :type user_id: str, None :param record_access_data: The user record access data that has already been retrieved (optional) :type record_access_data: dict, None :param raise_exc_on_failure: Raises an exception rather than returning a ``None`` value (``True`` by default) :type raise_exc_on_failure: bool :returns: Boolean value indicating the access level for the given field :raises: :py:exc:`salespyforce.errors.exceptions.InvalidFieldError` """ return self.can_access_record(access_type='read', record_id=record_id, user_id=user_id, record_access_data=record_access_data, raise_exc_on_failure=raise_exc_on_failure)
[docs] def can_edit_record(self, record_id, user_id=None, record_access_data=None, raise_exc_on_failure=True): """This method evaluates if a user has access to edit a specific record. .. versionadded:: 1.4.0 :param record_id: The ID of the record :type record_id: str :param user_id: The ID of the user to evaluate (defaults to the current/running user if not defined) :type user_id: str, None :param record_access_data: The user record access data that has already been retrieved (optional) :type record_access_data: dict, None :param raise_exc_on_failure: Raises an exception rather than returning a ``None`` value (``True`` by default) :type raise_exc_on_failure: bool :returns: Boolean value indicating the access level for the given field :raises: :py:exc:`salespyforce.errors.exceptions.InvalidFieldError` """ return self.can_access_record(access_type='edit', record_id=record_id, user_id=user_id, record_access_data=record_access_data, raise_exc_on_failure=raise_exc_on_failure)
[docs] def can_delete_record(self, record_id, user_id=None, record_access_data=None, raise_exc_on_failure=True): """This method evaluates if a user has access to delete a specific record. .. versionadded:: 1.4.0 :param record_id: The ID of the record :type record_id: str :param user_id: The ID of the user to evaluate (defaults to the current/running user if not defined) :type user_id: str, None :param record_access_data: The user record access data that has already been retrieved (optional) :type record_access_data: dict, None :param raise_exc_on_failure: Raises an exception rather than returning a ``None`` value (``True`` by default) :type raise_exc_on_failure: bool :returns: Boolean value indicating the access level for the given field :raises: :py:exc:`salespyforce.errors.exceptions.InvalidFieldError` """ return self.can_access_record(access_type='edit', record_id=record_id, user_id=user_id, record_access_data=record_access_data, raise_exc_on_failure=raise_exc_on_failure)
[docs] def create_sobject_record(self, sobject, payload): """This method creates a new record for a specific sObject. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm>`_) :param sobject: The sObject under which to create the new record :type sobject: str :param payload: The JSON payload with the record details :type payload: dict :returns: The API response from the POST request :raises: :py:exc:`RuntimeError`, :py:exc:`TypeError` """ # Ensure the payload is in the appropriate format if not isinstance(payload, dict): raise TypeError('The sObject payload must be provided as a dictionary.') # Perform the API call and return the response response = self.post(f'/services/data/{self.version}/sobjects/{sobject}', payload=payload) return response
[docs] def update_sobject_record(self, sobject, record_id, payload): """This method updates an existing sObject record. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm>`_) :param sobject: The sObject under which to update the record :type sobject: str :param record_id: The ID of the record to be updated :type record_id: str :param payload: The JSON payload with the record details to be updated :type payload: dict :returns: The API response from the PATCH request :raises: :py:exc:`RuntimeError`, :py:exc:`TypeError` """ # Ensure the payload is in the appropriate format if not isinstance(payload, dict): raise TypeError('The sObject payload must be provided as a dictionary.') # Perform the API call and return the response response = self.patch(f'/services/data/{self.version}/sobjects/{sobject}/{record_id}', payload=payload) return response
[docs] def download_image(self, image_url, record_id, field_name, file_path=None, sobject=None): """This method downloads an image using the sObject Rich Text Image Retrieve functionality. (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_rich_text_image_retrieve.htm>`_, `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_rich_text_image_retrieve.htm>`_) :param image_url: The URL for the image to be downloaded :type image_url: str :param record_id: The Record ID where the image is found :type record_id: str :param field_name: The field name within the record where the image is found :type field_name: str :param file_path: The path to the directory where the image should be saved (current directory if not defined) :type file_path: str, None :param sobject: The sObject for the record where the image is found (``Knowledge__kav`` by default) :type sobject: str :returns: The full path to the downloaded image :raises: :py:exc:`RuntimeError` """ # Ensure a valid sObject is defined (SFDC Knowledge unless otherwise specified) sobject = 'Knowledge__kav' if not sobject else sobject # Retrieve the reference ID for the image ref_id = core_utils.get_image_ref_id(image_url) # Define the URI and perform the API call image_path = None try: uri = f'/services/data/{self.version}/sobjects/{sobject}/{record_id}/richTextImageFields/{field_name}/{ref_id}' response = self.get(uri, return_json=False) # Save the image as an image file try: image_path = core_utils.download_image(file_name=f'{ref_id}.jpeg', file_path=file_path, response=response) except RuntimeError: errors.handlers.eprint(f'Failed to download the image with refid {ref_id}.') except RuntimeError as exc: errors.handlers.eprint(exc) return image_path
[docs] class Chatter(object): """This class includes methods associated with Salesforce Chatter."""
[docs] def __init__(self, sfdc_object): """This method initializes the :py:class:`salespyforce.core.Salesforce.Chatter` inner class object. :param sfdc_object: The core :py:class:`salespyforce.Salesforce` object :type sfdc_object: class[salespyforce.Salesforce] """ self.sfdc_object = sfdc_object
[docs] def get_my_news_feed(self, site_id=None): """This method retrieves the news feed for the user calling the function. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_news_feed.htm>`_) :param site_id: The ID of an Experience Cloud site against which to query (optional) :type site_id: str, None :returns: The news feed data :raises: :py:exc:`RuntimeError` """ return chatter_module.get_my_news_feed(self.sfdc_object, site_id=site_id)
[docs] def get_user_news_feed(self, user_id, site_id=None): """This method retrieves another user's news feed. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_user_profile_feed.htm>`_) :param user_id: The ID of the user whose feed you wish to return :type user_id: str :param site_id: The ID of an Experience Cloud site against which to query (optional) :type site_id: str, None :returns: The news feed data :raises: :py:exc:`RuntimeError` """ return chatter_module.get_user_news_feed(self.sfdc_object, user_id=user_id, site_id=site_id)
[docs] def get_group_feed(self, group_id, site_id=None): """This method retrieves a group's news feed. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_get_group_feed.htm>`_) :param group_id: The ID of the group whose feed you wish to return :type group_id: str :param site_id: The ID of an Experience Cloud site against which to query (optional) :type site_id: str, None :returns: The news feed data :raises: :py:exc:`RuntimeError` """ return chatter_module.get_group_feed(self.sfdc_object, group_id=group_id, site_id=site_id)
[docs] def post_feed_item(self, subject_id, message_text=None, message_segments=None, site_id=None, created_by_id=None): """This method publishes a new Chatter feed item. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_post_feed_item.htm>`_) :param subject_id: The Subject ID against which to publish the feed item (e.g. ``0F9B000000000W2``) :type subject_id: str :param message_text: Plaintext to be used as the message body :type message_segments: str, None :param message_segments: Collection of message segments to use instead of a plaintext message :type message_segments: list, None :param site_id: The ID of an Experience Cloud site against which to query (optional) :type site_id: str, None :param created_by_id: The ID of the user to impersonate (**Experimental**) :type created_by_id: str, None :returns: The response of the POST request :raises: :py:exc:`RuntimeError` """ return chatter_module.post_feed_item(self.sfdc_object, subject_id=subject_id, message_text=message_text, message_segments=message_segments, site_id=site_id, created_by_id=created_by_id)
[docs] def post_comment(self, feed_element_id, message_text=None, message_segments=None, site_id=None, created_by_id=None): """This method publishes a comment on a Chatter feed item. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/quickreference_post_comment_to_feed_element.htm>`_) :param feed_element_id: The ID of the feed element on which to post the comment :type feed_element_id: str :param message_text: Plaintext to be used as the message body :type message_segments: str, None :param message_segments: Collection of message segments to use instead of a plaintext message :type message_segments: list, None :param site_id: The ID of an Experience Cloud site against which to query (optional) :type site_id: str, None :param created_by_id: The ID of the user to impersonate (**Experimental**) :type created_by_id: str, None :returns: The response of the POST request :raises: :py:exc:`RuntimeError` """ return chatter_module.post_comment(self.sfdc_object, feed_element_id=feed_element_id, message_text=message_text, message_segments=message_segments, site_id=site_id, created_by_id=created_by_id)
[docs] class Knowledge(object): """This class includes methods associated with Salesforce Knowledge."""
[docs] def __init__(self, sfdc_object): """This method initializes the :py:class:`salespyforce.core.Salesforce.Knowledge` inner class object. :param sfdc_object: The core :py:class:`salespyforce.Salesforce` object :type sfdc_object: class[salespyforce.Salesforce] """ self.sfdc_object = sfdc_object
[docs] def check_for_existing_article( self, title: str, sobject: Optional[str] = None, return_id: bool = False, return_id_and_number: bool = False, include_archived: bool = False, ): """This method checks to see if an article already exists with a given title and returns its article number. (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_. `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_) :param title: The title of the knowledge article for which to check :type title: str :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param return_id: Determines if the Article ID should be returned (``False`` by default) :type return_id: bool :param return_id_and_number: Determines if Article ID and Article Number should be returned (``False`` by default) :type return_id_and_number: bool :param include_archived: Determines if archived articles should be included (``False`` by default) :type include_archived: bool :returns: The Article Number, Article ID, or both (if found), or a blank string if not found """ return knowledge_module.check_for_existing_article(self.sfdc_object, title=title, sobject=sobject, return_id=return_id, return_id_and_number=return_id_and_number, include_archived=include_archived)
[docs] def get_article_id_from_number( self, article_number, sobject: Optional[str] = None, return_uri: bool = False, ): """This method returns the Article ID when an article number is provided. (`Reference 1 <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_query.htm>`_, `Reference 2 <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_development_soql_sosl_intro.htm>`_) .. warning:: The ability to retrieve the article URI/URL rather than the ID will be moved to a separate function in a future release. :param article_number: The Article Number to query :type article_number: str, int :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param return_uri: Determines if the URI of the article should be returned rather than the ID (``False`` by default) :type return_uri: bool :returns: The Article ID or Article URI, or a blank string if no article is found :raises: :py:exc:`TypeError`, :py:exc:`RuntimeError` """ # TODO: Move return_uri functionality (here and in underlying function) to a separate method/function return knowledge_module.get_article_id_from_number(self.sfdc_object, article_number=article_number, sobject=sobject, return_uri=return_uri)
[docs] def get_articles_list( self, query: Optional[str] = None, sort: Optional[str] = None, order: Optional[str] = None, page_size: int = 20, page_num: int = 1, ) -> list: """This method retrieves a list of knowledge articles. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artlist.htm>`_) :param query: A SOQL query with which to filter the results (optional) :type query: str, None :param sort: Optionally sort the results with one of the following values: ``LastPublishedDate``, ``CreatedDate``, ``Title``, or ``ViewScore`` :type sort: str, None :param order: Optionally define the ORDER BY as ``ASC`` or ``DESC`` :type order: str, None :param page_size: The number of results per page (``20`` by default) :type page_size: int :param page_num: The starting page number (``1`` by default) :type page_num: int :returns: The list of retrieved knowledge articles :raises: :py:exc:`RuntimeError` """ # TODO: Update to use constants for page_size and page_num return knowledge_module.get_articles_list(self.sfdc_object, query=query, sort=sort, order=order, page_size=page_size, page_num=page_num)
[docs] def get_article_details( self, article_id: str, sobject: Optional[str] = None, use_knowledge_articles_endpoint: Optional[bool] = None, ): """This method retrieves details for a single knowledge article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artdetails.htm>`_) .. versionchanged:: 1.4.0 A logic issue was resolved and the new optional ``use_knowledge_articles_endpoint`` parameter can now be set to force the ``knowledgeArticles`` endpoint to be used for the GET request rather than the ``sobjects`` endpoint. :param article_id: The Article ID for which to retrieve details :type article_id: str :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param use_knowledge_articles_endpoint: Optionally use the ``knowledgeArticles`` endpoint rather than ``sobjects`` to retrieve the article details (``False`` by default) :type use_knowledge_articles_endpoint: bool, None :returns: The details for the knowledge article :raises: :py:exc:`RuntimeError`, :py:exc:`salespyforce.errors.exceptions.DataMismatchError` """ return knowledge_module.get_article_details(self.sfdc_object, article_id=article_id, sobject=sobject, use_knowledge_articles_endpoint=use_knowledge_articles_endpoint)
[docs] def get_validation_status( self, article_id: Optional[str] = None, article_details: Optional[dict] = None, sobject: Optional[str] = None, ) -> str: """This method retrieves the Validation Status for a given Article ID. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_knowledge_support_artdetails.htm>`_) .. versionchanged:: 1.4.0 This method now returns an empty string rather than a ``None`` value if the ``ValidationStatus`` field is not found in the article details data, and a more specific exception class is used when input data is missing instead of the generic :py:exc:`RuntimeError` exception class. :param article_id: The Article ID for which to retrieve details :type article_id: str, None :param article_details: The dictionary of article details for the given article :type article_details: dict, None :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :returns: The validation status as a text string :raises: :py:exc:`RuntimeError`, :py:exc:`salespyforce.errors.exceptions.MissingRequiredDataError` """ return knowledge_module.get_validation_status(self.sfdc_object, article_id=article_id, article_details=article_details, sobject=sobject)
[docs] def get_article_metadata(self, article_id: str) -> dict: """This method retrieves metadata for a specific knowledge article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_retrieve_article_metadata.htm>`_) :param article_id: The Article ID for which to retrieve details :type article_id: str :returns: The article metadata as a dictionary :raises: :py:exc:`RuntimeError` """ return knowledge_module.get_article_metadata(self.sfdc_object, article_id=article_id)
[docs] def get_article_version(self, article_id: str): """This method retrieves the version ID for a given master article ID. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_retrieve_article_version.htm>`_) :param article_id: The Article ID for which to retrieve details :type article_id: str :returns: The version ID for the given master article ID :raises: :py:exc:`RuntimeError` """ # TODO: Determine how the data is returned and if it needs to be pruned to just the article version return knowledge_module.get_article_version(self.sfdc_object, article_id=article_id)
[docs] def get_article_url( self, article_id: Optional[str] = None, article_number=None, # Needs type hint sobject: Optional[str] = None, ): """This function constructs the URL to view a knowledge article in Lightning or Classic. :param article_id: The Article ID for which to retrieve details :type article_id: str, None :param article_number: The article number for which to retrieve details :type article_number: str, int, None :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :returns: The article URL as a string :raises: :py:exc:`ValueError`, :py:exc:`RuntimeError` """ return knowledge_module.get_article_url(self.sfdc_object, article_id=article_id, article_number=article_number, sobject=sobject)
[docs] def create_article( self, article_data: dict, sobject: Optional[str] = None, full_response: bool = False, ): """This method creates a new knowledge article draft. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm>`_) :param article_data: The article data used to populate the article :type article_data: dict :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param full_response: Determines if the full API response should be returned instead of the article ID (``False`` by default) :type full_response: bool :returns: The API response or the ID of the article draft :raises: :py:exc:`ValueError`, :py:exc:`TypeError`, :py:exc:`RuntimeError` """ return knowledge_module.create_article(self.sfdc_object, article_data=article_data, sobject=sobject, full_response=full_response)
[docs] def update_article( self, record_id: str, article_data: dict, sobject: Optional[str] = None, include_status_code: bool = False, ): """This method updates an existing knowledge article draft. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm>`_) :param record_id: The ID of the article draft record to be updated :type record_id: str :param article_data: The article data used to update the article :type article_data: dict :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param include_status_code: Determines if the API response status code should be returned (``False`` by default) :type include_status_code: bool :returns: A Boolean indicating if the update operation was successful, and optionally the API response status code :raises: :py:exc:`ValueError`, :py:exc:`TypeError`, :py:exc:`RuntimeError` """ return knowledge_module.update_article(self.sfdc_object, record_id=record_id, article_data=article_data, sobject=sobject, include_status_code=include_status_code)
[docs] def create_draft_from_online_article(self, article_id: str, unpublish: bool = False): """This method creates a draft knowledge article from an online article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/actions_obj_knowledge.htm#createDraftFromOnlineKnowledgeArticle>`_) :param article_id: The ID of the online article from which to create the draft :type article_id: str :param unpublish: Determines if the online article should be unpublished when the draft is created (``False`` by default) :type unpublish: bool :returns: The API response from the POST request :raises: :py:exc:`RuntimeError` """ return knowledge_module.create_draft_from_online_article(self.sfdc_object, article_id=article_id, unpublish=unpublish)
[docs] def create_draft_from_master_version( self, article_id: Optional[str] = None, knowledge_article_id: Optional[str] = None, article_data: Optional[dict] = None, sobject: Optional[str] = None, full_response: bool = False, ): """This method creates an online version of a master article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.198.0.knowledge_dev.meta/knowledge_dev/knowledge_REST_edit_online_master.htm>`_) :param article_id: The Article ID from which to create the draft :type article_id: str, None :param knowledge_article_id: The Knowledge Article ID (``KnowledgeArticleId``) from which to create the draft :type knowledge_article_id: str, None :param article_data: The article data associated with the article from which to create the draft :type article_data: dict, None :param sobject: The Salesforce object to query (``Knowledge__kav`` by default) :type sobject: str, None :param full_response: Determines if the full API response should be returned instead of the article ID (``False`` by default) :type full_response: bool :returns: The API response or the ID of the article draft :raises: :py:exc:`RuntimeError` """ return knowledge_module.create_draft_from_master_version(self.sfdc_object, article_id=article_id, knowledge_article_id=knowledge_article_id, article_data=article_data, sobject=sobject, full_response=full_response)
[docs] def publish_article(self, article_id, major_version=True, full_response=False): """This method publishes a draft knowledge article as a major or minor version. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_publish_master_version.htm>`_) :param article_id: The Article ID to publish :type article_id: str :param major_version: Determines if the published article should be a major version (``True`` by default) :type major_version: bool :param full_response: Determines if the full API response should be returned (``False`` by default) :type full_response: bool :returns: A Boolean value indicating the success of the action or the API response from the PATCH request :raises: :py:exc:`RuntimeError` """ return knowledge_module.publish_article(self.sfdc_object, article_id=article_id, major_version=major_version, full_response=full_response)
[docs] def publish_multiple_articles(self, article_id_list, major_version=True): """This method publishes multiple knowledge article drafts at one time. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/actions_obj_knowledge.htm#publishKnowledgeArticles>`_) :param article_id_list: A list of Article IDs to be published :type article_id_list: list :param major_version: Determines if the published article should be a major version (``True`` by default) :type major_version: bool :returns: The API response from the POST request :raises: :py:exc:`RuntimeError`, :py:exc:`TypeError`, :py:exc:`ValueError` """ return knowledge_module.publish_multiple_articles(self.sfdc_object, article_id_list=article_id_list, major_version=major_version)
[docs] def assign_data_category(self, article_id, category_group_name, category_name): """This method assigns a single data category for a knowledge article. (`Reference <https://itsmemohit.medium.com/quick-win-15-salesforce-knowledge-rest-apis-bb0725b2040e>`_) .. versionadded:: 1.2.0 :param article_id: The ID of the article to update :type article_id: str :param category_group_name: The unique Data Category Group Name :type category_group_name: str :param category_name: The unique Data Category Name :type category_name: str :returns: The API response from the POST request :raises: :py:exc:`RuntimeError` """ return knowledge_module.assign_data_category(self.sfdc_object, article_id=article_id, category_group_name=category_group_name, category_name=category_name)
[docs] def archive_article(self, article_id): """This function archives a published knowledge article. (`Reference <https://developer.salesforce.com/docs/atlas.en-us.knowledge_dev.meta/knowledge_dev/knowledge_REST_archive_master_version.htm>`_) .. versionadded:: 1.3.0 :param article_id: The ID of the article to archive :type article_id: str :returns: The API response from the POST request :raises: :py:exc:`RuntimeError` """ return knowledge_module.archive_article(self.sfdc_object, article_id=article_id)
[docs] def delete_article_draft(self, version_id: str, use_knowledge_management_endpoint: bool = True): """This function deletes an unpublished knowledge article draft. .. versionadded:: 1.4.0 :param version_id: The 15-character or 18-character ``Id`` (Knowledge Article Version ID) value :type version_id: str :param use_knowledge_management_endpoint: Leverage the ``/knowledgeManagement/articleVersions/masterVersions/`` endpoint rather than the ``/sobjects/Knowledge__kav/`` endpoint (``True`` by default) :type use_knowledge_management_endpoint: bool :returns: The API response from the DELETE request :raises: :py:exc:`RuntimeError` """ return knowledge_module.delete_article_draft(self.sfdc_object, version_id=version_id, use_knowledge_management_endpoint=use_knowledge_management_endpoint)
[docs] def define_connection_info(): """This function prompts the user for the connection information. :returns: The connection info in a dictionary """ base_url = input('Enter your instance URL: [] ') org_id = input('Enter the Org ID for your instance: [] ') username = input('Enter the username of your API user: [] ') password = input('Enter the password of your API user: [] ') endpoint_url = input('Enter the endpoint URL: [] ') client_id = input('Enter the Client ID: [] ') client_secret = input('Enter the Client Secret: [] ') security_token = input('Enter the Security Token: [] ') connection_info = compile_connection_info(base_url, org_id, username, password, endpoint_url, client_id, client_secret, security_token) return connection_info
[docs] def compile_connection_info(base_url, org_id, username, password, endpoint_url, client_id, client_secret, security_token): """This function compiles the connection info into a dictionary that can be consumed by the core object. :param base_url: The base URL of the Salesforce instance :type base_url: str :param org_id: The Org ID of the Salesforce instance :type org_id: str :param username: The username of the API user :type username: str :param password: The password of the API user :type password: str :param endpoint_url: The endpoint URL for the Salesforce instance :type endpoint_url: str :param client_id: The Client ID for the Salesforce instance :type client_id: str :param client_secret: The Client Secret for the Salesforce instance :type client_secret: str :param security_token: The Security Token for the Salesforce instance :type security_token: str :returns: The connection info in a dictionary """ connection_info = { 'base_url': base_url, 'org_id': org_id, 'username': username, 'password': password, 'endpoint_url': endpoint_url, 'client_id': client_id, 'client_secret': client_secret, 'security_token': security_token, } return connection_info