lib/docusign_rest/client.rb in docusign_rest-0.2.0 vs lib/docusign_rest/client.rb in docusign_rest-0.3.0
- old
+ new
@@ -5,10 +5,11 @@
class Client
# Define the same set of accessors as the DocusignRest module
attr_accessor *Configuration::VALID_CONFIG_KEYS
attr_accessor :docusign_authentication_headers, :acct_id
+ attr_accessor :previous_call_log
def initialize(options={})
# Merge the config values from the module and those passed to the client.
merged_options = DocusignRest.options.merge(options)
@@ -36,10 +37,13 @@
# Set the account_id from the configure block if present, but can't call
# the instance var @account_id because that'll override the attr_accessor
# that is automatically configured for the configure block
@acct_id = account_id
+
+ #initialize the log cache
+ @previous_call_log = []
end
# Internal: sets the default request headers allowing for user overrides
# via options[:headers] from within other requests. Additionally injects
@@ -141,10 +145,11 @@
request = Net::HTTP::Post.new(uri.request_uri, content_type)
request.body = "grant_type=password&client_id=#{integrator_key}&username=#{email}&password=#{password}&scope=api"
http = initialize_net_http_ssl(uri)
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public: gets info necessary to make additional requests to the DocuSign API
@@ -167,11 +172,13 @@
# userName - Full name provided when signing up for DocuSign
def get_login_information(options={})
uri = build_uri('/login_information')
request = Net::HTTP::Get.new(uri.request_uri, headers(options[:headers]))
http = initialize_net_http_ssl(uri)
- http.request(request)
+ response = http.request(request)
+ generate_log(request, response, uri)
+ response
end
# Internal: uses the get_login_information method to determine the client's
# accountId and then caches that value into an instance variable so we
@@ -198,21 +205,19 @@
# Internal: takes in an array of hashes of signers and concatenates all the
# hashes with commas
#
# embedded - Tells DocuSign if this is an embedded signer which determines
- # weather or not to deliver emails. Also lets us authenticate
+ # whether or not to deliver emails. Also lets us authenticate
# them when they go to do embedded signing. Behind the scenes
# this is setting the clientUserId value to the signer's email.
# name - The name of the signer
# email - The email of the signer
# role_name - The role name of the signer ('Attorney', 'Client', etc.).
# tabs - Array of tab pairs grouped by type (Example type: 'textTabs')
# { textTabs: [ { tabLabel: "label", name: "name", value: "value" } ] }
#
- # NOTE: The 'tabs' option is NOT supported in 'v1' of the REST API
- #
# Returns a hash of users that need to be embedded in the template to
# create an envelope
def get_template_roles(signers)
template_roles = []
signers.each_with_index do |signer, index|
@@ -237,10 +242,21 @@
template_roles << template_role
end
template_roles
end
+ def get_sign_here_tabs(tabs)
+ Array(tabs).map do |tab|
+ {
+ documentId: tab[:document_id],
+ recipientId: tab[:recipient_id],
+ anchorString: tab[:anchor_string],
+ anchorXOffset: tab[:anchorXOffset],
+ anchorYOffset: tab[:anchorYOffset]
+ }
+ end
+ end
# TODO (2014-02-03) jonk => document
def get_signer_tabs(tabs)
Array(tabs).map do |tab|
{
@@ -258,18 +274,24 @@
# TODO (2014-02-03) jonk => document
def get_event_notification(event_notification)
return {} unless event_notification
{
useSoapInterface: event_notification[:use_soap_interface] || false,
- includeCertificatWithSoap: event_notification[:include_certificate_with_soap] || false,
+ includeCertificateWithSoap: event_notification[:include_certificate_with_soap] || false,
url: event_notification[:url],
loggingEnabled: event_notification[:logging],
- 'EnvelopeEvents' => Array(event_notification[:envelope_events]).map do |envelope_event|
+ 'envelopeEvents' => Array(event_notification[:envelope_events]).map do |envelope_event|
{
includeDocuments: envelope_event[:include_documents] || false,
envelopeEventStatusCode: envelope_event[:envelope_event_status_code]
}
+ end,
+ 'recipientEvents' => Array(event_notification[:recipient_events]).map do |recipient_event|
+ {
+ includeDocuments: recipient_event[:include_documents] || false,
+ recipientEventStatusCode: recipient_event[:recipient_event_status_code]
+ }
end
}
end
@@ -281,11 +303,11 @@
# template - Includes other optional fields only used when
# being called from a template
# email - The signer's email
# name - The signer's name
# embedded - Tells DocuSign if this is an embedded signer which
- # determines weather or not to deliver emails. Also
+ # determines whether or not to deliver emails. Also
# lets us authenticate them when they go to do
# embedded signing. Behind the scenes this is setting
# the clientUserId value to the signer's email.
# email_notification - Send an email or not
# role_name - The signer's role, like 'Attorney' or 'Client', etc.
@@ -310,21 +332,21 @@
doc_signer = {
email: signer[:email],
name: signer[:name],
accessCode: '',
addAccessCodeToEmail: false,
- customFields: nil,
+ customFields: signer[:custom_fields],
iDCheckConfigurationName: nil,
iDCheckInformationInput: nil,
inheritEmailNotificationConfiguration: false,
note: '',
phoneAuthentication: nil,
recipientAttachment: nil,
recipientId: "#{index + 1}",
requireIdLookup: false,
roleName: signer[:role_name],
- routingOrder: index + 1,
+ routingOrder: signer[:routing_order] || index + 1,
socialAuthentications: nil
}
if signer[:email_notification]
doc_signer[:emailNotification] = signer[:email_notification]
@@ -353,11 +375,11 @@
emailTabs: get_tabs(signer[:email_tabs], options, index),
envelopeIdTabs: nil,
fullNameTabs: get_tabs(signer[:full_name_tabs], options, index),
listTabs: get_tabs(signer[:list_tabs], options, index),
noteTabs: nil,
- numberTabs: nil,
+ numberTabs: get_tabs(signer[:number_tabs], options, index),
radioGroupTabs: get_tabs(signer[:radio_group_tabs], options, index),
initialHereTabs: get_tabs(signer[:initial_here_tabs], options.merge!(initial_here_tab: true), index),
signHereTabs: get_tabs(signer[:sign_here_tabs], options.merge!(sign_here_tab: true), index),
signerAttachmentTabs: nil,
ssnTabs: nil,
@@ -371,10 +393,76 @@
end
doc_signers
end
+ # Internal: people to be Carbon Copied on the document that is created
+ # https://docs.docusign.com/esign/restapi/Envelopes/Envelopes/create/
+ #
+ # Expecting options to be an array of hashes, with each hash representing a person to carbon copy
+ #
+ # email - The email of the recipient to be copied on the document
+ # name - The name of the recipient
+ # signer_count - Used to generate required attributes recipientId and routingOrder which must be unique in the document
+ #
+ def get_carbon_copies(options, signer_count)
+ copies = []
+ (options || []).each do |cc|
+ signer_count += 1
+ raise "Missing required data [:email, :name]" unless (cc[:email] && cc[:name])
+ cc.merge!(recipient_id: signer_count, routing_order: signer_count)
+ copies << camelize_keys(cc)
+ end
+ copies
+ end
+
+ # Public: Translate ruby oriented keys to camel cased keys recursively through the hash received
+ #
+ # The method expects symbol parameters in ruby form ":access_code" and translates them to camel cased "accessCode"
+ #
+ # example [{access_code: '12345', email_notification: {email_body: 'abcdef'}}] -> [{'accessCode': '12345', 'emailNotification': {'emailBody': 'abcdef'}}]
+ #
+ def camelize_keys(hash)
+ new_hash={}
+ hash.each do |k,v|
+ new_hash[camelize(k.to_s)] = (v.is_a?(Hash) ? camelize_keys(v) : v)
+ end
+ new_hash
+ end
+
+ # Generic implementation to avoid having to pull in Rails dependencies
+ #
+ def camelize(str)
+ str.gsub(/_([a-z])/) { $1.upcase }
+ end
+
+ # Internal: takes an array of hashes of certified deliveries
+ #
+ # email - The recipient email
+ # name - The recipient name
+ # recipient_id - The recipient's id
+ # embedded - Tells DocuSign if this is an embedded recipient which
+ # determines whether or not to deliver emails.
+ def get_certified_deliveries(certified_deliveries)
+ doc_certified_deliveries = []
+
+ certified_deliveries.each do |certified_delivery|
+ doc_certified_delivery = {
+ email: certified_delivery[:email],
+ name: certified_delivery[:name],
+ recipientId: certified_delivery[:recipient_id]
+ }
+
+ if certified_delivery[:embedded]
+ doc_certified_delivery[:clientUserId] = certified_delivery[:client_id] || certified_delivery[:email]
+ end
+
+ doc_certified_deliveries << doc_certified_delivery
+ end
+ doc_certified_deliveries
+ end
+
# TODO (2014-02-03) jonk => document
def get_tabs(tabs, options, index)
tab_array = []
Array(tabs).map do |tab|
@@ -410,19 +498,27 @@
tab_hash[:optional] = tab[:optional] || false
tab_hash[:tabLabel] = tab[:label] || 'Signature 1'
tab_hash[:width] = tab[:width] if tab[:width]
tab_hash[:height] = tab[:height] if tab[:height]
tab_hash[:value] = tab[:value] if tab[:value]
+ tab_hash[:fontSize] = tab[:font_size] if tab[:font_size]
+ tab_hash[:fontColor] = tab[:font_color] if tab[:font_color]
+ tab_hash[:bold] = tab[:bold] if tab[:bold]
+ tab_hash[:italic] = tab[:italic] if tab[:italic]
+ tab_hash[:underline] = tab[:underline] if tab[:underline]
tab_hash[:selected] = tab[:selected] if tab[:selected]
tab_hash[:locked] = tab[:locked] || false
tab_hash[:list_items] = tab[:list_items] if tab[:list_items]
tab_hash[:groupName] = tab[:group_name] if tab.key?(:group_name)
tab_hash[:radios] = get_tabs(tab[:radios], options, index) if tab.key?(:radios)
+ tab_hash[:validationMessage] = tab[:validation_message] if tab[:validation_message]
+ tab_hash[:validationPattern] = tab[:validation_pattern] if tab[:validation_pattern]
+
tab_array << tab_hash
end
tab_array
end
@@ -506,11 +602,10 @@
# Takes an optional array of files, which consist of documents to be used instead of templates
#
# Returns an array of server template hashes
def get_composite_template(server_template_ids, signers, files)
composite_array = []
- index = 0
server_template_ids.each_with_index do |template_id, idx|
server_template_hash = {
sequence: (idx+1).to_s,
templateId: template_id,
templateRoles: get_template_roles(signers),
@@ -543,16 +638,18 @@
email: signer[:email],
name: signer[:name],
recipientId: signer[:recipient_id],
roleName: signer[:role_name],
clientUserId: signer[:client_id] || signer[:email],
+ requireSignOnPaper: signer[:require_sign_on_paper] || false,
tabs: {
textTabs: get_signer_tabs(signer[:text_tabs]),
checkboxTabs: get_signer_tabs(signer[:checkbox_tabs]),
numberTabs: get_signer_tabs(signer[:number_tabs]),
fullNameTabs: get_signer_tabs(signer[:fullname_tabs]),
- dateTabs: get_signer_tabs(signer[:date_tabs])
+ dateTabs: get_signer_tabs(signer[:date_tabs]),
+ signHereTabs: get_sign_here_tabs(signer[:sign_here_tabs])
}
}
signers_array << signers_hash
end
template_hash = {sequence: sequence, recipients: { signers: signers_array }}
@@ -575,10 +672,13 @@
# params - A hash of params (including files for uploading and a
# customized request body)
# headers={} - The fully merged, final request headers
# boundary - Optional: you can give the request a custom boundary
#
+
+ headers = headers.dup.merge(parts: {post_body: {'Content-Type' => 'application/json'}})
+
request = Net::HTTP::Post::Multipart.new(
uri.request_uri,
{ post_body: post_body }.merge(file_params),
headers
)
@@ -601,57 +701,65 @@
# the local path of the file you wish to upload. Absolute
# paths recommended.
# file_name - The name you want to give to the file you are uploading
# content_type - (for the request body) application/json is what DocuSign
# is expecting
- # email_subject - (Optional) short subject line for the email
- # email_body - (Optional) custom text that will be injected into the
+ # email[subject] - (Optional) short subject line for the email
+ # email[body] - (Optional) custom text that will be injected into the
# DocuSign generated email
# signers - A hash of users who should receive the document and need
# to sign it. More info about the options available for
# this method are documented above it's method definition.
+ # carbon_copies - An array of hashes that includes users names and email who
+ # should receive a copy of the document once it is complete.
# status - Options include: 'sent', 'created', 'voided' and determine
# if the envelope is sent out immediately or stored for
# sending at a later time
# customFields - (Optional) A hash of listCustomFields and textCustomFields.
# Each contains an array of corresponding customField hashes.
# For details, please see: http://bit.ly/1FnmRJx
- # headers - Allows a client to pass in some
+ # headers - Allows a client to pass in some headers
+ # web_sign - (Optional) If true, the signer is allowed to print the
+ # document and sign it on paper. False if not defined.
#
# Returns a JSON parsed response object containing:
# envelopeId - The envelope's ID
# status - Sent, created, or voided
# statusDateTime - The date/time the envelope was created
# uri - The relative envelope uri
def create_envelope_from_document(options={})
ios = create_file_ios(options[:files])
file_params = create_file_params(ios)
- post_body = {
+ post_hash = {
emailBlurb: "#{options[:email][:body] if options[:email]}",
emailSubject: "#{options[:email][:subject] if options[:email]}",
documents: get_documents(ios),
recipients: {
- signers: get_signers(options[:signers])
+ signers: get_signers(options[:signers]),
+ carbonCopies: get_carbon_copies(options[:carbon_copies],options[:signers].size)
},
+ eventNotification: get_event_notification(options[:event_notification]),
status: "#{options[:status]}",
customFields: options[:custom_fields]
- }.to_json
+ }
+ post_hash[:enableWetSign] = options[:wet_sign] if options.has_key? :web_sign
+ post_body = post_hash.to_json
uri = build_uri("/accounts/#{acct_id}/envelopes")
http = initialize_net_http_ssl(uri)
request = initialize_net_http_multipart_post_request(
uri, post_body, file_params, headers(options[:headers])
)
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
-
# Public: allows a template to be dynamically created with several options.
#
# files - An array of hashes of file parameters which will be used
# to create actual files suitable for upload in a multipart
# request.
@@ -704,10 +812,11 @@
request = initialize_net_http_multipart_post_request(
uri, post_body, file_params, headers(options[:headers])
)
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# TODO (2014-02-03) jonk => document
@@ -718,10 +827,11 @@
uri = build_uri("/accounts/#{acct_id}/templates/#{template_id}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public: create an envelope for delivery from a template
@@ -753,23 +863,27 @@
post_body = {
status: options[:status],
emailBlurb: options[:email][:body],
emailSubject: options[:email][:subject],
templateId: options[:template_id],
+ enableWetSign: options[:wet_sign],
+ brandId: options[:brand_id],
eventNotification: get_event_notification(options[:event_notification]),
templateRoles: get_template_roles(options[:signers]),
- customFields: options[:custom_fields]
+ customFields: options[:custom_fields],
+ allowReassign: options[:allow_reassign]
}.to_json
uri = build_uri("/accounts/#{acct_id}/envelopes")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public: create an envelope for delivery from a composite template
@@ -780,14 +894,12 @@
# determine if the envelope is sent out immediately or
# stored for sending at a later time
# email/body - Sets the text in the email body
# email/subject - Sets the text in the email subject line
# files - Sets documents to be used instead of inline or server templates
- # template_roles - See the get_template_roles method definition for a list
- # of options to pass. Note: for consistency sake we call
- # this 'signers' and not 'templateRoles' when we build up
- # the request in client code.
+ # signers - See get_template_roles/get_inline_signers for a list
+ # of options to pass.
# headers - Optional hash of headers to merge into the existing
# required headers for a multipart request.
# server_template_ids - Array of ids for templates uploaded to DocuSign. Templates
# will be added in the order they appear in the array.
#
@@ -806,10 +918,12 @@
post_body = {
emailBlurb: "#{options[:email][:body] if options[:email]}",
emailSubject: "#{options[:email][:subject] if options[:email]}",
status: options[:status],
+ brandId: options[:brand_id],
+ allowReassign: options[:allow_reassign],
compositeTemplates: get_composite_template(options[:server_template_ids], options[:signers], options[:files])
}.to_json
uri = build_uri("/accounts/#{acct_id}/envelopes")
@@ -818,10 +932,11 @@
request = initialize_net_http_multipart_post_request(
uri, post_body, file_params, headers(options[:headers])
)
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public returns the names specified for a given email address (existing docusign user)
@@ -840,14 +955,45 @@
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
+ # Public adds the certified delivery recipients (Need to View) for a given envelope
+ #
+ # envelope_id - ID of the envelope for which you want to retrieve the
+ # signer info
+ # headers - optional hash of headers to merge into the existing
+ # required headers for a multipart request.
+ # certified_deliveries - A required hash of all the certified delivery recipients
+ # that need to be added to the envelope
+ #
+ # # The response returns the success or failure of each recipient being added
+ # to the envelope and the envelope ID
+ def add_envelope_certified_deliveries(options={})
+ content_type = { 'Content-Type' => 'application/json' }
+ content_type.merge(options[:headers]) if options[:headers]
+ post_body = {
+ certifiedDeliveries: get_certified_deliveries(options[:certified_deliveries]),
+ }.to_json
+
+ uri = build_uri("/accounts/#{acct_id}/envelopes/#{options[:envelope_id]}/recipients")
+
+ http = initialize_net_http_ssl(uri)
+
+ request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
+ request.body = post_body
+
+ response = http.request(request)
+ generate(request, response, uri)
+ JSON.parse(response.body)
+ end
+
# Public returns the URL for embedded signing
#
# envelope_id - the ID of the envelope you wish to use for embedded signing
# name - the name of the signer
# email - the email of the recipient
@@ -875,10 +1021,11 @@
request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public returns the URL for embedded sending
#
@@ -899,10 +1046,11 @@
request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
request.body = { returnUrl: options[:return_url] }.to_json
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public returns the URL for embedded console
#
@@ -925,11 +1073,11 @@
request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
-
+ generate_log(request, response, uri)
parsed_response = JSON.parse(response.body)
parsed_response['url']
end
@@ -953,10 +1101,11 @@
uri = build_uri("/accounts/#{acct_id}/envelopes/#{options[:envelope_id]}/recipients?include_tabs=#{include_tabs}&include_extended=#{include_extended}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public retrieves the envelope status
@@ -969,10 +1118,11 @@
uri = build_uri("/accounts/#{acct_id}/envelopes/#{options[:envelope_id]}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public retrieves the statuses of envelopes matching the given query
@@ -983,24 +1133,27 @@
# Defaults to the time of the call.
#
# from_to_status - The status of the envelope checked for in the from_date - to_date period.
# Defaults to 'changed'
#
+ # envelope_ids - Comma joined list of envelope_ids which you want to query.
+ #
# status - The current status of the envelope. Defaults to any status.
#
# Returns an array of hashes containing envelope statuses, ids, and similar information.
def get_envelope_statuses(options={})
content_type = { 'Content-Type' => 'application/json' }
content_type.merge(options[:headers]) if options[:headers]
- query_params = options.slice(:from_date, :to_date, :from_to_status, :status)
+ query_params = options.slice(:from_date, :to_date, :from_to_status, :envelope_ids, :status)
+ # Note that Hash#to_query is an ActiveSupport monkeypatch
uri = build_uri("/accounts/#{acct_id}/envelopes?#{query_params.to_query}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
-
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public retrieves the attached file from a given envelope
@@ -1029,10 +1182,11 @@
uri = build_uri("/accounts/#{acct_id}/envelopes/#{options[:envelope_id]}/documents/#{options[:document_id]}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
return response.body if options[:return_stream]
split_path = options[:local_save_path].split('/')
split_path.pop #removes the document name and extension from the array
path = split_path.join("/") #rejoins the array to form path to the folder that will contain the file
@@ -1056,11 +1210,11 @@
uri = build_uri("/accounts/#{acct_id}/envelopes/#{options[:envelope_id]}/documents")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
-
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public retrieves a PDF containing the combined content of all
@@ -1069,10 +1223,11 @@
# envelope_id - ID of the envelope from which the doc will be retrieved
# local_save_path - Local absolute path to save the doc to including the
# filename itself
# headers - Optional hash of headers to merge into the existing
# required headers for a multipart request.
+ # params - Optional params; for example, certificate: true
#
# Example
#
# client.get_combined_document_from_envelope(
# envelope_id: @envelope_response['envelopeId'],
@@ -1084,14 +1239,16 @@
def get_combined_document_from_envelope(options={})
content_type = { 'Content-Type' => 'application/json' }
content_type.merge(options[:headers]) if options[:headers]
uri = build_uri("/accounts/#{acct_id}/envelopes/#{options[:envelope_id]}/documents/combined")
+ uri.query = URI.encode_www_form(options[:params]) if options[:params]
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
return response.body if options[:return_stream]
split_path = options[:local_save_path].split('/')
split_path.pop #removes the document name and extension from the array
path = split_path.join("/") #rejoins the array to form path to the folder that will contain the file
@@ -1130,11 +1287,11 @@
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Put.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
-
+ generate_log(request, response, uri)
response
end
# Public returns a hash of audit events for a given envelope
@@ -1154,11 +1311,11 @@
uri = build_uri("/accounts/#{acct_id}/envelopes/#{options[:envelope_id]}/audit_events")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
-
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public retrieves folder information. Helpful to use before client.search_folder_for_envelopes
def get_folder_list(options={})
@@ -1168,10 +1325,11 @@
uri = build_uri("/accounts/#{@acct_id}/folders/")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public retrieves the envelope(s) from a specific folder based on search params.
#
@@ -1209,10 +1367,11 @@
uri = build_uri("/accounts/#{@acct_id}/folders/#{options[:folder_id]}/?#{q.join('&')}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# TODO (2014-02-03) jonk => document
@@ -1226,10 +1385,11 @@
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# TODO (2014-02-03) jonk => document
@@ -1253,10 +1413,11 @@
uri = build_uri("/accounts/#{account_id}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Delete.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
json = response.body
json = '{}' if json.nil? || json == ''
JSON.parse(json)
end
@@ -1271,11 +1432,13 @@
def get_templates
uri = build_uri("/accounts/#{acct_id}/templates")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers({ 'Content-Type' => 'application/json' }))
- JSON.parse(http.request(request).body)
+ response = http.request(request)
+ generate_log(request, response, uri)
+ JSON.parse(response.body)
end
# Public: Retrieves a list of templates used in an envelope
#
@@ -1285,11 +1448,13 @@
def get_templates_in_envelope(envelope_id)
uri = build_uri("/accounts/#{acct_id}/envelopes/#{envelope_id}/templates")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers({ 'Content-Type' => 'application/json' }))
- JSON.parse(http.request(request).body)
+ response = http.request(request)
+ generate_log(request, response, uri)
+ JSON.parse(response.body)
end
# Grabs envelope data.
# Equivalent to the following call in the API explorer:
@@ -1301,10 +1466,11 @@
uri = build_uri("/accounts/#{acct_id}/envelopes/#{envelope_id}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public deletes a recipient for a given envelope
@@ -1327,10 +1493,11 @@
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Delete.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public voids an in-process envelope
@@ -1351,15 +1518,17 @@
uri = build_uri("/accounts/#{acct_id}/envelopes/#{options[:folder_id]}")
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Put.new(uri.request_uri, headers(content_type))
request.body = post_body
- http.request(request)
+ response = http.request(request)
+ generate_log(request, response, uri)
+ response
end
# Public deletes a document for a given envelope
- # See https://www.docusign.com/p/RESTAPIGuide/RESTAPIGuide.htm#REST API References/Remove Documents from a Draft Envelope.htm%3FTocPath%3DREST%2520API%2520References%7C_____54
+ # See https://docs.docusign.com/esign/restapi/Envelopes/EnvelopeDocuments/delete/
#
# envelope_id - ID of the envelope from which the doc will be retrieved
# document_id - ID of the document to delete
#
# Returns the success or failure of each document being added to the envelope and
@@ -1380,15 +1549,16 @@
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Delete.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public adds a document to a given envelope
- # See https://www.docusign.com/p/RESTAPIGuide/RESTAPIGuide.htm#REST API References/Add Document.htm%3FTocPath%3DREST%2520API%2520References%7C_____56
+ # See https://docs.docusign.com/esign/restapi/Envelopes/EnvelopeDocuments/update/
#
# envelope_id - ID of the envelope from which the doc will be added
# document_id - ID of the document to add
# file_path - Local or remote path to file
# content_type - optional content type for file. Defaults to application/pdf.
@@ -1410,24 +1580,25 @@
post_body = open(options[:file_path]).read
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Put.new(uri.request_uri, headers(headers))
request.body = post_body
-
- http.request(request)
+ response = http.request(request)
+ generate_log(request, response, uri)
+ response
end
# Public adds signers to a given envelope
- # See https://www.docusign.com/p/RESTAPIGuide/RESTAPIGuide.htm#REST%20API%20References/Add%20Recipients%20to%20an%20Envelope.htm%3FTocPath%3DREST%2520API%2520References|_____77
+ # Seehttps://docs.docusign.com/esign/restapi/Envelopes/EnvelopeRecipients/update/
#
# envelope_id - ID of the envelope to which the recipient will be added
# signers - Array of hashes
- # See https://www.docusign.com/p/RESTAPIGuide/RESTAPIGuide.htm#REST%20API%20References/Recipients/Signers%20Recipient.htm%3FTocPath%3DREST%2520API%2520References|Send%2520an%2520Envelope%2520or%2520Create%2520a%2520Draft%2520Envelope|Recipient%2520Parameters|_____7
+ # See https://docs.docusign.com/esign/restapi/Envelopes/EnvelopeRecipients/update/#definitions
#
# TODO: This could be made more general as an add_envelope_recipient method
# to handle recipient types other than Signer
- # See: https://www.docusign.com/p/RESTAPIGuide/RESTAPIGuide.htm#REST%20API%20References/Recipient%20Parameter.htm%3FTocPath%3DREST%2520API%2520References|Send%2520an%2520Envelope%2520or%2520Create%2520a%2520Draft%2520Envelope|Recipient%2520Parameters|_____0
+ # See: https://docs.docusign.com/esign/restapi/Envelopes/EnvelopeRecipients/update/#examples
def add_envelope_signers(options = {})
content_type = { "Content-Type" => "application/json" }
content_type.merge(options[:headers]) if options[:headers]
uri = build_uri("/accounts/#{@acct_id}/envelopes/#{options[:envelope_id]}/recipients")
@@ -1436,15 +1607,16 @@
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Put.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
end
# Public adds recipient tabs to a given envelope
- # See https://www.docusign.com/p/RESTAPIGuide/RESTAPIGuide.htm#REST API References/Add Tabs for a Recipient.htm%3FTocPath%3DREST%2520API%2520References%7C_____86
+ # See https://docs.docusign.com/esign/restapi/Envelopes/EnvelopeRecipients/update/
#
# envelope_id - ID of the envelope from which the doc will be added
# recipient - ID of the recipient to add tabs to
# tabs - hash of tab (see example below)
# {
@@ -1486,9 +1658,34 @@
http = initialize_net_http_ssl(uri)
request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
request.body = post_body
response = http.request(request)
+ generate_log(request, response, uri)
JSON.parse(response.body)
+ end
+
+ private
+
+ # Private: Generates a standardized log of the request and response pair
+ # to and from DocuSign for logging and API Certification.
+ # and resulting list is set to the publicly accessible: @previous_call_log
+ # For example:
+ # envelope = connection.create_envelope_from_document(doc)
+ # connection.previous_call_log.each {|line| logger.debug line }
+ def generate_log(request, response, uri)
+ log = ['--DocuSign REQUEST--']
+ log << "#{request.method} #{uri.to_s}"
+ request.each_capitalized{ |k,v| log << "#{k}: #{v.gsub(/(?<="Password":")(.+?)(?=")/, '[FILTERED]')}" }
+ # Trims out the actual binary file to reduce log size
+ if request.body
+ request_body = request.body.gsub(/(?<=Content-Transfer-Encoding: binary).+?(?=-------------RubyMultipartPost)/m, "\n[BINARY BLOB]\n")
+ log << "Body: #{request_body}"
+ end
+ log << '--DocuSign RESPONSE--'
+ log << "HTTP/#{response.http_version} #{response.code} #{response.msg}"
+ response.each_capitalized{ |k,v| log << "#{k}: #{v}" }
+ log << "Body: #{response.body}"
+ @previous_call_log = log
end
end
end