qubesync 0.1.0__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.
qubesync-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 QubeSync
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,388 @@
1
+ Metadata-Version: 2.4
2
+ Name: qubesync
3
+ Version: 0.1.0
4
+ Summary: A Python library for QuickBooks Desktop / Enterprise integration via QubeSync API with intuitive DSL for building QBXML requests.
5
+ Author-email: QubeSync <support@qubesync.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/qubeintegrations/qube_sync_py
8
+ Project-URL: Documentation, https://docs.qubesync.com
9
+ Project-URL: Repository, https://github.com/qubeintegrations/qube_sync_py
10
+ Project-URL: Issues, https://github.com/qubeintegrations/qube_sync_py/issues
11
+ Keywords: quickbooks,qbxml,accounting,integration,api,dsl
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: requests
26
+ Dynamic: license-file
27
+
28
+ # QubeSync Python Library
29
+
30
+ A Python library for seamless QuickBooks integration via the QubeSync API. This library provides both a robust API client for QubeSync operations and a powerful DSL (Domain Specific Language) for building QuickBooks XML requests.
31
+
32
+ ## Features
33
+
34
+ - 🔌 **Complete QubeSync API client** - Connect, queue requests, handle webhooks
35
+ - 🏗️ **Intuitive Request Builder DSL** - Build QBXML requests with Python syntax
36
+ - 🔒 **Webhook security** - Built-in signature verification for webhooks
37
+ - 🚀 **Multiple syntax styles** - Context managers, fluent chaining, or Ruby-like syntax
38
+ - ✅ **Fully tested** - comprehensive tests ensuring reliability
39
+ - 📦 **Clean imports** - Simple `from qubesync import ...` syntax
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install qubesync
45
+ ```
46
+
47
+ Or install from source:
48
+
49
+ ```bash
50
+ git clone https://github.com/qubeintegrations/qube_sync_py
51
+ cd qube_sync_py
52
+ pip install -e .
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ### Environment Setup
58
+
59
+ Set your QubeSync credentials:
60
+
61
+ ```bash
62
+ export QUBE_API_KEY="your_api_key_here"
63
+ export QUBE_WEBHOOK_SECRET="your_webhook_secret_here"
64
+ export QUBE_URL="https://qubesync.com/api/v1" # Optional, defaults to this
65
+ ```
66
+
67
+ ### Basic Usage
68
+
69
+ ```python
70
+ from qubesync import QubeSync, RequestBuilder
71
+
72
+ # 1. Create a connection
73
+ connection = QubeSync.create_connection({
74
+ "name": "My Customer Inc.", # optional (will populate from QuickBooks on first sync if blank)
75
+ "redirect_url": "https://your-app.com/qbd-setup/callback"
76
+ })
77
+
78
+ # 2. Build a request using the DSL
79
+ request = RequestBuilder(version="16.0")
80
+ with request as r:
81
+ with r.QBXML() as qbxml:
82
+ with qbxml.QBXMLMsgsRq(onError='stopOnError') as msgs:
83
+ with msgs.CustomerQueryRq(requestID='customer_123', iterator='Start') as query:
84
+ query.MaxReturned(50)
85
+ query.IncludeRetElement('ListID')
86
+ query.IncludeRetElement('Name')
87
+ query.IncludeRetElement('Email')
88
+
89
+ # 3. Queue the request
90
+ response = QubeSync.queue_request(connection['id'], {
91
+ 'request_json': request.as_json(),
92
+ 'webhook_url': 'https://your-app.com/webhooks/qb_response'
93
+ })
94
+
95
+ print(f"Request queued with ID: {response['id']}")
96
+ ```
97
+
98
+ ## API Client Reference
99
+
100
+ ### Connection Management
101
+
102
+ ```python
103
+ from qubesync import QubeSync
104
+
105
+ # Create a new connection
106
+ connection = QubeSync.create_connection({
107
+ "name": "My Customer Inc.", # optional (will populate from QuickBooks on first sync if blank)
108
+ "redirect_url": "https://your-app.com/qbd-setup/callback"
109
+ })
110
+
111
+ # Get connection details
112
+ connection = QubeSync.get_connection(connection_id)
113
+
114
+ # Delete a connection
115
+ QubeSync.delete_connection(connection_id)
116
+
117
+ # Generate QWC file for QuickBooks
118
+ qwc_content = QubeSync.get_qwc(connection_id)
119
+
120
+ # Generate password for QuickBooks connection
121
+ password = QubeSync.generate_password(connection_id)
122
+ ```
123
+
124
+ ### Request Management
125
+
126
+ ```python
127
+ # Queue a request
128
+ request_data = {
129
+ 'request_json': {...}, # Built using RequestBuilder
130
+ 'webhook_url': 'https://your-app.com/webhook'
131
+ }
132
+ response = QubeSync.queue_request(connection_id, request_data)
133
+
134
+ # Get request status
135
+ request = QubeSync.get_request(request_id)
136
+
137
+ # Get all requests for a connection
138
+ requests = QubeSync.get_requests(connection_id)
139
+
140
+ # Delete a request
141
+ QubeSync.delete_request(request_id)
142
+ ```
143
+
144
+ ### Webhook Handling
145
+
146
+ ```python
147
+ # Verify and parse webhook payload
148
+ def handle_webhook(request):
149
+ signature = request.headers.get('X-Qube-Signature')
150
+ body = request.body.decode('utf-8')
151
+
152
+ try:
153
+ webhook_data = QubeSync.verify_and_build_webhook(body, signature)
154
+ # Process the verified webhook data in the background
155
+ # to ensure a quick response
156
+ print(f"Received response for request: {webhook_data['request_id']}")
157
+ return {"status": "success"}
158
+ except (StaleWebhookError, InvalidWebhookSignatureError) as e:
159
+ print(f"Webhook verification failed: {e}")
160
+ return {"status": "error"}, 400
161
+ ```
162
+
163
+ ## RequestBuilder DSL
164
+
165
+ The RequestBuilder provides an intuitive Python DSL for constructing QBXML requests that get converted to JSON format for the QubeSync API.
166
+
167
+ ### Context Manager Style (Recommended)
168
+
169
+ ```python
170
+ from qubesync import RequestBuilder
171
+
172
+ request = RequestBuilder(version="16.0")
173
+ with request as r:
174
+ with r.QBXML() as qbxml:
175
+ with qbxml.QBXMLMsgsRq(onError='stopOnError') as msgs:
176
+ with msgs.InvoiceQueryRq(requestID='inv_query_123') as query:
177
+ query.MaxReturned(25)
178
+ query.IncludeRetElement('TxnID')
179
+ query.IncludeRetElement('TxnNumber')
180
+ query.IncludeRetElement('CustomerRef')
181
+ query.IncludeRetElement('TxnDate')
182
+ query.IncludeRetElement('DueDate')
183
+ query.IncludeRetElement('BalanceRemaining')
184
+
185
+ json_data = request.as_json()
186
+ ```
187
+
188
+ ### Fluent Chaining Style
189
+
190
+ ```python
191
+ from qubesync import FluentRequestBuilder
192
+
193
+ request = FluentRequestBuilder(version="16.0")
194
+ request.QBXML().QBXMLMsgsRq(onError='stopOnError').ItemQueryRq(
195
+ requestID='item_query_456'
196
+ ).add_children([
197
+ ('MaxReturned', 100),
198
+ ('IncludeRetElement', 'ListID'),
199
+ ('IncludeRetElement', 'Name'),
200
+ ('IncludeRetElement', 'Type'),
201
+ ('IncludeRetElement', 'SalePrice')
202
+ ])
203
+
204
+ json_data = request.as_json()
205
+ ```
206
+
207
+ ### Ruby-like Style
208
+
209
+ ```python
210
+ from qubesync import RequestBuilder
211
+
212
+ request = RequestBuilder(version="16.0")
213
+ qbxml = request.QBXML()
214
+ msgs = qbxml.QBXMLMsgsRq(onError='stopOnError')
215
+ query = msgs.CustomerQueryRq(requestID='customer_789', iterator='Start')
216
+ query.MaxReturned(30)
217
+ query.IncludeRetElement('ListID')
218
+ query.IncludeRetElement('Name')
219
+ query.IncludeRetElement('CompanyName')
220
+
221
+ json_data = request.as_json()
222
+ ```
223
+
224
+ ### Handling Python Reserved Words
225
+
226
+ Use trailing underscores for Python reserved words:
227
+
228
+ ```python
229
+ request = RequestBuilder(version="16.0")
230
+ with request as r:
231
+ with r.QBXML() as qbxml:
232
+ with qbxml.QBXMLMsgsRq(onError='stopOnError') as msgs:
233
+ # Use trailing underscore for 'class', 'for', 'if', etc.
234
+ with msgs.CustomQueryRq(class_='invoice', for_='reports') as query:
235
+ query.MaxReturned(100)
236
+ ```
237
+
238
+ ### Complex Nested Structures
239
+
240
+ ```python
241
+ request = RequestBuilder(version="16.0")
242
+ with request as r:
243
+ with r.QBXML() as qbxml:
244
+ with qbxml.QBXMLMsgsRq(onError='stopOnError') as msgs:
245
+ with msgs.InvoiceAddRq(requestID='invoice_add_999') as add_req:
246
+ with add_req.InvoiceAdd() as invoice:
247
+ with invoice.CustomerRef() as customer_ref:
248
+ customer_ref.ListID('80000001-1234567890')
249
+
250
+ invoice.TxnDate('2024-01-15')
251
+ invoice.RefNumber('INV-2024-001')
252
+
253
+ with invoice.InvoiceLineAdd() as line:
254
+ with line.ItemRef() as item_ref:
255
+ item_ref.ListID('80000002-1234567890')
256
+ line.Desc('Professional Services')
257
+ line.Quantity(10)
258
+ line.Rate(150.00)
259
+ ```
260
+
261
+ ## Example Output
262
+
263
+ The RequestBuilder generates clean JSON that the QubeSync API processes:
264
+
265
+ ```json
266
+ {
267
+ "version": "16.0",
268
+ "request": [
269
+ {
270
+ "name": "QBXML",
271
+ "children": [
272
+ {
273
+ "name": "QBXMLMsgsRq",
274
+ "attributes": {"onError": "stopOnError"},
275
+ "children": [
276
+ {
277
+ "name": "CustomerQueryRq",
278
+ "attributes": {"requestID": "customer_123", "iterator": "Start"},
279
+ "children": [
280
+ {"name": "MaxReturned", "text": 50},
281
+ {"name": "IncludeRetElement", "text": "ListID"},
282
+ {"name": "IncludeRetElement", "text": "Name"},
283
+ {"name": "IncludeRetElement", "text": "Email"}
284
+ ]
285
+ }
286
+ ]
287
+ }
288
+ ]
289
+ }
290
+ ]
291
+ }
292
+ ```
293
+
294
+ ## Error Handling
295
+
296
+ ```python
297
+ from qubesync import (
298
+ QubeSync,
299
+ QubeSyncError,
300
+ ConfigError,
301
+ StaleWebhookError,
302
+ InvalidWebhookSignatureError
303
+ )
304
+
305
+ try:
306
+ connection = QubeSync.create_connection({"name": "Test"})
307
+ except ConfigError as e:
308
+ print(f"Configuration error: {e}")
309
+ except QubeSyncError as e:
310
+ print(f"API error: {e}")
311
+
312
+ # Webhook verification
313
+ try:
314
+ webhook_data = QubeSync.verify_and_build_webhook(body, signature)
315
+ except StaleWebhookError:
316
+ print("Webhook is too old")
317
+ except InvalidWebhookSignatureError:
318
+ print("Invalid webhook signature")
319
+ ```
320
+
321
+ ## Available Classes
322
+
323
+ ### API Client
324
+ - `QubeSync` - Main API client for all QubeSync operations
325
+ - `QubeSyncError` - Base exception class
326
+ - `ConfigError` - Configuration/environment variable errors
327
+ - `StaleWebhookError` - Webhook timestamp too old
328
+ - `InvalidWebhookSignatureError` - Webhook signature verification failed
329
+
330
+ ### Request Building
331
+ - `RequestBuilder` - Context manager style DSL (recommended)
332
+ - `FluentRequestBuilder` - Method chaining style DSL
333
+ - `RequestElement` - Individual request element (advanced usage)
334
+
335
+ ## Development
336
+
337
+ ### Running Tests
338
+
339
+ ```bash
340
+ # Run all tests
341
+ python run_tests.py
342
+
343
+ # Run with verbose output
344
+ python run_tests.py --verbose
345
+
346
+ # Run specific test file
347
+ python run_tests.py tests/test_request_builder.py
348
+ ```
349
+
350
+ ### Running Examples
351
+
352
+ ```bash
353
+ # Run all examples
354
+ python examples.py
355
+
356
+ # Use virtual environment
357
+ ./bin/python examples.py
358
+ ```
359
+
360
+ ## Contributing
361
+
362
+ 1. Fork the repository
363
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
364
+ 3. Make your changes
365
+ 4. Run tests (`python run_tests.py`)
366
+ 5. Commit your changes (`git commit -m 'Add amazing feature'`)
367
+ 6. Push to the branch (`git push origin feature/amazing-feature`)
368
+ 7. Open a Pull Request
369
+
370
+ ## License
371
+
372
+ This project is licensed under the MIT License - see the LICENSE file for details.
373
+
374
+ ## Support
375
+
376
+ - 📧 Email: support@qubesync.com
377
+ - 📖 Documentation: [QubeSync Docs](https://docs.qubesync.com)
378
+ - 🐛 Issues: [GitHub Issues](https://github.com/qubeintegrations/qube_sync_py/issues)
379
+
380
+ ## Changelog
381
+
382
+ ### v0.1.0
383
+ - Initial release
384
+ - Complete QubeSync API client
385
+ - RequestBuilder DSL with multiple syntax styles
386
+ - Comprehensive test suite
387
+ - Webhook security verification
388
+ - Clean module structure with intuitive imports