Skip to content

Commit 47cbb9d

Browse files
Add Connected fields code example (#186)
* add code example * code style fixes
1 parent 5166b69 commit 47cbb9d

File tree

10 files changed

+289
-6
lines changed

10 files changed

+289
-6
lines changed

Gemfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ group :test do
6868
gem 'test-unit'
6969
end
7070

71-
gem 'docusign_admin', '~> 2.0.0.rc2'
71+
gem 'docusign_admin', '~> 2.0.0'
7272
gem 'docusign_click', '~> 1.4.0'
7373
gem 'docusign_esign', '~> 5.0.0'
7474
gem 'docusign_monitor', '~> 1.2.0'
7575
gem 'docusign_rooms', '~> 1.3.0'
76-
gem 'docusign_webforms', '~> 1.0.0'
76+
gem 'docusign_webforms', '~> 2.0.0'
7777
gem 'omniauth-oauth2', '~> 1.8.0'
7878
gem 'omniauth-rails_csrf_protection'
7979

Gemfile.lock

+3-3
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ GEM
134134
json (~> 2.1, >= 2.1.0)
135135
jwt (~> 2.2, >= 2.2.1)
136136
typhoeus (~> 1.0, >= 1.0.1)
137-
docusign_webforms (1.0.0)
137+
docusign_webforms (2.0.0)
138138
addressable (~> 2.7, >= 2.7.0)
139139
json (~> 2.1, >= 2.1.0)
140140
jwt (~> 2.2, >= 2.2.1)
@@ -392,12 +392,12 @@ DEPENDENCIES
392392
capybara (~> 3.40.0)
393393
chromedriver-helper (~> 2.1.1)
394394
coffee-rails (~> 5.0.0)
395-
docusign_admin (~> 2.0.0.rc2)
395+
docusign_admin (~> 2.0.0)
396396
docusign_click (~> 1.4.0)
397397
docusign_esign (~> 5.0.0)
398398
docusign_monitor (~> 1.2.0)
399399
docusign_rooms (~> 1.3.0)
400-
docusign_webforms (~> 1.0.0)
400+
docusign_webforms (~> 2.0.0)
401401
jbuilder (~> 2.13.0)
402402
listen (~> 3.9.0)
403403
matrix (~> 0.4.2)

app/assets/javascripts/search.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ const DS_SEARCH = (function () {
77
ADMIN: "admin",
88
CONNECT: "connect",
99
WEBFORMS: "webforms",
10-
NOTARY: "notary"
10+
NOTARY: "notary",
11+
CONNECTED_FIELDS: "connectedfields"
1112
}
1213

1314
const processJSONData = function () {
@@ -148,6 +149,8 @@ const DS_SEARCH = (function () {
148149
return "weg";
149150
case API_TYPES.NOTARY:
150151
return "neg";
152+
case API_TYPES.CONNECTED_FIELDS:
153+
return "feg";
151154
}
152155
}
153156

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# frozen_string_literal: true
2+
3+
require_relative '../../services/utils'
4+
5+
class ConnectedFields::Feg001SetConnectedFieldsController < EgController
6+
before_action -> { check_auth('ConnectedFields') }
7+
before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'ConnectedFields') }
8+
9+
def get
10+
super
11+
args = {
12+
account_id: session['ds_account_id'],
13+
access_token: session['ds_access_token'],
14+
extensions_base_path: 'https://api-d.docusign.com'
15+
}
16+
17+
example_service = ConnectedFields::Eg001SetConnectedFieldsService.new(args)
18+
@apps = example_service.get_tab_groups
19+
20+
return unless @apps.nil? || @apps.empty?
21+
22+
additional_page_data = example['AdditionalPage'].find { |p| p['Name'] == 'no_verification_app' }
23+
24+
@title = @example['ExampleName']
25+
@message = additional_page_data['ResultsPageText']
26+
render 'ds_common/example_done'
27+
end
28+
29+
def create
30+
envelope_args = {
31+
signer_email: param_gsub(params['signerEmail']),
32+
signer_name: param_gsub(params['signerName']),
33+
doc_pdf: File.join('data', Rails.application.config.doc_pdf)
34+
}
35+
args = {
36+
account_id: session['ds_account_id'],
37+
base_path: session['ds_base_path'],
38+
extensions_base_path: 'https://api-d.docusign.com',
39+
access_token: session['ds_access_token'],
40+
selected_app_id: param_gsub(params['appId']),
41+
envelope_args: envelope_args
42+
}
43+
44+
example_service = ConnectedFields::Eg001SetConnectedFieldsService.new(args)
45+
apps = example_service.get_tab_groups
46+
selected_app = apps.find { |app| app['appId'] == args[:selected_app_id] }
47+
48+
results = example_service.send_envelope selected_app
49+
session[:envelope_id] = results['envelope_id']
50+
@title = @example['ExampleName']
51+
@message = format_string(@example['ResultsPageText'], results['envelope_id'])
52+
render 'ds_common/example_done'
53+
rescue DocuSign_eSign::ApiError => e
54+
handle_error(e)
55+
end
56+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# frozen_string_literal: true
2+
3+
class ConnectedFields::Eg001SetConnectedFieldsService
4+
attr_reader :args
5+
6+
include ApiCreator
7+
8+
def initialize(args)
9+
@args = args
10+
end
11+
12+
def get_tab_groups
13+
#ds-snippet-start:ConnectedFields1Step2
14+
headers = {
15+
'Authorization' => "Bearer #{args[:access_token]}",
16+
'Accept' => 'application/json',
17+
'Content-Type' => 'application/json'
18+
}
19+
#ds-snippet-end:ConnectedFields1Step2
20+
21+
#ds-snippet-start:ConnectedFields1Step3
22+
url = URI("#{args[:extensions_base_path]}/v1/accounts/#{args[:account_id]}/connected-fields/tab-groups")
23+
response = Net::HTTP.get_response(url, headers)
24+
response_data = JSON.parse(response.body)
25+
26+
filtered_apps = response_data.select do |app|
27+
app['tabs']&.any? do |tab|
28+
(tab['extensionData']&.dig('actionContract')&.include? 'Verify') ||
29+
(tab['tabLabel']&.include? 'connecteddata')
30+
end
31+
end
32+
33+
# return unique apps
34+
filtered_apps.uniq { |app| app['appId'] }
35+
#ds-snippet-end:ConnectedFields1Step3
36+
end
37+
38+
#ds-snippet-start:ConnectedFields1Step4
39+
def extract_verification_data(selected_app_id, tab)
40+
extension_data = tab['extensionData']
41+
42+
{
43+
app_id: selected_app_id,
44+
extension_group_id: extension_data['extensionGroupId'],
45+
publisher_name: extension_data['publisherName'],
46+
application_name: extension_data['applicationName'],
47+
action_name: extension_data['actionName'],
48+
action_input_key: extension_data['actionInputKey'],
49+
action_contract: extension_data['actionContract'],
50+
extension_name: extension_data['extensionName'],
51+
extension_contract: extension_data['extensionContract'],
52+
required_for_extension: extension_data['requiredForExtension'],
53+
tab_label: tab['tabLabel'],
54+
connection_key: extension_data['connectionInstances']&.dig(0, 'connectionKey') || '',
55+
connection_value: extension_data['connectionInstances']&.dig(0, 'connectionValue') || ''
56+
}
57+
end
58+
#ds-snippet-end:ConnectedFields1Step4
59+
60+
def send_envelope(app)
61+
# Create the envelope definition
62+
#ds-snippet-start:ConnectedFields1Step6
63+
envelope = make_envelope args[:envelope_args], app
64+
65+
# Call Docusign to create the envelope
66+
envelope_api = create_envelope_api(args)
67+
68+
results = envelope_api.create_envelope args[:account_id], envelope
69+
70+
{ 'envelope_id' => results.envelope_id }
71+
#ds-snippet-end:ConnectedFields1Step6
72+
end
73+
74+
private
75+
76+
#ds-snippet-start:ConnectedFields1Step5
77+
def make_envelope(envelope_args, app)
78+
doc = DocuSign_eSign::Document.new
79+
doc.document_base64 = Base64.encode64(File.binread(envelope_args[:doc_pdf]))
80+
doc.name = 'Lorem Ipsum'
81+
doc.file_extension = 'pdf'
82+
doc.document_id = '1'
83+
84+
# The order in the docs array determines the order in the envelope
85+
# Create a signer recipient to sign the document, identified by name and email
86+
# We're setting the parameters via the object creation
87+
signer = DocuSign_eSign::Signer.new
88+
signer.email = envelope_args[:signer_email]
89+
signer.name = envelope_args[:signer_name]
90+
signer.recipient_id = 1
91+
92+
# The Docusign platform searches throughout your envelope's documents for matching
93+
# anchor strings. So the sign_here_2 tab will be used in both document 2 and 3
94+
# since they use the same anchor string for their "signer 1" tabs.
95+
sign_here = DocuSign_eSign::SignHere.new
96+
sign_here.anchor_string = '/sn1/'
97+
sign_here.anchor_units = 'pixels'
98+
sign_here.anchor_x_offset = '20'
99+
sign_here.anchor_y_offset = '10'
100+
101+
text_tabs = []
102+
app['tabs'].each do |tab|
103+
next if tab['tabLabel'].include?('SuggestionInput')
104+
105+
verification_data = extract_verification_data(app['appId'], tab)
106+
text_tabs.push(text_tab(verification_data, text_tabs.length))
107+
end
108+
109+
tabs = DocuSign_eSign::Tabs.new
110+
tabs.sign_here_tabs = [sign_here]
111+
tabs.text_tabs = text_tabs
112+
signer.tabs = tabs
113+
114+
recipients = DocuSign_eSign::Recipients.new
115+
recipients.signers = [signer]
116+
117+
envelope_definition = DocuSign_eSign::EnvelopeDefinition.new
118+
envelope_definition.email_subject = 'Please sign this document'
119+
envelope_definition.documents = [doc]
120+
envelope_definition.recipients = recipients
121+
envelope_definition.status = 'sent'
122+
envelope_definition
123+
end
124+
125+
def extension_data(verification_data)
126+
{
127+
extensionGroupId: verification_data[:extension_group_id],
128+
publisherName: verification_data[:publisher_name],
129+
applicationId: verification_data[:app_id],
130+
applicationName: verification_data[:application_name],
131+
actionName: verification_data[:action_name],
132+
actionContract: verification_data[:action_contract],
133+
extensionName: verification_data[:extension_name],
134+
extensionContract: verification_data[:extension_contract],
135+
requiredForExtension: verification_data[:required_for_extension],
136+
actionInputKey: verification_data[:action_input_key],
137+
extensionPolicy: 'MustVerifyToSign',
138+
connectionInstances: [
139+
{
140+
connectionKey: verification_data[:connection_key],
141+
connectionValue: verification_data[:connection_value]
142+
}
143+
]
144+
}
145+
end
146+
147+
def text_tab(verification_data, text_tabs_count)
148+
{
149+
requireInitialOnSharedChange: false,
150+
requireAll: false,
151+
name: verification_data[:application_name],
152+
required: true,
153+
locked: false,
154+
disableAutoSize: false,
155+
maxLength: 4000,
156+
tabLabel: verification_data[:tab_label],
157+
font: 'lucidaconsole',
158+
fontColor: 'black',
159+
fontSize: 'size9',
160+
documentId: '1',
161+
recipientId: '1',
162+
pageNumber: '1',
163+
xPosition: (70 + 100 * (text_tabs_count / 10)).to_s,
164+
yPosition: (560 + 20 * (text_tabs_count % 10)).to_s,
165+
width: '84',
166+
height: '22',
167+
templateRequired: false,
168+
tabType: 'text',
169+
tooltip: verification_data[:action_input_key],
170+
extensionData: extension_data(verification_data)
171+
}
172+
end
173+
#ds-snippet-end:ConnectedFields1Step5
174+
end

app/services/jwt_auth/jwt_creator.rb

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def self.consent_url(state, api)
1919
scope = 'signature impersonation organization_read group_read permission_read user_read user_write account_read domain_read identity_provider_read user_data_redact asset_group_account_read asset_group_account_clone_write asset_group_account_clone_read organization_sub_account_write organization_sub_account_read' if api == 'Admin'
2020
scope = 'signature webforms_read webforms_instance_read webforms_instance_write' if api == 'WebForms'
2121
scope = 'signature organization_read notary_read notary_write' if api == 'Notary'
22+
scope = 'signature adm_store_unified_repo_read' if api == 'ConnectedFields'
2223

2324
base_uri = "#{Rails.configuration.authorization_server}/oauth/auth"
2425
response_type = 'code'
@@ -55,6 +56,10 @@ def initialize(session)
5556
scope = 'signature organization_read notary_read notary_write'
5657
@client_module = DocuSign_eSign
5758
end
59+
if session[:api] == 'ConnectedFields'
60+
scope = 'signature adm_store_unified_repo_read'
61+
@client_module = DocuSign_eSign
62+
end
5863

5964
@scope = scope
6065
@api_client = create_initial_api_client(host: Rails.configuration.aud, client_module: @client_module, debugging: false)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<%= render('partials/example_info') %>
2+
3+
<% form_index = 0 %>
4+
<% signer_email_index = 0 %>
5+
<% signer_name_index = 1 %>
6+
<% app_index = 2 %>
7+
8+
<form class="eg" action="" method="post" data-busy="form">
9+
<% if @example["Forms"][form_index]["FormName"] %>
10+
<%= sanitize @example["Forms"][form_index]["FormName"] %>
11+
<% end %>
12+
13+
<div class="form-group">
14+
<label for="signerEmail"><%= @example["Forms"][form_index]["Inputs"][signer_email_index]["InputName"] %></label>
15+
<input type="email" class="form-control" id="signerEmail" name="signerEmail"
16+
aria-describedby="emailHelp"
17+
placeholder="<%= @example["Forms"][form_index]["Inputs"][signer_email_index]["InputPlaceholder"] %>" required
18+
value="<%= @config.signer_email %>">
19+
<%= render('partials/email_will_not_be_shared') %>
20+
</div>
21+
<div class="form-group">
22+
<label for="signerName"><%= @example["Forms"][form_index]["Inputs"][signer_name_index]["InputName"] %></label>
23+
<input type="text" class="form-control" id="signerName"
24+
placeholder="<%= @example["Forms"][form_index]["Inputs"][signer_name_index]["InputPlaceholder"] %>" name="signerName"
25+
value="<%= @config.signer_name %>" required>
26+
</div>
27+
<div class="form-group">
28+
<label for="appId"><%= @example["Forms"][form_index]["Inputs"][app_index]["InputName"] %></label>
29+
<select id="appId" name="appId" class="form-control" required>
30+
<% @apps.each do |app| %>
31+
<option value="<%= app['appId'] %>" selected><%= app['tabs'][0]['extensionData']['applicationName'] %></option>
32+
<% end %>
33+
</select>
34+
</div>
35+
<%= render('partials/submit_button') %>
36+
</form>

app/views/ds_common/index.html.erb

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
"w"
5858
elsif api["Name"] == "Notary"
5959
"n"
60+
elsif api["Name"] == "ConnectedFields"
61+
"f"
6062
else
6163
"e"
6264
end %>

config/initializers/omniauth.rb

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
strategy.options[:authorize_params].scope = 'signature webforms_read webforms_instance_read webforms_instance_write'
5757
when 'Notary'
5858
strategy.options[:authorize_params].scope = 'signature organization_read notary_read notary_write'
59+
when 'ConnectedFields'
60+
strategy.options[:authorize_params].scope = 'signature adm_store_unified_repo_read'
5961
end
6062
}
6163
end

config/routes.rb

+5
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@
250250
post 'neg004' => 'neg004_send_with_third_party_notary#create'
251251
end
252252

253+
scope module: 'connected_fields' do
254+
get 'feg001' => 'feg001_set_connected_fields#get'
255+
post 'feg001' => 'feg001_set_connected_fields#create'
256+
end
257+
253258
root 'ds_common#index'
254259

255260
# Login starts with POST'ing to: /auth/docusign

0 commit comments

Comments
 (0)