Skip to content

Commit 4f6af2b

Browse files
committed
2 parents 0ba5739 + 7add73e commit 4f6af2b

11 files changed

+322
-5
lines changed

app/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .admin import views as admin_views
1414
from .connect import views as connect_views
1515
from .webforms import views as webforms_views
16+
from .notary import views as notary_views
1617
from .views import core
1718

1819
session_path = "/tmp/python_recipe_sessions"
@@ -117,6 +118,8 @@
117118

118119
app.register_blueprint(webforms_views.weg001)
119120

121+
app.register_blueprint(notary_views.neg004)
122+
120123
if "DYNO" in os.environ: # On Heroku?
121124
import logging
122125

app/api_type.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
EXAMPLES_API_TYPE ={'Rooms': False, 'ESignature': True, 'Click': False, 'Monitor': False, 'Admin': False}
1+
EXAMPLES_API_TYPE ={'Rooms': False, 'ESignature': True, 'Click': False, 'Monitor': False, 'Admin': False, 'Notary': False}

app/consts.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,6 @@
116116
"ROOMS": "Rooms",
117117
"ADMIN": "Admin",
118118
"CONNECT": "Connect",
119-
"WEBFORMS": "WebForms"
119+
"WEBFORMS": "WebForms",
120+
"NOTARY": "Notary"
120121
}

app/docusign/ds_client.py

+10
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
"signature", "webforms_read", "webforms_instance_read", "webforms_instance_write"
4343
]
4444

45+
NOTARY_SCOPES = [
46+
"signature", "organization_read", "notary_read", "notary_write"
47+
]
48+
4549

4650
class DSClient:
4751
ds_app = None
@@ -71,6 +75,8 @@ def _auth_code_grant(cls, api):
7175
use_scopes.extend(ADMIN_SCOPES)
7276
elif api == "WebForms":
7377
use_scopes.extend(WEBFORMS_SCOPES)
78+
elif api == "Notary":
79+
use_scopes.extend(NOTARY_SCOPES)
7480
else:
7581
use_scopes.extend(SCOPES)
7682
# remove duplicate scopes
@@ -107,6 +113,8 @@ def _pkce_auth(cls, api):
107113
use_scopes.extend(ADMIN_SCOPES)
108114
elif api == "WebForms":
109115
use_scopes.extend(WEBFORMS_SCOPES)
116+
elif api == "Notary":
117+
use_scopes.extend(NOTARY_SCOPES)
110118
else:
111119
use_scopes.extend(SCOPES)
112120
# remove duplicate scopes
@@ -130,6 +138,8 @@ def _jwt_auth(cls, api):
130138
use_scopes.extend(ADMIN_SCOPES)
131139
elif api == "WebForms":
132140
use_scopes.extend(WEBFORMS_SCOPES)
141+
elif api == "Notary":
142+
use_scopes.extend(NOTARY_SCOPES)
133143
else:
134144
use_scopes.extend(SCOPES)
135145
# remove duplicate scopes
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import base64
2+
from os import path
3+
4+
from docusign_esign import EnvelopesApi, EnvelopeDefinition, Document, Signer, Notary, SignHere, Tabs, Recipients, \
5+
NotarySeal, NotaryRecipient, RecipientSignatureProvider, RecipientSignatureProviderOptions
6+
7+
from ...consts import demo_docs_path, pattern
8+
from ...jwt_helpers import create_api_client
9+
10+
11+
class Eg004SendWithThirdPartyNotary:
12+
13+
@classmethod
14+
def worker(cls, args):
15+
"""
16+
1. Create the envelope request object
17+
2. Send the envelope
18+
"""
19+
20+
envelope_args = args["envelope_args"]
21+
# Create the envelope request object
22+
envelope_definition = cls.make_envelope(envelope_args)
23+
#ds-snippet-start:Notary4Step2
24+
api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"])
25+
envelopes_api = EnvelopesApi(api_client)
26+
#ds-snippet-end:Notary4Step2
27+
28+
#ds-snippet-start:Notary4Step4
29+
results = envelopes_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition)
30+
#ds-snippet-end:Notary4Step4
31+
32+
envelope_id = results.envelope_id
33+
34+
return {"envelope_id": envelope_id}
35+
36+
#ds-snippet-start:Notary4Step3
37+
@classmethod
38+
def make_envelope(cls, args):
39+
"""
40+
Creates envelope
41+
Document 1: An HTML document.
42+
DocuSign will convert all of the documents to the PDF format.
43+
The recipients" field tags are placed using <b>anchor</b> strings.
44+
"""
45+
46+
# document 1 (html) has sign here anchor tag **signature_1**
47+
#
48+
# The envelope has two recipients.
49+
# recipient 1 - signer
50+
# The envelope will be sent first to the signer.
51+
52+
# create the envelope definition
53+
env = EnvelopeDefinition(
54+
email_subject="Please sign this document set"
55+
)
56+
doc1_b64 = base64.b64encode(bytes(cls.create_document1(args), "utf-8")).decode("ascii")
57+
58+
# Create the document models
59+
document1 = Document( # create the DocuSign document object
60+
document_base64=doc1_b64,
61+
name="Order acknowledgement", # can be different from actual file name
62+
file_extension="html", # many different document types are accepted
63+
document_id="1" # a label used to reference the doc
64+
)
65+
# The order in the docs array determines the order in the envelope
66+
env.documents = [document1]
67+
68+
# Create the signer recipient model
69+
signer1 = Signer(
70+
email=args["signer_email"],
71+
name=args["signer_name"],
72+
recipient_id="2",
73+
routing_order="1",
74+
client_user_id="1000",
75+
notary_id="1"
76+
)
77+
# routingOrder (lower means earlier) determines the order of deliveries
78+
# to the recipients. Parallel routing order is supported by using the
79+
# same integer as the order for two or more recipients.
80+
81+
# Create signHere fields (also known as tabs) on the documents,
82+
# We"re using anchor (autoPlace) positioning
83+
#
84+
# The DocuSign platform searches throughout your envelope"s
85+
# documents for matching anchor strings. So the
86+
# signHere2 tab will be used in both document 2 and 3 since they
87+
# use the same anchor string for their "signer 1" tabs.
88+
sign_here1 = SignHere(
89+
document_id="1",
90+
x_position="200",
91+
y_position="235",
92+
page_number="1"
93+
)
94+
95+
sign_here2 = SignHere(
96+
stamp_type="stamp",
97+
document_id="1",
98+
x_position="200",
99+
y_position="150",
100+
page_number="1"
101+
102+
)
103+
104+
# Add the tabs model (including the sign_here tabs) to the signer
105+
# The Tabs object wants arrays of the different field/tab types
106+
signer1.tabs = Tabs(sign_here_tabs=[sign_here1, sign_here2])
107+
108+
notary_seal_tab = NotarySeal(
109+
x_position = "300",
110+
y_position = "235",
111+
document_id = "1",
112+
page_number = "1",
113+
)
114+
115+
notary_sign_here = SignHere(
116+
x_position = "300",
117+
y_position = "150",
118+
document_id = "1",
119+
page_number = "1",
120+
)
121+
122+
notary_tabs = Tabs(
123+
sign_here_tabs = [notary_sign_here],
124+
notary_seal_tabs = [ notary_seal_tab ],
125+
)
126+
127+
recipient_signature_provider = RecipientSignatureProvider(
128+
seal_documents_with_tabs_only = "false",
129+
signature_provider_name = "ds_authority_idv",
130+
signature_provider_options = RecipientSignatureProviderOptions()
131+
)
132+
133+
notary_recipient = NotaryRecipient(
134+
name = "Notary",
135+
recipient_id = "1",
136+
routing_order = "1",
137+
tabs = notary_tabs,
138+
notary_type = "remote",
139+
notary_source_type = "thirdparty",
140+
notary_third_party_partner = "onenotary",
141+
recipient_signature_providers = [recipient_signature_provider]
142+
)
143+
144+
# Add the recipients to the envelope object
145+
recipients = Recipients(signers=[signer1], notaries= [notary_recipient])
146+
env.recipients = recipients
147+
148+
# Request that the envelope be sent by setting |status| to "sent".
149+
# To request that the envelope be created as a draft, set to "created"
150+
env.status = args["status"]
151+
152+
return env
153+
154+
@classmethod
155+
def create_document1(cls, args):
156+
""" Creates document 1 -- an html document"""
157+
158+
return f"""
159+
<!DOCTYPE html>
160+
<html>
161+
<head>
162+
<meta charset="UTF-8">
163+
</head>
164+
<body style="font-family:sans-serif;margin-left:2em;">
165+
<h1 style="font-family: "Trebuchet MS", Helvetica, sans-serif;
166+
color: darkblue;margin-bottom: 0;">World Wide Corp</h1>
167+
<h2 style="font-family: "Trebuchet MS", Helvetica, sans-serif;
168+
margin-top: 0px;margin-bottom: 3.5em;font-size: 1em;
169+
color: darkblue;">Order Processing Division</h2>
170+
<h4>Ordered by {args["signer_name"]}</h4>
171+
<p style="margin-top:0em; margin-bottom:0em;">Email: {args["signer_email"]}</p>
172+
<p style="margin-top:3em;">
173+
Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie.
174+
Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée.
175+
Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice.
176+
Donut jujubes oat cake jelly-o.
177+
Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.
178+
</p>
179+
<!-- Note the anchor tag for the signature field is in white. -->
180+
<h3 style="margin-top:3em;">Agreed: <span style="color:white;">**signature_1**/</span></h3>
181+
</body>
182+
</html>
183+
"""
184+
#ds-snippet-end:Notary4Step3

app/notary/views/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .eg004_send_with_third_party_notary import neg004
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
""" Example 004: Send envelope with third party Notary """
2+
3+
from os import path
4+
5+
from docusign_esign.client.api_exception import ApiException
6+
from flask import render_template, session, Blueprint, request
7+
8+
from ..examples.eg004_send_with_third_party_notary import Eg004SendWithThirdPartyNotary
9+
from ...docusign import authenticate, ensure_manifest, get_example_by_number
10+
from ...ds_config import DS_CONFIG
11+
from ...error_handlers import process_error
12+
from ...consts import pattern, API_TYPE
13+
14+
example_number = 4
15+
api = API_TYPE["NOTARY"]
16+
eg = f"neg00{example_number}" # reference (and url) for this example
17+
neg004 = Blueprint(eg, __name__)
18+
19+
def get_args():
20+
"""Get request and session arguments"""
21+
22+
# More data validation would be a good idea here
23+
# Strip anything other than characters listed
24+
signer_email = pattern.sub("", request.form.get("signer_email"))
25+
signer_name = pattern.sub("", request.form.get("signer_name"))
26+
27+
envelope_args = {
28+
"signer_email": signer_email,
29+
"signer_name": signer_name,
30+
"status": "sent",
31+
}
32+
args = {
33+
"account_id": session["ds_account_id"],
34+
"base_path": session["ds_base_path"],
35+
"access_token": session["ds_access_token"],
36+
"envelope_args": envelope_args
37+
}
38+
return args
39+
40+
@neg004.route(f"/{eg}", methods=["POST"])
41+
@authenticate(eg=eg, api=api)
42+
@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"])
43+
def sign_by_email():
44+
"""
45+
1. Get required arguments
46+
2. Call the worker method
47+
3. Render success response with envelopeId
48+
"""
49+
50+
# 1. Get required arguments
51+
#args = Eg002SigningViaEmailController.get_args()
52+
args = get_args()
53+
try:
54+
# 1. Call the worker method
55+
results = Eg004SendWithThirdPartyNotary.worker(args)
56+
except ApiException as err:
57+
return process_error(err)
58+
59+
session["envelope_id"] = results["envelope_id"] # Save for use by other examples which need an envelopeId
60+
61+
# 2. Render success response with envelopeId
62+
example = get_example_by_number(session["manifest"], example_number, api)
63+
return render_template(
64+
"example_done.html",
65+
title=example["ExampleName"],
66+
message=example["ResultsPageText"].format(results['envelope_id'])
67+
)
68+
69+
@neg004.route(f"/{eg}", methods=["GET"])
70+
@ensure_manifest(manifest_url=DS_CONFIG["example_manifest_url"])
71+
@authenticate(eg=eg, api=api)
72+
def get_view():
73+
"""responds with the form for the example"""
74+
example = get_example_by_number(session["manifest"], example_number, api)
75+
76+
return render_template(
77+
"notary/eg004_send_with_third_party_notary.html",
78+
title=example["ExampleName"],
79+
example=example,
80+
source_file="eg004_send_with_third_party_notary.py",
81+
source_url=DS_CONFIG["github_example_url"] + "eg004_send_with_third_party_notary.py",
82+
documentation=DS_CONFIG["documentation"] + eg,
83+
show_doc=DS_CONFIG["documentation"],
84+
signer_name=DS_CONFIG["signer_name"],
85+
signer_email=DS_CONFIG["signer_email"]
86+
)

app/static/assets/search.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ const DS_SEARCH = (function () {
66
ROOMS: "rooms",
77
ADMIN: "admin",
88
CONNECT: "connect",
9-
WEBFORMS: "webforms"
9+
WEBFORMS: "webforms",
10+
NOTARY: "notary"
1011
}
1112

1213
const processJSONData = function () {
@@ -145,6 +146,8 @@ const DS_SEARCH = (function () {
145146
return "cneg";
146147
case API_TYPES.WEBFORMS:
147148
return "weg";
149+
case API_TYPES.NOTARY:
150+
return "neg";
148151
}
149152
}
150153

app/templates/cfr_home.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ <h2>{{ group["Name"] }}</h2>
4545
{% for example in group["Examples"] -%}
4646
{% if not example["SkipForLanguages"] or "python" not in example["SkipForLanguages"] %}
4747
{% if example.CFREnabled == "AllAccounts" or example.CFREnabled == "CFROnly" %}
48-
{% set api_prefix = "a" if api["Name"] == "Admin" else "c" if api["Name"] == "Click" else "r" if api["Name"] == "Rooms" else "m" if api["Name"] == "Monitor" else "w" if api["Name"] == "WebForms" else "" %}
48+
{% set api_prefix = "a" if api["Name"] == "Admin" else "c" if api["Name"] == "Click" else "r" if api["Name"] == "Rooms" else "m" if api["Name"] == "Monitor" else "w" if api["Name"] == "WebForms" else "n" if api["Name"] == "Notary" else "" %}
4949
<h4
5050
id="{{ api_prefix + 'example' + '0' * (3 - example['ExampleNumber'] | string() | length ) + example['ExampleNumber'] | string() }}"
5151
>

app/templates/home.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ <h2>{{ group["Name"] }}</h2>
4343
{% for example in group["Examples"] -%}
4444
{% if not example["SkipForLanguages"] or "python" not in example["SkipForLanguages"] %}
4545
{% if example.CFREnabled != "CFROnly" %}
46-
{% set api_prefix = "a" if api["Name"] == "Admin" else "c" if api["Name"] == "Click" else "r" if api["Name"] == "Rooms" else "m" if api["Name"] == "Monitor" else "cn" if api["Name"] == "Connect" else "ms" if api["Name"] == "WebForms" else "" %}
46+
{% set api_prefix = "a" if api["Name"] == "Admin" else "c" if api["Name"] == "Click" else "r" if api["Name"] == "Rooms" else "m" if api["Name"] == "Monitor" else "cn" if api["Name"] == "Connect" else "ms" if api["Name"] == "WebForms" else "n" if api["Name"] == "Notary" else "" %}
4747
<h4
4848
id="{{ api_prefix + 'example' + '0' * (3 - example['ExampleNumber'] | string() | length ) + example['ExampleNumber'] | string() }}"
4949
>

0 commit comments

Comments
 (0)