salespyforce 1.3.0rc3__tar.gz → 1.4.0.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {salespyforce-1.3.0rc3/src/salespyforce.egg-info → salespyforce-1.4.0.dev1}/PKG-INFO +46 -23
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/README.md +5 -1
- salespyforce-1.4.0.dev1/pyproject.toml +62 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/core.py +35 -11
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/log_utils.py +10 -9
- salespyforce-1.4.0.dev1/src/salespyforce/utils/tests/conftest.py +202 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/tests/resources.py +2 -2
- salespyforce-1.4.0.dev1/src/salespyforce/utils/tests/test_core_utils.py +195 -0
- salespyforce-1.4.0.dev1/src/salespyforce/utils/tests/test_instantiate_object.py +62 -0
- salespyforce-1.4.0.dev1/src/salespyforce/utils/tests/test_log_utils.py +79 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/tests/test_sobjects.py +22 -15
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/tests/test_soql.py +8 -8
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/tests/test_sosl.py +9 -9
- salespyforce-1.4.0.dev1/src/salespyforce/utils/tests/test_version_utils.py +70 -0
- salespyforce-1.4.0.dev1/src/salespyforce/utils/version.py +52 -0
- salespyforce-1.3.0rc3/PKG-INFO +0 -234
- salespyforce-1.3.0rc3/pyproject.toml +0 -20
- salespyforce-1.3.0rc3/setup.cfg +0 -4
- salespyforce-1.3.0rc3/setup.py +0 -89
- salespyforce-1.3.0rc3/src/salespyforce/utils/tests/test_instantiate_object.py +0 -49
- salespyforce-1.3.0rc3/src/salespyforce/utils/version.py +0 -26
- salespyforce-1.3.0rc3/src/salespyforce.egg-info/SOURCES.txt +0 -28
- salespyforce-1.3.0rc3/src/salespyforce.egg-info/dependency_links.txt +0 -1
- salespyforce-1.3.0rc3/src/salespyforce.egg-info/requires.txt +0 -13
- salespyforce-1.3.0rc3/src/salespyforce.egg-info/top_level.txt +0 -1
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/LICENSE +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/__init__.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/api.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/chatter.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/errors/__init__.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/errors/exceptions.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/errors/handlers.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/knowledge.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/__init__.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/core_utils.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/helper.py +0 -0
- {salespyforce-1.3.0rc3 → salespyforce-1.4.0.dev1}/src/salespyforce/utils/tests/__init__.py +0 -0
|
@@ -1,33 +1,51 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: salespyforce
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0.dev1
|
|
4
4
|
Summary: A Python toolset for performing Salesforce API calls
|
|
5
|
-
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2023 Jeff Shurtliff
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
Keywords: salesforce,sfdc,api,rest,tooling,python
|
|
6
27
|
Author: Jeff Shurtliff
|
|
7
|
-
Author-email:
|
|
8
|
-
|
|
9
|
-
Classifier: Development Status ::
|
|
10
|
-
Classifier:
|
|
11
|
-
Classifier: Intended Audience :: Information Technology
|
|
12
|
-
Classifier: Intended Audience :: System Administrators
|
|
28
|
+
Author-email: jeffshurtliff@gmail.com
|
|
29
|
+
Requires-Python: >=3.9,<4.0
|
|
30
|
+
Classifier: Development Status :: 4 - Beta
|
|
31
|
+
Classifier: Intended Audience :: Developers
|
|
13
32
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: Natural Language :: English
|
|
15
|
-
Classifier: Operating System :: OS Independent
|
|
16
33
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
34
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
35
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
36
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
-
Classifier:
|
|
23
|
-
Classifier:
|
|
24
|
-
Classifier:
|
|
25
|
-
Classifier: Topic ::
|
|
26
|
-
Classifier: Topic ::
|
|
27
|
-
Requires-
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
41
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
42
|
+
Requires-Dist: PyYAML (>=6.0.3,<7)
|
|
43
|
+
Requires-Dist: requests (>=2.32.5)
|
|
44
|
+
Project-URL: Documentation, https://salespyforce.readthedocs.io/en/latest/
|
|
45
|
+
Project-URL: Homepage, https://github.com/jeffshurtliff/salespyforce
|
|
46
|
+
Project-URL: Issue Tracker, https://github.com/jeffshurtliff/salespyforce/issues
|
|
47
|
+
Project-URL: Repository, https://github.com/jeffshurtliff/salespyforce
|
|
28
48
|
Description-Content-Type: text/markdown
|
|
29
|
-
Provides-Extra: sphinx
|
|
30
|
-
License-File: LICENSE
|
|
31
49
|
|
|
32
50
|
# salespyforce
|
|
33
51
|
A Python toolset for performing Salesforce API calls
|
|
@@ -45,7 +63,7 @@ A Python toolset for performing Salesforce API calls
|
|
|
45
63
|
<td>Latest Beta/RC Release</td>
|
|
46
64
|
<td>
|
|
47
65
|
<a href='https://pypi.org/project/salespyforce/#history'>
|
|
48
|
-
<img alt="PyPI" src="https://img.shields.io/badge/pypi-1.
|
|
66
|
+
<img alt="PyPI" src="https://img.shields.io/badge/pypi-1.4.0.dev1-blue">
|
|
49
67
|
</a>
|
|
50
68
|
</td>
|
|
51
69
|
</tr>
|
|
@@ -122,6 +140,10 @@ A Python toolset for performing Salesforce API calls
|
|
|
122
140
|
</tr>
|
|
123
141
|
</table>
|
|
124
142
|
|
|
143
|
+
## Overview
|
|
144
|
+
salespyforce is a Python toolkit focused on interacting with Salesforce APIs, with a primary `Salesforce` class
|
|
145
|
+
that centralizes authentication, version selection, and access to helper feature sets (Chatter, Knowledge).
|
|
146
|
+
|
|
125
147
|
## Installation
|
|
126
148
|
The package can be installed via pip using the syntax below.
|
|
127
149
|
|
|
@@ -232,3 +254,4 @@ If you would like to donate to this project then you can do so using [this PayPa
|
|
|
232
254
|
|
|
233
255
|
## Disclaimer
|
|
234
256
|
This package is considered unofficial and is in no way endorsed or supported by [Salesforce Inc](https://www.salesforce.com).
|
|
257
|
+
|
|
@@ -14,7 +14,7 @@ A Python toolset for performing Salesforce API calls
|
|
|
14
14
|
<td>Latest Beta/RC Release</td>
|
|
15
15
|
<td>
|
|
16
16
|
<a href='https://pypi.org/project/salespyforce/#history'>
|
|
17
|
-
<img alt="PyPI" src="https://img.shields.io/badge/pypi-1.
|
|
17
|
+
<img alt="PyPI" src="https://img.shields.io/badge/pypi-1.4.0.dev1-blue">
|
|
18
18
|
</a>
|
|
19
19
|
</td>
|
|
20
20
|
</tr>
|
|
@@ -91,6 +91,10 @@ A Python toolset for performing Salesforce API calls
|
|
|
91
91
|
</tr>
|
|
92
92
|
</table>
|
|
93
93
|
|
|
94
|
+
## Overview
|
|
95
|
+
salespyforce is a Python toolkit focused on interacting with Salesforce APIs, with a primary `Salesforce` class
|
|
96
|
+
that centralizes authentication, version selection, and access to helper feature sets (Chatter, Knowledge).
|
|
97
|
+
|
|
94
98
|
## Installation
|
|
95
99
|
The package can be installed via pip using the syntax below.
|
|
96
100
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "salespyforce"
|
|
3
|
+
version = "1.4.0.dev1"
|
|
4
|
+
description = "A Python toolset for performing Salesforce API calls"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.9,<4.0"
|
|
7
|
+
license = { file = "LICENSE" }
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Jeff Shurtliff", email = "jeffshurtliff@gmail.com" }
|
|
10
|
+
]
|
|
11
|
+
keywords = ["salesforce", "sfdc", "api", "rest", "tooling", "python"]
|
|
12
|
+
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
"Topic :: Internet :: WWW/HTTP"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
dependencies = [
|
|
29
|
+
"PyYAML>=6.0.3,<7",
|
|
30
|
+
"requests>=2.32.5",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/jeffshurtliff/salespyforce"
|
|
35
|
+
Repository = "https://github.com/jeffshurtliff/salespyforce"
|
|
36
|
+
Documentation = "https://salespyforce.readthedocs.io/en/latest/"
|
|
37
|
+
"Issue Tracker" = "https://github.com/jeffshurtliff/salespyforce/issues"
|
|
38
|
+
|
|
39
|
+
# --------------------------------------------------------------------
|
|
40
|
+
# Poetry-specific config
|
|
41
|
+
# --------------------------------------------------------------------
|
|
42
|
+
[tool.poetry]
|
|
43
|
+
packages = [
|
|
44
|
+
{ include = "salespyforce", from = "src" }
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
include = ["LICENSE", "README.md"]
|
|
48
|
+
|
|
49
|
+
[tool.poetry.group.dev.dependencies]
|
|
50
|
+
pytest = "^7.2"
|
|
51
|
+
bandit = { version = "^1.7.8", extras = ["sarif"] }
|
|
52
|
+
# Sphinx = "^7.0"
|
|
53
|
+
# sphinxcontrib-applehelp = ">=1.0.2"
|
|
54
|
+
# sphinxcontrib-devhelp = ">=1.0.2"
|
|
55
|
+
# sphinxcontrib-htmlhelp = ">=2.0.0"
|
|
56
|
+
# sphinxcontrib-jsmath = ">=1.0.1"
|
|
57
|
+
# sphinxcontrib-qthelp = ">=1.0.3"
|
|
58
|
+
# sphinxcontrib-serializinghtml = ">=1.1.4"
|
|
59
|
+
|
|
60
|
+
[build-system]
|
|
61
|
+
requires = ["poetry-core>=1.2.0"]
|
|
62
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
:Module: salespyforce.core
|
|
4
4
|
:Synopsis: This module performs the core Salesforce-related operations
|
|
5
5
|
:Usage: ``from salespyforce import Salesforce``
|
|
6
|
-
:Example: ``sfdc = Salesforce()``
|
|
6
|
+
:Example: ``sfdc = Salesforce(helper=helper_file_path)``
|
|
7
7
|
:Created By: Jeff Shurtliff
|
|
8
8
|
:Last Modified: Jeff Shurtliff
|
|
9
|
-
:Modified Date:
|
|
9
|
+
:Modified Date: 17 Nov 2025
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import re
|
|
@@ -20,7 +20,7 @@ from .utils import core_utils, log_utils
|
|
|
20
20
|
from .utils.helper import get_helper_settings
|
|
21
21
|
|
|
22
22
|
# Define constants
|
|
23
|
-
|
|
23
|
+
FALLBACK_SFDC_API_VERSION = '65.0' # Used if querying the org for the version fails
|
|
24
24
|
|
|
25
25
|
# Initialize logging
|
|
26
26
|
logger = log_utils.initialize_logging(__name__)
|
|
@@ -29,14 +29,18 @@ logger = log_utils.initialize_logging(__name__)
|
|
|
29
29
|
class Salesforce(object):
|
|
30
30
|
"""This is the class for the core object leveraged in this module."""
|
|
31
31
|
# Define the function that initializes the object instance (i.e. instantiates the object)
|
|
32
|
-
def __init__(self, connection_info=None, version=
|
|
32
|
+
def __init__(self, connection_info=None, version=None, base_url=None, org_id=None, username=None,
|
|
33
33
|
password=None, endpoint_url=None, client_id=None, client_secret=None, security_token=None, helper=None):
|
|
34
34
|
"""This method instantiates the core Salesforce object.
|
|
35
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
|
+
|
|
36
40
|
:param connection_info: The information for connecting to the Salesforce instance
|
|
37
41
|
:type connection_info: dict, None
|
|
38
|
-
:param version: The Salesforce API version to utilize (
|
|
39
|
-
:type version: str
|
|
42
|
+
:param version: The Salesforce API version to utilize (uses latest version from org if not explicitly defined)
|
|
43
|
+
:type version: str, None
|
|
40
44
|
:param base_url: The base URL of the Salesforce instance
|
|
41
45
|
:type base_url: str, None
|
|
42
46
|
:param org_id: The Org ID of the Salesforce instance
|
|
@@ -93,15 +97,15 @@ class Salesforce(object):
|
|
|
93
97
|
# Define the base URL value
|
|
94
98
|
self.base_url = self.connection_info.get('base_url')
|
|
95
99
|
|
|
96
|
-
# Define the version value
|
|
97
|
-
self.version = f'v{version}'
|
|
98
|
-
|
|
99
100
|
# Define the connection response data variables
|
|
100
101
|
auth_response = self.connect()
|
|
101
102
|
self.access_token = auth_response.get('access_token')
|
|
102
103
|
self.instance_url = auth_response.get('instance_url')
|
|
103
104
|
self.signature = auth_response.get('signature')
|
|
104
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
|
+
|
|
105
109
|
# Import inner object classes so their methods can be called from the primary object
|
|
106
110
|
self.chatter = self._import_chatter_class()
|
|
107
111
|
self.knowledge = self._import_knowledge_class()
|
|
@@ -271,12 +275,33 @@ class Salesforce(object):
|
|
|
271
275
|
headers=headers, timeout=timeout, show_full_error=show_full_error,
|
|
272
276
|
return_json=return_json)
|
|
273
277
|
|
|
274
|
-
def get_api_versions(self):
|
|
278
|
+
def get_api_versions(self) -> list:
|
|
275
279
|
"""This method returns the API versions for the Salesforce releases.
|
|
276
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.
|
|
277
283
|
"""
|
|
278
284
|
return self.get('/services/data')
|
|
279
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
|
+
|
|
280
305
|
def get_org_limits(self):
|
|
281
306
|
"""This method returns a list of all org limits.
|
|
282
307
|
|
|
@@ -357,7 +382,6 @@ class Salesforce(object):
|
|
|
357
382
|
query = core_utils.url_encode(query)
|
|
358
383
|
return self.get(f'/services/data/{self.version}/search/?q={query}')
|
|
359
384
|
|
|
360
|
-
|
|
361
385
|
def create_sobject_record(self, sobject, payload):
|
|
362
386
|
"""This method creates a new record for a specific sObject.
|
|
363
387
|
(`Reference <https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_create.htm>`_)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:Example: ``logger = log_utils.initialize_logging(__name__)``
|
|
7
7
|
:Created By: Jeff Shurtliff
|
|
8
8
|
:Last Modified: Jeff Shurtliff
|
|
9
|
-
:Modified Date:
|
|
9
|
+
:Modified Date: 31 Dec 2025
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -59,7 +59,7 @@ class LessThanFilter(logging.Filter):
|
|
|
59
59
|
self.max_level = exclusive_maximum
|
|
60
60
|
|
|
61
61
|
def filter(self, record):
|
|
62
|
-
"""This method returns a Boolean integer value indicating whether
|
|
62
|
+
"""This method returns a Boolean integer value indicating whether a message should be logged.
|
|
63
63
|
|
|
64
64
|
.. note:: A non-zero return indicates that the message will be logged.
|
|
65
65
|
"""
|
|
@@ -69,6 +69,9 @@ class LessThanFilter(logging.Filter):
|
|
|
69
69
|
def _apply_defaults(_logger_name, _formatter, _debug, _log_level, _file_level, _console_level, _syslog_level):
|
|
70
70
|
"""This function applies default values to the configuration settings if not explicitly defined.
|
|
71
71
|
|
|
72
|
+
.. version-changed:: 1.4.0
|
|
73
|
+
The default logging level is now defined.
|
|
74
|
+
|
|
72
75
|
:param _logger_name: The name of the logger instance
|
|
73
76
|
:type _logger_name: str, None
|
|
74
77
|
:param _formatter: The log format to utilize for the logger instance
|
|
@@ -79,8 +82,9 @@ def _apply_defaults(_logger_name, _formatter, _debug, _log_level, _file_level, _
|
|
|
79
82
|
:type _log_level: str, None
|
|
80
83
|
:returns: The values that will be used for the configuration settings
|
|
81
84
|
"""
|
|
85
|
+
_default_log_level = LOGGING_DEFAULTS.get('log_level')
|
|
82
86
|
_log_levels = {
|
|
83
|
-
'general': _log_level,
|
|
87
|
+
'general': _log_level or _default_log_level,
|
|
84
88
|
'file': _file_level,
|
|
85
89
|
'console': _console_level,
|
|
86
90
|
'syslog': _syslog_level,
|
|
@@ -90,12 +94,9 @@ def _apply_defaults(_logger_name, _formatter, _debug, _log_level, _file_level, _
|
|
|
90
94
|
for _log_type in _log_levels:
|
|
91
95
|
_log_levels[_log_type] = 'debug'
|
|
92
96
|
else:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
_log_levels[_lvl_type] = _log_level
|
|
97
|
-
else:
|
|
98
|
-
_log_level = LOGGING_DEFAULTS.get('log_level')
|
|
97
|
+
for _lvl_type, _lvl_value in _log_levels.items():
|
|
98
|
+
if _lvl_value is None:
|
|
99
|
+
_log_levels[_lvl_type] = _log_levels['general']
|
|
99
100
|
if _formatter and isinstance(_formatter, str):
|
|
100
101
|
_formatter = logging.Formatter(_formatter)
|
|
101
102
|
_formatter = LOGGING_DEFAULTS.get('formatter') if not _formatter else _formatter
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
:Module: salespyforce.utils.tests.conftest
|
|
4
|
+
:Synopsis: Configuration for performing unit testing with pytest
|
|
5
|
+
:Usage: Leveraged by pytest in test modules
|
|
6
|
+
:Example: ``soql_response = salesforce_unit.soql_query(soql_statement)``
|
|
7
|
+
:Created By: Jeff Shurtliff
|
|
8
|
+
:Last Modified: Jeff Shurtliff
|
|
9
|
+
:Modified Date: 13 Dec 2025
|
|
10
|
+
|
|
11
|
+
Pytest fixtures for ``salespyforce.utils.tests``.
|
|
12
|
+
|
|
13
|
+
This module centralizes helpers used across the test suite to avoid
|
|
14
|
+
repeated setup in individual test files. It introduces two key fixtures:
|
|
15
|
+
|
|
16
|
+
* ``salesforce_integration`` — Instantiates the real ``Salesforce``
|
|
17
|
+
client when a helper file is available. Tests using this fixture are
|
|
18
|
+
marked as ``integration`` and are skipped unless ``--integration`` is
|
|
19
|
+
provided.
|
|
20
|
+
* ``salesforce_unit`` — Provides a lightweight stub that mimics the
|
|
21
|
+
public API used by existing tests without performing any network I/O.
|
|
22
|
+
|
|
23
|
+
The goal is to make it easy to switch between fast, isolated unit tests
|
|
24
|
+
and opt-in integration runs against a real Salesforce org. Additional
|
|
25
|
+
fixtures can be added here to share common mocking or configuration.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import os
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from types import SimpleNamespace
|
|
33
|
+
from typing import Iterator
|
|
34
|
+
|
|
35
|
+
import pytest
|
|
36
|
+
|
|
37
|
+
from salespyforce.core import Salesforce
|
|
38
|
+
|
|
39
|
+
# Define constants
|
|
40
|
+
HELPER_FILE_NAME = "helper_dm_conn.yml"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# -----------------------------
|
|
44
|
+
# Pytest configuration hooks
|
|
45
|
+
# -----------------------------
|
|
46
|
+
|
|
47
|
+
def pytest_addoption(parser: pytest.Parser) -> None:
|
|
48
|
+
"""This function registers custom CLI options.
|
|
49
|
+
|
|
50
|
+
.. version-added:: 1.4.0
|
|
51
|
+
|
|
52
|
+
``--integration`` enables tests that require access to a real
|
|
53
|
+
Salesforce org. Keeping this opt-in protects routine runs from
|
|
54
|
+
network or credential dependencies.
|
|
55
|
+
"""
|
|
56
|
+
parser.addoption(
|
|
57
|
+
"--integration",
|
|
58
|
+
action="store_true",
|
|
59
|
+
default=False,
|
|
60
|
+
help="run tests that require a Salesforce helper file",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def pytest_configure(config: pytest.Config) -> None:
|
|
65
|
+
"""This function declares custom markers so pytest will not warn during collection.
|
|
66
|
+
|
|
67
|
+
.. version-added:: 1.4.0
|
|
68
|
+
"""
|
|
69
|
+
config.addinivalue_line(
|
|
70
|
+
"markers",
|
|
71
|
+
"integration: marks tests that require a real Salesforce org",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def pytest_collection_modifyitems(
|
|
76
|
+
config: pytest.Config, items: list[pytest.Item]
|
|
77
|
+
) -> None:
|
|
78
|
+
"""This function skips integration tests when ``--integration`` is not provided.
|
|
79
|
+
|
|
80
|
+
.. version-added:: 1.4.0
|
|
81
|
+
"""
|
|
82
|
+
if config.getoption("--integration"):
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
skip_integration = pytest.mark.skip(
|
|
86
|
+
reason="requires --integration to run against a Salesforce org"
|
|
87
|
+
)
|
|
88
|
+
for item in items:
|
|
89
|
+
if "integration" in item.keywords:
|
|
90
|
+
item.add_marker(skip_integration)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# -----------------------------
|
|
94
|
+
# Helper utilities
|
|
95
|
+
# -----------------------------
|
|
96
|
+
|
|
97
|
+
def _find_helper_file() -> Path | None:
|
|
98
|
+
"""This function locates a helper file in common locations used by this project.
|
|
99
|
+
|
|
100
|
+
.. version-added:: 1.4.0
|
|
101
|
+
"""
|
|
102
|
+
helper_locations = [
|
|
103
|
+
Path(os.environ.get("HOME", "")) / "secrets" / HELPER_FILE_NAME,
|
|
104
|
+
Path("local") / HELPER_FILE_NAME,
|
|
105
|
+
]
|
|
106
|
+
for helper_path in helper_locations:
|
|
107
|
+
if helper_path.is_file():
|
|
108
|
+
return helper_path.resolve()
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# -----------------------------
|
|
113
|
+
# Fixtures
|
|
114
|
+
# -----------------------------
|
|
115
|
+
|
|
116
|
+
@pytest.fixture(scope="session")
|
|
117
|
+
def integration_helper_file() -> Path:
|
|
118
|
+
"""This fixture returns the helper file path or skips the test if none is available.
|
|
119
|
+
|
|
120
|
+
.. version-added:: 1.4.0
|
|
121
|
+
|
|
122
|
+
Keeping this lookup in a session-scoped fixture ensures we only
|
|
123
|
+
perform filesystem checks once per test run and provides a single
|
|
124
|
+
source of truth for integration tests.
|
|
125
|
+
"""
|
|
126
|
+
helper_path = _find_helper_file()
|
|
127
|
+
if helper_path is None:
|
|
128
|
+
pytest.skip("No Salesforce helper file found for integration tests")
|
|
129
|
+
return helper_path
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@pytest.fixture(scope="session")
|
|
133
|
+
def salesforce_integration(integration_helper_file: Path) -> Iterator[Salesforce]:
|
|
134
|
+
"""This fixture instantiates the real Salesforce client for integration tests.
|
|
135
|
+
|
|
136
|
+
.. version-added:: 1.4.0
|
|
137
|
+
|
|
138
|
+
The fixture is session-scoped to avoid repeated authentication and
|
|
139
|
+
to reuse connections across tests. It is intended only for tests
|
|
140
|
+
marked with ``@pytest.mark.integration``.
|
|
141
|
+
"""
|
|
142
|
+
client = Salesforce(helper=str(integration_helper_file))
|
|
143
|
+
yield client
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@pytest.fixture()
|
|
147
|
+
def salesforce_unit(monkeypatch: pytest.MonkeyPatch) -> SimpleNamespace:
|
|
148
|
+
"""This fixture provides a lightweight stub that mimics the ``Salesforce`` API.
|
|
149
|
+
|
|
150
|
+
.. version-added:: 1.4.0
|
|
151
|
+
|
|
152
|
+
This fixture avoids network calls by supplying deterministic return
|
|
153
|
+
values for the subset of methods exercised by the current tests.
|
|
154
|
+
It can be extended as coverage grows to keep unit tests fast and
|
|
155
|
+
self-contained.
|
|
156
|
+
"""
|
|
157
|
+
# Minimal data used across tests
|
|
158
|
+
sample_urls = {
|
|
159
|
+
"base_url": "https://example.force.com",
|
|
160
|
+
"rest_resources": {"metadata": "available"},
|
|
161
|
+
"org_limits": {"DailyApiRequests": {"Remaining": 15000}},
|
|
162
|
+
"sobjects": {"sobjects": []},
|
|
163
|
+
"account": {
|
|
164
|
+
"objectDescribe": {},
|
|
165
|
+
"activateable": False,
|
|
166
|
+
},
|
|
167
|
+
"soql_result": {
|
|
168
|
+
"done": True,
|
|
169
|
+
"totalSize": 1,
|
|
170
|
+
"records": [{"Id": "001XX000003NGqqYAG"}],
|
|
171
|
+
},
|
|
172
|
+
"sosl_result": {
|
|
173
|
+
"searchRecords": [
|
|
174
|
+
{"attributes": {"type": "Account"}, "Id": "001XX000003NGqqYAG"}
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
def _create_response(**overrides):
|
|
180
|
+
"""This function creates an API response payload mimicking the Salesforce REST API responses.
|
|
181
|
+
|
|
182
|
+
.. version-added:: 1.4.0
|
|
183
|
+
"""
|
|
184
|
+
response = {"id": "001D000000IqhSLIAZ", "success": True, "errors": []}
|
|
185
|
+
response.update(overrides)
|
|
186
|
+
return response
|
|
187
|
+
|
|
188
|
+
stub = SimpleNamespace()
|
|
189
|
+
stub.base_url = sample_urls["base_url"]
|
|
190
|
+
stub.get_api_versions = lambda: [{"version": "v65.0"}]
|
|
191
|
+
stub.get_rest_resources = lambda: sample_urls["rest_resources"]
|
|
192
|
+
stub.get_org_limits = lambda: sample_urls["org_limits"]
|
|
193
|
+
stub.get_all_sobjects = lambda: sample_urls["sobjects"]
|
|
194
|
+
stub.get_sobject = lambda *_args, **_kwargs: sample_urls["account"]
|
|
195
|
+
stub.describe_object = lambda *_args, **_kwargs: sample_urls["account"]
|
|
196
|
+
stub.create_sobject_record = (
|
|
197
|
+
lambda *_args, **_kwargs: _create_response()
|
|
198
|
+
)
|
|
199
|
+
stub.soql_query = lambda *_args, **_kwargs: sample_urls["soql_result"]
|
|
200
|
+
stub.search_string = lambda *_args, **_kwargs: sample_urls["sosl_result"]
|
|
201
|
+
|
|
202
|
+
return stub
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
:Example: ``exceptions = resources.import_exceptions_module()``
|
|
7
7
|
:Created By: Jeff Shurtliff
|
|
8
8
|
:Last Modified: Jeff Shurtliff
|
|
9
|
-
:Modified Date:
|
|
9
|
+
:Modified Date: 14 Nov 2025
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -17,7 +17,7 @@ import pytest
|
|
|
17
17
|
|
|
18
18
|
# Define constants
|
|
19
19
|
SKIP_LOCAL_TEST_MSG = 'skipping local-only tests'
|
|
20
|
-
HELPER_FILE_NAME = '
|
|
20
|
+
HELPER_FILE_NAME = 'helper_dm_conn.yml'
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class MockResponse:
|