sfq 0.0.1__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.
sfq/__init__.py ADDED
@@ -0,0 +1,137 @@
1
+ import http.client
2
+ import logging
3
+ import time
4
+ import json
5
+ from urllib.parse import urlparse, quote
6
+
7
+ logging.basicConfig(level=logging.INFO)
8
+
9
+ class SFAuth:
10
+ def __init__(
11
+ self,
12
+ instance_url,
13
+ client_id,
14
+ refresh_token,
15
+ api_version="v62.0",
16
+ token_endpoint="/services/oauth2/token",
17
+ access_token=None,
18
+ token_expiration_time=None,
19
+ token_lifetime=15 * 60,
20
+ ):
21
+ """
22
+ Initializes the SFAuth with necessary parameters.
23
+
24
+ :param instance_url: The Salesforce instance URL.
25
+ :param client_id: The client ID for OAuth.
26
+ :param refresh_token: The refresh token for OAuth.
27
+ :param api_version: The Salesforce API version (default is "v62.0").
28
+ :param token_endpoint: The token endpoint (default is "/services/oauth2/token").
29
+ :param access_token: The access token for the current session (default is None).
30
+ :param token_expiration_time: The expiration time of the access token (default is None).
31
+ :param token_lifetime: The lifetime of the access token (default is 15 minutes).
32
+ """
33
+ self.instance_url = instance_url
34
+ self.client_id = client_id
35
+ self.refresh_token = refresh_token
36
+ self.api_version = api_version
37
+ self.token_endpoint = token_endpoint
38
+ self.access_token = access_token
39
+ self.token_expiration_time = token_expiration_time
40
+ self.token_lifetime = token_lifetime
41
+
42
+ def _prepare_payload(self):
43
+ """Prepare the payload for the token request."""
44
+ return {
45
+ "grant_type": "refresh_token",
46
+ "client_id": self.client_id,
47
+ "refresh_token": self.refresh_token,
48
+ }
49
+
50
+ def _send_post_request(self, payload):
51
+ """Send a POST request to the Salesforce token endpoint using http.client."""
52
+ parsed_url = urlparse(self.instance_url)
53
+ conn = http.client.HTTPSConnection(parsed_url.netloc)
54
+
55
+ headers = {"Content-Type": "application/x-www-form-urlencoded"}
56
+ body = "&".join([f"{key}={value}" for key, value in payload.items()])
57
+
58
+ try:
59
+ conn.request("POST", self.token_endpoint, body, headers)
60
+ response = conn.getresponse()
61
+ data = response.read().decode("utf-8")
62
+ if response.status == 200:
63
+ return json.loads(data)
64
+ else:
65
+ logging.error(
66
+ f"HTTP error occurred: {response.status} {response.reason}"
67
+ )
68
+ logging.error(f"Response content: {data}")
69
+ except Exception as err:
70
+ logging.error(f"Other error occurred: {err}")
71
+ finally:
72
+ conn.close()
73
+
74
+ return None
75
+
76
+ def _refresh_token_if_needed(self):
77
+ """Automatically refresh the token if it has expired or is missing."""
78
+ _token_expiration = self._is_token_expired
79
+ if self.access_token and not _token_expiration:
80
+ return
81
+
82
+ if not self.access_token:
83
+ logging.debug("No access token available. Requesting a new one.")
84
+ elif _token_expiration:
85
+ logging.debug("Access token has expired. Requesting a new one.")
86
+
87
+ payload = self._prepare_payload()
88
+ token_data = self._send_post_request(payload)
89
+ if token_data:
90
+ self.access_token = token_data["access_token"]
91
+ self.token_expiration_time = int(token_data["issued_at"]) + int(self.token_lifetime)
92
+ logging.debug("Access token refreshed successfully.")
93
+ else:
94
+ logging.error("Failed to refresh access token.")
95
+
96
+
97
+ def _is_token_expired(self):
98
+ """Check if the access token has expired."""
99
+ return time.time() >= self.token_expiration_time
100
+
101
+ def query(self, query, tooling=False):
102
+ """Query Salesforce using SOQL or Tooling API, depending on the `tooling` parameter."""
103
+ self._refresh_token_if_needed()
104
+
105
+ if not self.access_token:
106
+ logging.error("No access token available to make the query.")
107
+ return None
108
+
109
+ if tooling:
110
+ query_endpoint = f"/services/data/{self.api_version}/tooling/query"
111
+ else:
112
+ query_endpoint = f"/services/data/{self.api_version}/query"
113
+
114
+ headers = {"Authorization": f"Bearer {self.access_token}"}
115
+
116
+ # Handle special characters in the query
117
+ encoded_query = quote(query)
118
+ params = f"?q={encoded_query}"
119
+
120
+ parsed_url = urlparse(self.instance_url)
121
+ conn = http.client.HTTPSConnection(parsed_url.netloc)
122
+
123
+ try:
124
+ conn.request("GET", query_endpoint + params, headers=headers)
125
+ response = conn.getresponse()
126
+ data = response.read().decode("utf-8")
127
+ if response.status == 200:
128
+ return json.loads(data)
129
+ else:
130
+ logging.error(f"HTTP error occurred during query: {response.status} {response.reason}")
131
+ logging.error(f"Response content: {data}")
132
+ except Exception as err:
133
+ logging.error(f"Other error occurred during query: {err}")
134
+ finally:
135
+ conn.close()
136
+
137
+ return None
sfq/py.typed ADDED
File without changes
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.3
2
+ Name: sfq
3
+ Version: 0.0.1
4
+ Summary: Python wrapper for the Salesforce's Query API.
5
+ Author-email: David Moruzzi <sfq.pypi@dmoruzi.com>
6
+ Keywords: salesforce,salesforce query
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Requires-Python: >=3.12
13
+ Description-Content-Type: text/markdown
14
+
15
+ # sfq (Salesforce Query)
16
+
17
+ sfq is a lightweight Python wrapper library designed to simplify querying Salesforce, reducing repetitive code for accessing Salesforce data.
18
+
19
+ For more varied workflows, consider using an alternative like [Simple Salesforce](https://simple-salesforce.readthedocs.io/en/stable/). This library was even referenced on the [Salesforce Developers Blog](https://developer.salesforce.com/blogs/2021/09/how-to-automate-data-extraction-from-salesforce-using-python).
20
+
21
+ ## Features
22
+
23
+ - Simplified query execution for Salesforce instances.
24
+ - Integration with Salesforce authentication via refresh tokens.
25
+ - Option to interact with Salesforce Tooling API for more advanced queries.
26
+
27
+ ## Installation
28
+
29
+ You can install the `sfq` library using `pip`:
30
+
31
+ ```bash
32
+ pip install sfq
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ ### Querying Salesforce Data
38
+
39
+ ```python
40
+ from sfq import SFAuth
41
+
42
+ # Initialize the SFAuth class with authentication details
43
+ sf = SFAuth(
44
+ instance_url="https://example-dev-ed.trailblaze.my.salesforce.com",
45
+ client_id="PlatformCLI",
46
+ refresh_token="your-refresh-token-here"
47
+ )
48
+
49
+ # Execute a query to fetch account records
50
+ print(sf.query("SELECT Id FROM Account LIMIT 5"))
51
+
52
+ # Execute a query to fetch Tooling API data
53
+ print(sf.query("SELECT Id, FullName, Metadata FROM SandboxSettings LIMIT 5", tooling=True))
54
+ ```
55
+
56
+ ### Querying from Bash
57
+
58
+ You can easily incorporate this into ad-hoc bash scripts or commands:
59
+
60
+ ```bash
61
+ python -c "from sfq import SFAuth; sf = SFAuth(instance_url='$instance_url', client_id='$client_id', refresh_token='$refresh_token'); print(sf.query('$query'))" | jq -r '.records[].Id'
62
+ ```
63
+
64
+ ## How to Obtain Salesforce Tokens
65
+
66
+ To use the `sfq` library, you'll need a **client ID** and **refresh token**. The easiest way to obtain these is by using the Salesforce CLI:
67
+
68
+ ### Steps to Get Tokens
69
+
70
+ 1. **Install the Salesforce CLI**:
71
+ Follow the instructions on the [Salesforce CLI installation page](https://developer.salesforce.com/tools/salesforcecli).
72
+
73
+ 2. **Authenticate with Salesforce**:
74
+ Login to your Salesforce org using the following command:
75
+
76
+ ```bash
77
+ sf org login web --alias int --instance-url https://corpa--int.sandbox.my.salesforce.com
78
+ ```
79
+
80
+ 3. **Display Org Details**:
81
+ To get the client ID, refresh token, and instance URL, run:
82
+
83
+ ```bash
84
+ sf org display --target-org int --verbose --json
85
+ ```
86
+
87
+ The output will look like this:
88
+
89
+ ```json
90
+ {
91
+ "status": 0,
92
+ "result": {
93
+ "id": "00Daa0000000000000",
94
+ "apiVersion": "62.0",
95
+ "accessToken": "your-access-token-here",
96
+ "instanceUrl": "https://example-dev-ed.trailblaze.my.salesforce.com",
97
+ "username": "user@example.com",
98
+ "clientId": "PlatformCLI",
99
+ "connectedStatus": "Connected",
100
+ "sfdxAuthUrl": "force://PlatformCLI::your-refresh-token-here::https://example-dev-ed.trailblaze.my.salesforce.com",
101
+ "alias": "int"
102
+ }
103
+ }
104
+ ```
105
+
106
+ 4. **Extract and Use the Tokens**:
107
+ The `sfdxAuthUrl` is structured as:
108
+
109
+ ```
110
+ force://<client_id>::<refresh_token>::<instance_url>
111
+ ```
112
+
113
+ You can extract and use the tokens in a bash script like this:
114
+
115
+ ```bash
116
+ query="SELECT Id FROM User WHERE IsActive = true AND Profile.Name = 'System Administrator'"
117
+
118
+ sfdxAuthUrl=$(sf org display --target-org int --verbose --json | jq -r '.result.sfdxAuthUrl' | sed 's/force:\/\///')
119
+ clientId=$(echo "$sfdxAuthUrl" | sed 's/::/\n/g' | sed -n '1p')
120
+ refreshToken=$(echo "$sfdxAuthUrl" | sed 's/::/\n/g' | sed -n '2p')
121
+ instanceUrl=$(echo "$sfdxAuthUrl" | sed 's/::/\n/g' | sed -n '3p')
122
+
123
+ pip install sfq && python -c "from sfq import SFAuth; sf = SFAuth(instance_url='$instanceUrl', client_id='$clientId', refresh_token='$refreshToken'); print(sf.query('$query'))" | jq -r '.records[].Id'
124
+ ```
125
+
126
+ ## Notes
127
+
128
+ - **Authentication**: Make sure your refresh token is kept secure, as it grants access to your Salesforce instance.
129
+ - **Tooling API**: You can set the `tooling=True` argument in the `query` method to access the Salesforce Tooling API for more advanced metadata queries.
@@ -0,0 +1,5 @@
1
+ sfq/__init__.py,sha256=pOOF_qbF_5lWmZCp7Zth_cllmqD9Svr3pOVu01-1h5w,5180
2
+ sfq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ sfq-0.0.1.dist-info/METADATA,sha256=tDxHUnStTdh2UrOqI9ItvquS3BHSVANmdhuF2-Sshlo,4585
4
+ sfq-0.0.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
5
+ sfq-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.26.3
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any