blueink-client-python 1.0.0__tar.gz → 1.2.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.
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/PKG-INFO +54 -3
- blueink-client-python-1.0.0/src/blueink_client_python.egg-info/PKG-INFO → blueink_client_python-1.2.0/README.md +50 -27
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/setup.cfg +2 -2
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/bundle_helper.py +233 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/client.py +14 -4
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/endpoints.py +9 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/model/bundles.py +164 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/request_helper.py +9 -1
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/bundle.py +101 -0
- blueink-client-python-1.0.0/src/blueink/subclients/template.py → blueink_client_python-1.2.0/src/blueink/subclients/envelope_template.py +17 -14
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/person.py +1 -1
- blueink_client_python-1.2.0/src/blueink/subclients/template.py +99 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/webhook.py +0 -3
- blueink_client_python-1.2.0/src/blueink/tests/test_bundle_helper.py +634 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/tests/test_client.py +58 -0
- blueink-client-python-1.0.0/README.md → blueink_client_python-1.2.0/src/blueink_client_python.egg-info/PKG-INFO +78 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink_client_python.egg-info/SOURCES.txt +1 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink_client_python.egg-info/requires.txt +1 -1
- blueink-client-python-1.0.0/src/blueink/tests/test_bundle_helper.py +0 -188
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/LICENSE +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/pyproject.toml +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/__init__.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/constants.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/model/__init__.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/model/persons.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/model/webhook.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/paginator.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/person_helper.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/__init__.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/packet.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/subclients/subclient.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/tests/__init__.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/tests/test_person_helper.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/utils/__init__.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink/utils/testcase.py +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink_client_python.egg-info/dependency_links.txt +0 -0
- {blueink-client-python-1.0.0 → blueink_client_python-1.2.0}/src/blueink_client_python.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: blueink-client-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Python Client for Blueink eSignature API
|
|
5
5
|
Home-page: https://github.com/blueinkhq/blueink-client-python
|
|
6
6
|
Author: Blueink
|
|
@@ -19,11 +19,12 @@ Requires-Dist: email-validator
|
|
|
19
19
|
Provides-Extra: munch
|
|
20
20
|
Requires-Dist: munch>=2.5; extra == "munch"
|
|
21
21
|
Provides-Extra: requests
|
|
22
|
-
Requires-Dist: requests>=2.
|
|
22
|
+
Requires-Dist: requests>=2.31; extra == "requests"
|
|
23
23
|
Provides-Extra: pydantic
|
|
24
24
|
Requires-Dist: pydantic>=1.9; extra == "pydantic"
|
|
25
25
|
Provides-Extra: email-validator
|
|
26
26
|
Requires-Dist: 1.2; extra == "email-validator"
|
|
27
|
+
Dynamic: license-file
|
|
27
28
|
|
|
28
29
|
# blueink-client-python
|
|
29
30
|

|
|
@@ -400,6 +401,56 @@ with open("/path/to/file/example.pdf", 'rb') as file:
|
|
|
400
401
|
doc04_key = bh.add_document_by_file(file)
|
|
401
402
|
```
|
|
402
403
|
|
|
404
|
+
#### Auto-Placement Fields
|
|
405
|
+
|
|
406
|
+
Auto-placement fields allow you to automatically search for text in documents and place
|
|
407
|
+
signature/input fields at those locations with optional offsets. This eliminates the need
|
|
408
|
+
to manually specify exact coordinates for fields.
|
|
409
|
+
|
|
410
|
+
```python
|
|
411
|
+
from blueink import BundleHelper, Client
|
|
412
|
+
|
|
413
|
+
bh = BundleHelper(
|
|
414
|
+
label="Auto-Placement Example",
|
|
415
|
+
email_subject="Please sign",
|
|
416
|
+
is_test=True,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
signer_key = bh.add_signer(name="John Doe", email="john@example.com")
|
|
420
|
+
doc_key = bh.add_document_by_url("https://www.irs.gov/pub/irs-pdf/fw9.pdf")
|
|
421
|
+
|
|
422
|
+
# Add auto-placement field that searches for "Signature" text
|
|
423
|
+
bh.add_auto_placement(
|
|
424
|
+
document_key=doc_key,
|
|
425
|
+
kind="sig", # Field type: signature
|
|
426
|
+
search="Signature", # Text to search for
|
|
427
|
+
w=20, # Width
|
|
428
|
+
h=5, # Height
|
|
429
|
+
offset_x=-5, # Move 5 units left from found text
|
|
430
|
+
offset_y=2, # Move 2 units down from found text
|
|
431
|
+
editors=[signer_key],
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Add auto-placement for an input field
|
|
435
|
+
bh.add_auto_placement(
|
|
436
|
+
document_key=doc_key,
|
|
437
|
+
kind="inp", # Field type: input
|
|
438
|
+
search="Address", # Text to search for
|
|
439
|
+
w=20,
|
|
440
|
+
h=2,
|
|
441
|
+
offset_x=8, # Move 8 units right from found text
|
|
442
|
+
editors=[signer_key],
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
client = Client()
|
|
446
|
+
response = client.bundles.create_from_bundle_helper(bh)
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**Key benefits of auto-placement:**
|
|
450
|
+
- No need to manually find exact coordinates
|
|
451
|
+
- Works with template documents that have consistent text labels
|
|
452
|
+
- Automatically adjusts to text position in the document
|
|
453
|
+
- Can be combined with regular manually-positioned fields
|
|
403
454
|
|
|
404
455
|
#### Retrieval
|
|
405
456
|
|
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: blueink-client-python
|
|
3
|
-
Version: 1.0.0
|
|
4
|
-
Summary: Python Client for Blueink eSignature API
|
|
5
|
-
Home-page: https://github.com/blueinkhq/blueink-client-python
|
|
6
|
-
Author: Blueink
|
|
7
|
-
Author-email: pypi@blueink.com
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/blueinkhq/blueink-client-python/issues
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.8
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
License-File: LICENSE
|
|
15
|
-
Requires-Dist: munch
|
|
16
|
-
Requires-Dist: requests
|
|
17
|
-
Requires-Dist: pydantic
|
|
18
|
-
Requires-Dist: email-validator
|
|
19
|
-
Provides-Extra: munch
|
|
20
|
-
Requires-Dist: munch>=2.5; extra == "munch"
|
|
21
|
-
Provides-Extra: requests
|
|
22
|
-
Requires-Dist: requests>=2.27; extra == "requests"
|
|
23
|
-
Provides-Extra: pydantic
|
|
24
|
-
Requires-Dist: pydantic>=1.9; extra == "pydantic"
|
|
25
|
-
Provides-Extra: email-validator
|
|
26
|
-
Requires-Dist: 1.2; extra == "email-validator"
|
|
27
|
-
|
|
28
1
|
# blueink-client-python
|
|
29
2
|

|
|
30
3
|

|
|
@@ -400,6 +373,56 @@ with open("/path/to/file/example.pdf", 'rb') as file:
|
|
|
400
373
|
doc04_key = bh.add_document_by_file(file)
|
|
401
374
|
```
|
|
402
375
|
|
|
376
|
+
#### Auto-Placement Fields
|
|
377
|
+
|
|
378
|
+
Auto-placement fields allow you to automatically search for text in documents and place
|
|
379
|
+
signature/input fields at those locations with optional offsets. This eliminates the need
|
|
380
|
+
to manually specify exact coordinates for fields.
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
from blueink import BundleHelper, Client
|
|
384
|
+
|
|
385
|
+
bh = BundleHelper(
|
|
386
|
+
label="Auto-Placement Example",
|
|
387
|
+
email_subject="Please sign",
|
|
388
|
+
is_test=True,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
signer_key = bh.add_signer(name="John Doe", email="john@example.com")
|
|
392
|
+
doc_key = bh.add_document_by_url("https://www.irs.gov/pub/irs-pdf/fw9.pdf")
|
|
393
|
+
|
|
394
|
+
# Add auto-placement field that searches for "Signature" text
|
|
395
|
+
bh.add_auto_placement(
|
|
396
|
+
document_key=doc_key,
|
|
397
|
+
kind="sig", # Field type: signature
|
|
398
|
+
search="Signature", # Text to search for
|
|
399
|
+
w=20, # Width
|
|
400
|
+
h=5, # Height
|
|
401
|
+
offset_x=-5, # Move 5 units left from found text
|
|
402
|
+
offset_y=2, # Move 2 units down from found text
|
|
403
|
+
editors=[signer_key],
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Add auto-placement for an input field
|
|
407
|
+
bh.add_auto_placement(
|
|
408
|
+
document_key=doc_key,
|
|
409
|
+
kind="inp", # Field type: input
|
|
410
|
+
search="Address", # Text to search for
|
|
411
|
+
w=20,
|
|
412
|
+
h=2,
|
|
413
|
+
offset_x=8, # Move 8 units right from found text
|
|
414
|
+
editors=[signer_key],
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
client = Client()
|
|
418
|
+
response = client.bundles.create_from_bundle_helper(bh)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Key benefits of auto-placement:**
|
|
422
|
+
- No need to manually find exact coordinates
|
|
423
|
+
- Works with template documents that have consistent text labels
|
|
424
|
+
- Automatically adjusts to text position in the document
|
|
425
|
+
- Can be combined with regular manually-positioned fields
|
|
403
426
|
|
|
404
427
|
#### Retrieval
|
|
405
428
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = blueink-client-python
|
|
3
|
-
version = 1.
|
|
3
|
+
version = 1.2.0
|
|
4
4
|
author = Blueink
|
|
5
5
|
author_email = pypi@blueink.com
|
|
6
6
|
description = Python Client for Blueink eSignature API
|
|
@@ -27,7 +27,7 @@ install_requires =
|
|
|
27
27
|
|
|
28
28
|
[options.extras_require]
|
|
29
29
|
munch = munch>=2.5;
|
|
30
|
-
requests = requests>=2.
|
|
30
|
+
requests = requests>=2.31;
|
|
31
31
|
pydantic = pydantic>=1.9
|
|
32
32
|
email-validator> = 1.2
|
|
33
33
|
|
|
@@ -4,9 +4,13 @@ from os.path import basename
|
|
|
4
4
|
from typing import List
|
|
5
5
|
|
|
6
6
|
from blueink.model.bundles import (
|
|
7
|
+
AutoPlacement,
|
|
7
8
|
Bundle,
|
|
8
9
|
Document,
|
|
10
|
+
EnvelopeTemplate,
|
|
11
|
+
EnvelopeTemplateFieldValue,
|
|
9
12
|
Field,
|
|
13
|
+
ImportedDocument,
|
|
10
14
|
Packet,
|
|
11
15
|
TemplateRef,
|
|
12
16
|
TemplateRefAssignment,
|
|
@@ -25,6 +29,9 @@ class BundleHelper:
|
|
|
25
29
|
is_test: bool = False,
|
|
26
30
|
custom_key: str = None,
|
|
27
31
|
team: str = None,
|
|
32
|
+
signing_brand: str = None,
|
|
33
|
+
expires: str = None,
|
|
34
|
+
tag_values: dict = None,
|
|
28
35
|
):
|
|
29
36
|
"""Helper class to aid building a Bundle.
|
|
30
37
|
|
|
@@ -38,6 +45,9 @@ class BundleHelper:
|
|
|
38
45
|
is_test:
|
|
39
46
|
custom_key:
|
|
40
47
|
team:
|
|
48
|
+
signing_brand:
|
|
49
|
+
expires:
|
|
50
|
+
tag_values: Optional dict mapping data flow tags to pre-filled values
|
|
41
51
|
"""
|
|
42
52
|
self._label = label
|
|
43
53
|
self._in_order = in_order
|
|
@@ -49,6 +59,10 @@ class BundleHelper:
|
|
|
49
59
|
self._packets = {}
|
|
50
60
|
self._custom_key = custom_key
|
|
51
61
|
self._team = team
|
|
62
|
+
self._signing_brand = signing_brand
|
|
63
|
+
self._expires = expires
|
|
64
|
+
self._tag_values = tag_values
|
|
65
|
+
self._envelope_template = None
|
|
52
66
|
|
|
53
67
|
# for file uploads, index should match those in the document "file_index" field
|
|
54
68
|
self.file_names = []
|
|
@@ -107,6 +121,20 @@ class BundleHelper:
|
|
|
107
121
|
|
|
108
122
|
return self.add_document_by_b64(filename, b64str, **additional_data)
|
|
109
123
|
|
|
124
|
+
def add_document_by_html(self, html_content: str, **additional_data) -> str:
|
|
125
|
+
"""Add a document using an HTML string for HTML-to-PDF conversion.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
html_content: HTML content string
|
|
129
|
+
additional_data: Optional additional kwargs (e.g., filename, html_fields_mode)
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Document Key
|
|
133
|
+
"""
|
|
134
|
+
document = Document.create(file_html=html_content, **additional_data)
|
|
135
|
+
self._documents[document.key] = document
|
|
136
|
+
return document.key
|
|
137
|
+
|
|
110
138
|
def add_document_by_b64(self, filename: str, b64str: str, **additional_data):
|
|
111
139
|
"""Add a file using a b64 string; utf-8 encoded
|
|
112
140
|
|
|
@@ -205,6 +233,10 @@ class BundleHelper:
|
|
|
205
233
|
v_pattern: str = None,
|
|
206
234
|
v_min: int = None,
|
|
207
235
|
v_max: int = None,
|
|
236
|
+
v_regex: str = None,
|
|
237
|
+
v_regex_msg: str = None,
|
|
238
|
+
v_attachment_types: List[str] = None,
|
|
239
|
+
data_flow_tag: str = None,
|
|
208
240
|
key=None,
|
|
209
241
|
**additional_data,
|
|
210
242
|
):
|
|
@@ -222,6 +254,10 @@ class BundleHelper:
|
|
|
222
254
|
v_pattern: Optional
|
|
223
255
|
v_min: Optional
|
|
224
256
|
v_max: Optional
|
|
257
|
+
v_regex: Optional regular expression for field validation
|
|
258
|
+
v_regex_msg: Optional error message shown when v_regex validation fails
|
|
259
|
+
v_attachment_types: Optional list of allowed attachment file extensions (only for kind='att')
|
|
260
|
+
data_flow_tag: Optional data flow tag to associate with the field
|
|
225
261
|
editors: Optional
|
|
226
262
|
key: Optional
|
|
227
263
|
additional_data: Optional and will append any additional kwargs to the json of the field
|
|
@@ -243,6 +279,10 @@ class BundleHelper:
|
|
|
243
279
|
v_pattern=v_pattern,
|
|
244
280
|
v_min=v_min,
|
|
245
281
|
v_max=v_max,
|
|
282
|
+
v_regex=v_regex,
|
|
283
|
+
v_regex_msg=v_regex_msg,
|
|
284
|
+
v_attachment_types=v_attachment_types,
|
|
285
|
+
data_flow_tag=data_flow_tag,
|
|
246
286
|
key=key,
|
|
247
287
|
**additional_data,
|
|
248
288
|
)
|
|
@@ -252,6 +292,75 @@ class BundleHelper:
|
|
|
252
292
|
self._documents[document_key].add_field(field)
|
|
253
293
|
return field.key
|
|
254
294
|
|
|
295
|
+
def add_auto_placement(
|
|
296
|
+
self,
|
|
297
|
+
document_key: str,
|
|
298
|
+
kind: str,
|
|
299
|
+
search: str,
|
|
300
|
+
w: int,
|
|
301
|
+
h: int,
|
|
302
|
+
offset_x: int = 0,
|
|
303
|
+
offset_y: int = 0,
|
|
304
|
+
editors: List[str] = None,
|
|
305
|
+
page: int = None,
|
|
306
|
+
v_attachment_types: List[str] = None,
|
|
307
|
+
**additional_data,
|
|
308
|
+
):
|
|
309
|
+
"""Add an auto-placement field to a document.
|
|
310
|
+
|
|
311
|
+
Auto-placement fields automatically search for text in the document and place
|
|
312
|
+
the field at the found location with optional offsets.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
document_key: Key of the document to add the auto-placement to
|
|
316
|
+
kind: Field type (e.g., 'sig' for signature, 'inp' for input, 'ini' for initials)
|
|
317
|
+
search: Text to search for in the document
|
|
318
|
+
w: Width of the field
|
|
319
|
+
h: Height of the field
|
|
320
|
+
offset_x: Horizontal offset from the search text (default: 0)
|
|
321
|
+
offset_y: Vertical offset from the search text (default: 0)
|
|
322
|
+
editors: List of signer keys who can edit this field
|
|
323
|
+
page: Optional page number to limit search to
|
|
324
|
+
v_attachment_types: Optional list of allowed attachment file extensions (only for kind='att')
|
|
325
|
+
additional_data: Optional additional kwargs to append to the auto-placement
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
None (auto-placements don't have keys like regular fields)
|
|
329
|
+
|
|
330
|
+
Example:
|
|
331
|
+
# Add a signature field that searches for "Signature" text
|
|
332
|
+
bh.add_auto_placement(
|
|
333
|
+
document_key=doc_key,
|
|
334
|
+
kind='sig',
|
|
335
|
+
search='Signature',
|
|
336
|
+
w=20,
|
|
337
|
+
h=5,
|
|
338
|
+
offset_x=-5,
|
|
339
|
+
offset_y=2,
|
|
340
|
+
editors=['signer-1']
|
|
341
|
+
)
|
|
342
|
+
"""
|
|
343
|
+
if document_key not in self._documents:
|
|
344
|
+
raise RuntimeError(f"No document found with key {document_key}!")
|
|
345
|
+
|
|
346
|
+
auto_placement = AutoPlacement.create(
|
|
347
|
+
kind=kind,
|
|
348
|
+
search=search,
|
|
349
|
+
w=w,
|
|
350
|
+
h=h,
|
|
351
|
+
offset_x=offset_x,
|
|
352
|
+
offset_y=offset_y,
|
|
353
|
+
page=page,
|
|
354
|
+
v_attachment_types=v_attachment_types,
|
|
355
|
+
**additional_data,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
if editors:
|
|
359
|
+
for editor_key in editors:
|
|
360
|
+
auto_placement.add_editor(editor_key)
|
|
361
|
+
|
|
362
|
+
self._documents[document_key].add_auto_placement(auto_placement)
|
|
363
|
+
|
|
255
364
|
def add_signer(
|
|
256
365
|
self,
|
|
257
366
|
name: str,
|
|
@@ -264,6 +373,8 @@ class BundleHelper:
|
|
|
264
373
|
auth_id: bool = False,
|
|
265
374
|
order: int = None,
|
|
266
375
|
key=None,
|
|
376
|
+
requires_witness: bool = None,
|
|
377
|
+
witness_nominated_by: str = None,
|
|
267
378
|
**additional_data,
|
|
268
379
|
):
|
|
269
380
|
"""Create and add a signer. With at least an email xor phone number.
|
|
@@ -279,6 +390,8 @@ class BundleHelper:
|
|
|
279
390
|
auth_id: Optional
|
|
280
391
|
deliver_via: Optional
|
|
281
392
|
order: Optional
|
|
393
|
+
requires_witness: Optional
|
|
394
|
+
witness_nominated_by: Optional
|
|
282
395
|
additional_data: Optional and will append any additional kwargs to the json of the signer
|
|
283
396
|
|
|
284
397
|
Returns:
|
|
@@ -298,6 +411,8 @@ class BundleHelper:
|
|
|
298
411
|
deliver_via=deliver_via,
|
|
299
412
|
order=order,
|
|
300
413
|
key=key,
|
|
414
|
+
requires_witness=requires_witness,
|
|
415
|
+
witness_nominated_by=witness_nominated_by,
|
|
301
416
|
**additional_data,
|
|
302
417
|
)
|
|
303
418
|
self._packets[packet.key] = packet
|
|
@@ -347,6 +462,64 @@ class BundleHelper:
|
|
|
347
462
|
field_val = TemplateRefFieldValue.create(key, value, **additional_data)
|
|
348
463
|
self._documents[document_key].field_values.append(field_val)
|
|
349
464
|
|
|
465
|
+
def set_envelope_template(
|
|
466
|
+
self, template_id: str, field_values: dict = None, **additional_data
|
|
467
|
+
):
|
|
468
|
+
"""Set the envelope template for this bundle.
|
|
469
|
+
|
|
470
|
+
When using an envelope template, the template contains the complete envelope
|
|
471
|
+
configuration including documents, signers, and field assignments.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
template_id: Envelope template ID (format: T-xxxxxxxxxxx)
|
|
475
|
+
field_values: Optional dict mapping field keys to initial values
|
|
476
|
+
additional_data: Optional additional kwargs to append to the envelope template
|
|
477
|
+
|
|
478
|
+
Example:
|
|
479
|
+
bh = BundleHelper(label="Contract", is_test=True)
|
|
480
|
+
bh.add_signer(name="John Doe", email="john@example.com", key="signer-1")
|
|
481
|
+
bh.set_envelope_template(
|
|
482
|
+
template_id="T-abc123",
|
|
483
|
+
field_values={"company_name": "ACME Corp"}
|
|
484
|
+
)
|
|
485
|
+
"""
|
|
486
|
+
vals = []
|
|
487
|
+
if field_values:
|
|
488
|
+
for field_key, init_val in field_values.items():
|
|
489
|
+
fieldval = EnvelopeTemplateFieldValue.create(
|
|
490
|
+
key=field_key, initial_value=init_val
|
|
491
|
+
)
|
|
492
|
+
vals.append(fieldval)
|
|
493
|
+
|
|
494
|
+
self._envelope_template = EnvelopeTemplate.create(
|
|
495
|
+
template_id=template_id,
|
|
496
|
+
field_values=vals if vals else None,
|
|
497
|
+
**additional_data,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
def add_envelope_template_field_value(
|
|
501
|
+
self, key: str, initial_value: str, **additional_data
|
|
502
|
+
):
|
|
503
|
+
"""Add a field value to the envelope template.
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
key: Field key in the envelope template
|
|
507
|
+
initial_value: Initial value for the field
|
|
508
|
+
additional_data: Optional additional kwargs
|
|
509
|
+
|
|
510
|
+
Raises:
|
|
511
|
+
RuntimeError: If no envelope template has been set
|
|
512
|
+
"""
|
|
513
|
+
if self._envelope_template is None:
|
|
514
|
+
raise RuntimeError(
|
|
515
|
+
"No envelope template set. Call set_envelope_template() first."
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
field_val = EnvelopeTemplateFieldValue.create(
|
|
519
|
+
key=key, initial_value=initial_value, **additional_data
|
|
520
|
+
)
|
|
521
|
+
self._envelope_template.add_field_value(field_val)
|
|
522
|
+
|
|
350
523
|
def _compile_bundle(self, **additional_data) -> Bundle:
|
|
351
524
|
"""
|
|
352
525
|
Builds a Bundle object complete with all the packets (signers) and documents added through the course
|
|
@@ -368,6 +541,9 @@ class BundleHelper:
|
|
|
368
541
|
cc_emails=self._cc_emails,
|
|
369
542
|
custom_key=self._custom_key,
|
|
370
543
|
team=self._team,
|
|
544
|
+
signing_brand=self._signing_brand,
|
|
545
|
+
expires=self._expires,
|
|
546
|
+
tag_values=self._tag_values,
|
|
371
547
|
**additional_data,
|
|
372
548
|
)
|
|
373
549
|
return bundle_out
|
|
@@ -384,6 +560,63 @@ class BundleHelper:
|
|
|
384
560
|
bundle = self._compile_bundle(**additional_data)
|
|
385
561
|
return bundle.dict(exclude_unset=True, exclude_none=True)
|
|
386
562
|
|
|
563
|
+
def as_data_for_envelope_template(self, **additional_data):
|
|
564
|
+
"""Return data for creating a bundle from an envelope template.
|
|
565
|
+
|
|
566
|
+
This method is used when creating a bundle from an envelope template.
|
|
567
|
+
It returns a dictionary with packets and envelope_template fields.
|
|
568
|
+
|
|
569
|
+
Args:
|
|
570
|
+
additional_data: extra data to append to the request, as a dict
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
Dictionary suitable for create_from_envelope_template endpoint
|
|
574
|
+
|
|
575
|
+
Raises:
|
|
576
|
+
RuntimeError: If no envelope template has been set
|
|
577
|
+
|
|
578
|
+
Example:
|
|
579
|
+
bh = BundleHelper(label="Contract", is_test=True)
|
|
580
|
+
bh.add_signer(name="John Doe", email="john@example.com", key="signer-1")
|
|
581
|
+
bh.set_envelope_template("T-abc123", {"company_name": "ACME"})
|
|
582
|
+
data = bh.as_data_for_envelope_template()
|
|
583
|
+
"""
|
|
584
|
+
if self._envelope_template is None:
|
|
585
|
+
raise RuntimeError(
|
|
586
|
+
"No envelope template set. Call set_envelope_template() first."
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
packets = list(self._packets.values())
|
|
590
|
+
result = {
|
|
591
|
+
"packets": [p.dict(exclude_unset=True, exclude_none=True) for p in packets],
|
|
592
|
+
"envelope_template": self._envelope_template.dict(
|
|
593
|
+
exclude_unset=True, exclude_none=True
|
|
594
|
+
),
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
# Add optional bundle fields
|
|
598
|
+
if self._label:
|
|
599
|
+
result["label"] = self._label
|
|
600
|
+
if self._is_test is not None:
|
|
601
|
+
result["is_test"] = self._is_test
|
|
602
|
+
if self._email_subj:
|
|
603
|
+
result["email_subject"] = self._email_subj
|
|
604
|
+
if self._email_msg:
|
|
605
|
+
result["email_message"] = self._email_msg
|
|
606
|
+
if self._cc_emails:
|
|
607
|
+
result["cc_emails"] = self._cc_emails
|
|
608
|
+
if self._custom_key:
|
|
609
|
+
result["custom_key"] = self._custom_key
|
|
610
|
+
if self._team:
|
|
611
|
+
result["team"] = self._team
|
|
612
|
+
if self._signing_brand:
|
|
613
|
+
result["signing_brand"] = self._signing_brand
|
|
614
|
+
|
|
615
|
+
# Add any additional data
|
|
616
|
+
result.update(additional_data)
|
|
617
|
+
|
|
618
|
+
return result
|
|
619
|
+
|
|
387
620
|
def as_json(self, **additional_data):
|
|
388
621
|
"""Return a Bundle as a json
|
|
389
622
|
|
|
@@ -7,6 +7,7 @@ from blueink.constants import (
|
|
|
7
7
|
)
|
|
8
8
|
from blueink.request_helper import RequestHelper
|
|
9
9
|
from blueink.subclients.bundle import BundleSubClient
|
|
10
|
+
from blueink.subclients.envelope_template import EnvelopeTemplateSubClient
|
|
10
11
|
from blueink.subclients.packet import PacketSubClient
|
|
11
12
|
from blueink.subclients.person import PersonSubClient
|
|
12
13
|
from blueink.subclients.template import TemplateSubClient
|
|
@@ -19,6 +20,7 @@ class Client:
|
|
|
19
20
|
private_api_key: str = None,
|
|
20
21
|
base_url: str = None,
|
|
21
22
|
raise_exceptions: bool = True,
|
|
23
|
+
security_headers: dict = None,
|
|
22
24
|
):
|
|
23
25
|
"""Initialize a Client instance to access the Blueink eSignature API
|
|
24
26
|
|
|
@@ -29,6 +31,7 @@ class Client:
|
|
|
29
31
|
base_url: override the API base URL. If not supplied, we check the
|
|
30
32
|
environment variable BLUEINK_API_URL. If that is empty, the default
|
|
31
33
|
value of "https://api.blueink.com/api/v2" is used.
|
|
34
|
+
security_headers: Place for additional security headers, likely unnecessary
|
|
32
35
|
raise_exceptions (Default True): raise HTTPError if code != 200. Otherwise
|
|
33
36
|
return as NormalizedResponse objects.
|
|
34
37
|
|
|
@@ -44,9 +47,9 @@ class Client:
|
|
|
44
47
|
|
|
45
48
|
if not private_api_key:
|
|
46
49
|
raise ValueError(
|
|
47
|
-
"A Blueink Private API Key must be provided on Client initialization"
|
|
48
|
-
" or specified via the environment variable"
|
|
49
|
-
" {ENV_BLUEINK_PRIVATE_API_KEY}"
|
|
50
|
+
f"A Blueink Private API Key must be provided on Client initialization"
|
|
51
|
+
f" or specified via the environment variable"
|
|
52
|
+
f" {ENV_BLUEINK_PRIVATE_API_KEY}"
|
|
50
53
|
)
|
|
51
54
|
|
|
52
55
|
if not base_url:
|
|
@@ -57,10 +60,17 @@ class Client:
|
|
|
57
60
|
|
|
58
61
|
self._base_url = base_url
|
|
59
62
|
|
|
60
|
-
self._request_helper = RequestHelper(
|
|
63
|
+
self._request_helper = RequestHelper(
|
|
64
|
+
private_api_key,
|
|
65
|
+
raise_exceptions,
|
|
66
|
+
security_headers=security_headers,
|
|
67
|
+
)
|
|
61
68
|
|
|
62
69
|
self.bundles = BundleSubClient(self._base_url, self._request_helper)
|
|
63
70
|
self.persons = PersonSubClient(self._base_url, self._request_helper)
|
|
64
71
|
self.packets = PacketSubClient(self._base_url, self._request_helper)
|
|
65
72
|
self.templates = TemplateSubClient(self._base_url, self._request_helper)
|
|
73
|
+
self.envelope_templates = EnvelopeTemplateSubClient(
|
|
74
|
+
self._base_url, self._request_helper
|
|
75
|
+
)
|
|
66
76
|
self.webhooks = WebhookSubClient(self._base_url, self._request_helper)
|
|
@@ -8,6 +8,8 @@ from string import Template
|
|
|
8
8
|
|
|
9
9
|
class BUNDLES:
|
|
10
10
|
CREATE = "/bundles/"
|
|
11
|
+
CREATE_FROM_ENVELOPE_TEMPLATE = "/bundles/create_from_envelope_template/"
|
|
12
|
+
CREATE_PREPARATION_SESSION = "/bundles/preparation_session/"
|
|
11
13
|
LIST = "/bundles/"
|
|
12
14
|
RETRIEVE = "/bundles/${bundle_id}/"
|
|
13
15
|
CANCEL = "/bundles/${bundle_id}/cancel/"
|
|
@@ -34,6 +36,13 @@ class PACKETS:
|
|
|
34
36
|
class TEMPLATES:
|
|
35
37
|
LIST = "/templates/"
|
|
36
38
|
RETRIEVE = "/templates/${template_id}/"
|
|
39
|
+
UPDATE = "/templates/${template_id}/"
|
|
40
|
+
CREATE_PREPARATION_SESSION = "/templates/preparation_session/"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ENVELOPE_TEMPLATES:
|
|
44
|
+
LIST = "/envelope-templates/"
|
|
45
|
+
RETRIEVE = "/envelope-templates/${envelope_template_id}/"
|
|
37
46
|
|
|
38
47
|
|
|
39
48
|
class WEBHOOKS:
|