docusign_rest 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DocusignRest
2
2
 
3
- This 'wrapper gem' hooks a Ruby app (currently only tested with Rails) up to the DocuSign REST API to allow for embedded signing.
3
+ This 'wrapper gem' hooks a Ruby app (currently only tested with Rails) up to the [DocuSign](http://www.docusign.com/) [REST API](http://www.docusign.com/developer-center) to allow for embedded signing.
4
4
 
5
5
  ## Installation
6
6
 
@@ -75,13 +75,14 @@ The above options allow you to change the endpoint (to be able to hit the produc
75
75
 
76
76
  The docusign\_rest gem makes creating multipart POST (aka file upload) requests to the DocuSign REST API dead simple. It's built on top of Net:HTTP and utilizes the [multipart-post](https://github.com/nicksieger/multipart-post) gem to assist with formatting the multipart requests. The DocuSign REST API requires that all files be embedded as JSON directly in the request body (not the body\_stream like multipart-post does by default) so the docusign\_rest gem takes care of [setting that up for you](https://github.com/j2fly/docusign_rest/blob/master/lib/docusign_rest/client.rb#L397).
77
77
 
78
- This gem also monkey patches one small part of multipart-post to inject some header values and formatting that DocuSign requires. If you would like to see the monkey patched code please take a look at [lib/multipart-post/parts.rb](https://github.com/j2fly/docusign_rest/blob/master/lib/multipart_post/parts.rb). It's only re-opening one method, but feel free to make sure you understand that monkey patch if it concerns you.
78
+ This gem also monkey patches one small part of multipart-post to inject some header values and formatting that DocuSign requires. If you would like to see the monkey patched code please take a look at [lib/multipart-post/parts.rb](https://github.com/j2fly/docusign_rest/blob/master/lib/multipart_post/parts.rb). It's only re-opening one method, but feel free to make sure you understand that code if it concerns you.
79
79
 
80
80
  ### Examples
81
81
 
82
- * These examples assume you have already run the `docusign_rest:generate_config` rake task and have the configure block properly setup in an initializer with your username, password, integrator\_key, and account\_id.
82
+ * These examples assume you have already run the `docusign_rest:generate_config` rake task and have the configure block properly setup in an initializer with your username, password, integrator\_key, and account\_id.
83
+ * Unless noted otherwise, these requests return the JSON parsed body of the response so you can index the returned hash directly. For example: `template_response["templateId"]`.
83
84
 
84
- **Getting login information:**
85
+ **Getting account_id:**
85
86
 
86
87
  ```ruby
87
88
  client = DocusignRest::Client.new
@@ -93,14 +94,14 @@ puts client.get_account_id
93
94
 
94
95
  ```ruby
95
96
  client = DocusignRest::Client.new
96
- response = client.create_envelope_from_document(
97
+ document_envelope_response = client.create_envelope_from_document(
97
98
  email: {
98
99
  subject: "test email subject",
99
100
  body: "this is the email body and it's large!"
100
101
  },
101
102
  # If embedded is set to true in the signers array below, emails
102
- # don't go out and you can embed the signature page in an iFrame
103
- # by using the get_recipient_view method
103
+ # don't go out to the signers and you can embed the signature page in an
104
+ # iFrame by using the client.get_recipient_view method
104
105
  signers: [
105
106
  {
106
107
  #embedded: true,
@@ -110,16 +111,15 @@ response = client.create_envelope_from_document(
110
111
  {
111
112
  #embedded: true,
112
113
  name: 'Test Girl',
113
- email: 'someone@gmail.com'
114
+ email: 'someone+else@gmail.com'
114
115
  }
115
116
  ],
116
117
  files: [
117
- {path: 'test.pdf', name: 'test.pdf'},
118
- {path: 'test2.pdf', name: 'test2.pdf'}
118
+ {path: '/Absolute/path/to/test.pdf', name: 'test.pdf'},
119
+ {path: '/Absolute/path/to/test2.pdf', name: 'test2.pdf'}
119
120
  ],
120
121
  status: 'sent'
121
122
  )
122
- response = JSON.parse(response.body)
123
123
  ```
124
124
 
125
125
 
@@ -127,23 +127,29 @@ response = JSON.parse(response.body)
127
127
 
128
128
  ```ruby
129
129
  client = DocusignRest::Client.new
130
- response = client.create_template(
131
- description: 'Cool Description',
132
- name: "Cool Template Name",
130
+ @template_response = client.create_template(
131
+ description: 'Template Description',
132
+ name: "Template Name",
133
133
  signers: [
134
134
  {
135
135
  embedded: true,
136
136
  name: 'jon',
137
137
  email: 'someone@gmail.com',
138
138
  role_name: 'Issuer',
139
- anchor_string: 'sign here'
139
+ anchor_string: 'issuer_sig'
140
+ },
141
+ {
142
+ embedded: true,
143
+ name: 'tim',
144
+ email: 'someone+else@gmail.com',
145
+ role_name: 'Attorney',
146
+ anchor_string: 'attorney_sig'
140
147
  }
141
148
  ],
142
149
  files: [
143
- {path: 'test.pdf', name: 'test.pdf'}
150
+ {path: '/Absolute/path/to/test.pdf', name: 'test.pdf'}
144
151
  ]
145
152
  )
146
- @template_response = JSON.parse(response.body)
147
153
  ```
148
154
 
149
155
 
@@ -151,7 +157,7 @@ response = client.create_template(
151
157
 
152
158
  ```ruby
153
159
  client = DocusignRest::Client.new
154
- response = client.create_envelope_from_template(
160
+ @envelope_response = client.create_envelope_from_template(
155
161
  status: 'sent',
156
162
  email: {
157
163
  subject: "The test email subject envelope",
@@ -164,28 +170,80 @@ response = client.create_envelope_from_template(
164
170
  name: 'jon',
165
171
  email: 'someone@gmail.com',
166
172
  role_name: 'Issuer'
173
+ },
174
+ {
175
+ embedded: true,
176
+ name: 'tim',
177
+ email: 'someone+else@gmail.com',
178
+ role_name: 'Attorney'
167
179
  }
168
180
  ]
169
181
  )
170
- @envelope_response = JSON.parse(response.body)
171
182
  ```
172
183
 
173
184
 
174
- **Retrieving the url for embedded signing**
185
+ **Retrieving the url for embedded signing. (Returns a string, not a hash)**
175
186
 
176
187
  ```ruby
177
188
  client = DocusignRest::Client.new
178
- response = client.get_recipient_view(
189
+ @url = client.get_recipient_view(
179
190
  envelope_id: @envelope_response["envelopeId"],
180
- name: 'jon',
181
- email: 'someone@gmail.com',
191
+ name: current_user.full_name,
192
+ email: current_user.email,
182
193
  return_url: 'http://google.com'
183
194
  )
184
- @view_recipient_response = JSON.parse(response.body)
185
- puts @view_recipient_response["url"]
186
195
  ```
187
196
 
188
197
 
198
+ **Check status of an envelope including the signers hash w/ the status of each signer**
199
+
200
+ ```ruby
201
+ client = DocusignRest::Client.new
202
+ response = client.get_envelope_recipients(
203
+ envelope_id: @envelope_response["envelopeId"],
204
+ include_tabs: true,
205
+ include_extended: true
206
+ )
207
+ ```
208
+
209
+
210
+ ## Breaking out of the iFrame after signing
211
+
212
+ In order to return to your application after the signing process is complete it's important to have a way to evaluate whether or not the signing was successful and then do something about each case. The way I set this up was to render the embedded signing iframe for a controller action called 'embedded_signing' and specify the return_url of the `client.get_recipient_view` API call to be something like: http://myapp.com/docusign_response. Then in the same controller as the embedded_signing method, define the docusign_response method. This is where the signing process will redirect to after the user is done interacting with the DocuSign iframe. DocuSign passes a query string parameter in the return_url named 'event' and you can check like so: `if params[:event] == "signing_complete"` then you'll want to redirect to another spot in your app, not in the iframe. To do so, we need to use JavaScript to access the iframe's parent and set it's location to the path of our choosing. To do this, instanciate the DocusignRest::Utility class and call the breakout_path method like this:
213
+
214
+ ```ruby
215
+ class SomeController < ApplicationController
216
+
217
+ # the view corresponding to this action has the iFrame in it with the
218
+ # @url as it's src. @envelope_response is populated from either:
219
+ # @envelope_response = client.create_envelope_from_document
220
+ # or
221
+ # @envelope_response = client.create_envelope_from_template
222
+ def embedded_signing
223
+ client = DocusignRest::Client.new
224
+ @url = client.get_recipient_view(
225
+ envelope_id: @envelope_response["envelopeId"],
226
+ name: current_user.display_name,
227
+ email: current_user.email,
228
+ return_url: "http://localhost:3000/docusign_response"
229
+ )
230
+ end
231
+
232
+ def docusign_response
233
+ utility = DocusignRest::Utility.new
234
+
235
+ if params[:event] == "signing_complete"
236
+ flash[:notice] = "Thanks! Successfully signed"
237
+ render :text => utility.breakout_path(some_path), content_type: :html
238
+ else
239
+ flash[:notice] = "You chose not to sign the document."
240
+ render :text => utility.breakout_path(some_other_path), content_type: :html
241
+ end
242
+ end
243
+
244
+ end
245
+ ```
246
+
189
247
  ## Contributing
190
248
 
191
249
  1. Fork it
@@ -1,6 +1,7 @@
1
1
  require 'docusign_rest/version'
2
2
  require 'docusign_rest/configuration'
3
3
  require 'docusign_rest/client'
4
+ require 'docusign_rest/utility'
4
5
  require 'multipart_post' #require the multipart-post gem itself
5
6
  require 'net/http/post/multipart' #require the multipart-post net/http/post/multipart monkey patch
6
7
  require 'multipart_post/parts' #require my monkey patched parts.rb which adjusts the build_part method
@@ -243,8 +243,8 @@ module DocusignRest
243
243
  \"routingOrder\":#{index+1},
244
244
  \"socialAuthentications\":null,
245
245
  \"templateAccessCodeRequired\":false,
246
- \"templateLocked\":#{signer[:template_locked] || 'false'},
247
- \"templateRequired\":#{signer[:template_required] || 'false'},
246
+ \"templateLocked\":#{signer[:template_locked] || true},
247
+ \"templateRequired\":#{signer[:template_required] || true},
248
248
  \"email\":\"#{signer[:email]}\",
249
249
  \"name\":\"#{signer[:name]}\",
250
250
  \"autoNavigation\":false,
@@ -268,20 +268,24 @@ module DocusignRest
268
268
  \"signHereTabs\":[
269
269
  {
270
270
  \"anchorString\":\"#{signer[:anchor_string]}\",
271
- \"conditionalParentLabel\":null,
272
- \"conditionalParentValue\":null,
271
+ \"anchorXOffset\": \"0\",
272
+ \"anchorYOffset\": \"0\",
273
+ \"anchorIgnoreIfNotPresent\": false,
274
+ \"anchorUnits\": \"pixels\",
275
+ \"conditionalParentLabel\": null,
276
+ \"conditionalParentValue\": null,
273
277
  \"documentId\":\"#{signer[:document_id] || '1'}\",
274
278
  \"pageNumber\":\"#{signer[:page_number] || '1'}\",
275
279
  \"recipientId\":\"#{index+1}\",
276
- \"templateLocked\":#{signer[:template_locked] || 'false'},
277
- \"templateRequired\":#{signer[:template_required] || 'false'},
280
+ \"templateLocked\":#{signer[:template_locked] || true},
281
+ \"templateRequired\":#{signer[:template_required] || true},
278
282
  \"xPosition\":\"#{signer[:x_position] || '0'}\",
279
283
  \"yPosition\":\"#{signer[:y_position] || '0'}\",
280
284
  \"name\":\"#{signer[:sign_here_tab_text] || 'Sign Here'}\",
281
285
  \"optional\":false,
282
286
  \"scaleValue\":1,
283
287
  \"tabLabel\":\"#{signer[:tab_label] || 'Signature 1'}\"
284
- }
288
+ },
285
289
  ],
286
290
  \"signerAttachmentTabs\":null,
287
291
  \"ssnTabs\":null,
@@ -425,7 +429,7 @@ module DocusignRest
425
429
  # sending at a later time
426
430
  # headers - Allows a client to pass in some
427
431
  #
428
- # Returns a response object containing:
432
+ # Returns a JSON parsed response object containing:
429
433
  # envelopeId - The envelope's ID
430
434
  # status - Sent, created, or voided
431
435
  # statusDateTime - The date/time the envelope was created
@@ -454,7 +458,8 @@ module DocusignRest
454
458
  )
455
459
 
456
460
  # Finally do the Net::HTTP request!
457
- http.request(request)
461
+ response = http.request(request)
462
+ parsed_response = JSON.parse(response.body)
458
463
  end
459
464
 
460
465
 
@@ -482,7 +487,7 @@ module DocusignRest
482
487
  # headers - Optional hash of headers to merge into the existing
483
488
  # required headers for a multipart request.
484
489
  #
485
- # Returns a response body containing the template's:
490
+ # Returns a JSON parsed response body containing the template's:
486
491
  # name - Name given above
487
492
  # templateId - The auto-generated ID provided by DocuSign
488
493
  # Uri - the URI where the template is located on the DocuSign servers
@@ -516,7 +521,8 @@ module DocusignRest
516
521
  )
517
522
 
518
523
  # Finally do the Net::HTTP request!
519
- http.request(request)
524
+ response = http.request(request)
525
+ parsed_response = JSON.parse(response.body)
520
526
  end
521
527
 
522
528
 
@@ -538,7 +544,7 @@ module DocusignRest
538
544
  # headers - Optional hash of headers to merge into the existing
539
545
  # required headers for a multipart request.
540
546
  #
541
- # Returns a response body containing the envelope's:
547
+ # Returns a JSON parsed response body containing the envelope's:
542
548
  # name - Name given above
543
549
  # templateId - The auto-generated ID provided by DocuSign
544
550
  # Uri - the URI where the template is located on the DocuSign servers
@@ -558,13 +564,11 @@ module DocusignRest
558
564
 
559
565
  http = initialize_net_http_ssl(uri)
560
566
 
561
- request = Net::HTTP::Post.new(
562
- uri.request_uri,
563
- headers(content_type)
564
- )
567
+ request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
565
568
  request.body = post_body
566
569
 
567
- http.request(request)
570
+ response = http.request(request)
571
+ parsed_response = JSON.parse(response.body)
568
572
  end
569
573
 
570
574
 
@@ -578,7 +582,7 @@ module DocusignRest
578
582
  # headers - optional hash of headers to merge into the existing
579
583
  # required headers for a multipart request.
580
584
  #
581
- # Returns the URL for embedded signing which needs to be put in an iFrame
585
+ # Returns the URL string for embedded signing (can be put in an iFrame)
582
586
  def get_recipient_view(options={})
583
587
  content_type = {'Content-Type' => 'application/json'}
584
588
  content_type.merge(options[:headers]) if options[:headers]
@@ -595,15 +599,38 @@ module DocusignRest
595
599
 
596
600
  http = initialize_net_http_ssl(uri)
597
601
 
598
- request = Net::HTTP::Post.new(
599
- uri.request_uri,
600
- headers(content_type)
601
- )
602
+ request = Net::HTTP::Post.new(uri.request_uri, headers(content_type))
602
603
  request.body = post_body
603
604
 
604
- http.request(request)
605
+ response = http.request(request)
606
+ parsed_response = JSON.parse(response.body)
607
+ parsed_response["url"]
605
608
  end
606
609
 
610
+ # Public returns the envelope recipients for a given envelope
611
+ #
612
+ # include_tabs - boolean, determines if the tabs for each signer will be
613
+ # returned in the response, defaults to false.
614
+ # envelope_id - ID of the envelope for which you want to retrive the
615
+ # signer info
616
+ # headers - optional hash of headers to merge into the existing
617
+ # required headers for a multipart request.
618
+ #
619
+ # Returns a hash of detailed info about the envelope including the signer
620
+ # hash and status of each signer
621
+ def get_envelope_recipients(options={})
622
+ content_type = {'Content-Type' => 'application/json'}
623
+ content_type.merge(options[:headers]) if options[:headers]
624
+
625
+ include_tabs = options[:include_tabs] || false
626
+ include_extended = options[:include_extended] || false
627
+ uri = build_uri("/accounts/#{@acct_id}/envelopes/#{options[:envelope_id]}/recipients?include_tabs=#{include_tabs}&include_extended=#{include_extended}")
628
+
629
+ http = initialize_net_http_ssl(uri)
630
+ request = Net::HTTP::Get.new(uri.request_uri, headers(content_type))
631
+ response = http.request(request)
632
+ parsed_response = JSON.parse(response.body)
633
+ end
607
634
  end
608
635
 
609
636
  end
@@ -0,0 +1,54 @@
1
+ module DocusignRest
2
+
3
+ class Utility
4
+ # Public takes a path to redirect to and breaks the redirect out of an iFrame
5
+ #
6
+ # This can be used after embedded signing is either successful or not and has
7
+ # been redirected to a controller action (like docusign_response for instance)
8
+ # where the return from the signing process can be evaluated. If successful
9
+ # use this to redirect to one place, otherwise redirect to another place. You
10
+ # can use params[:event] to evaluate whether or not the signing was successful
11
+ #
12
+ # path - a relative or absolute path or rails helper can be passed in
13
+ #
14
+ # Example
15
+ #
16
+ # class SomeController < ApplicationController
17
+ #
18
+ # # the view corresponding to this action has the iFrame in it with the
19
+ # # @url as it's src. @envelope_response is populated from either:
20
+ # # @envelope_response = client.create_envelope_from_document
21
+ # # or
22
+ # # @envelope_response = client.create_envelope_from_template
23
+ # def embedded_signing
24
+ # client = DocusignRest::Client.new
25
+ # @url = client.get_recipient_view(
26
+ # envelope_id: @envelope_response["envelopeId"],
27
+ # name: current_user.display_name,
28
+ # email: current_user.email,
29
+ # return_url: "http://localhost:3000/docusign_response"
30
+ # )
31
+ # end
32
+ #
33
+ # def docusign_response
34
+ # utility = DocusignRest::Utility.new
35
+ #
36
+ # if params[:event] == "signing_complete"
37
+ # flash[:notice] = "Thanks! Successfully signed"
38
+ # render :text => utility.breakout_path(posts_path), content_type: :html
39
+ # else
40
+ # flash[:notice] = "You chose not to sign the document."
41
+ # render :text => utility.breakout_path(logout_path), content_type: :html
42
+ # end
43
+ # end
44
+ #
45
+ # end
46
+ #
47
+ # Returns a string of HTML including some JS to redirect to the passed in
48
+ # path but in the iFrame's parent.
49
+ def breakout_path(path)
50
+ "<html><body><script type='text/javascript' charset='utf-8'>parent.location.href = '#{path}';</script></body></html>"
51
+ end
52
+ end
53
+
54
+ end
@@ -1,3 +1,3 @@
1
1
  module DocusignRest
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -109,7 +109,6 @@ describe DocusignRest::Client do
109
109
  ],
110
110
  status: 'sent'
111
111
  )
112
- response = JSON.parse(response.body)
113
112
  response["status"].must_equal "sent"
114
113
  end
115
114
  end
@@ -118,7 +117,7 @@ describe DocusignRest::Client do
118
117
  before do
119
118
  # create the template dynamically
120
119
  VCR.use_cassette("create_template", record: :once) do
121
- response = @client.create_template(
120
+ @template_response = @client.create_template(
122
121
  description: 'Cool Description',
123
122
  name: "Cool Template Name",
124
123
  signers: [
@@ -138,12 +137,11 @@ describe DocusignRest::Client do
138
137
  {path: 'test.pdf', name: 'test.pdf'}
139
138
  ]
140
139
  )
141
- @template_response = JSON.parse(response.body)
142
140
  end
143
141
 
144
142
  # use the templateId to get the envelopeId
145
143
  VCR.use_cassette("create_envelope/from_template", record: :once) do
146
- response = @client.create_envelope_from_template(
144
+ @envelope_response = @client.create_envelope_from_template(
147
145
  status: 'sent',
148
146
  email: {
149
147
  subject: "The test email subject envelope",
@@ -159,7 +157,6 @@ describe DocusignRest::Client do
159
157
  }
160
158
  ]
161
159
  )
162
- @envelope_response = JSON.parse(response.body)
163
160
  end
164
161
  end
165
162
 
@@ -179,11 +176,22 @@ describe DocusignRest::Client do
179
176
  email: 'someone@gmail.com',
180
177
  return_url: 'http://google.com'
181
178
  )
182
- @view_recipient_response = JSON.parse(response.body)
183
- puts @view_recipient_response["url"]
179
+ response.must_match(/http/)
184
180
  end
185
181
  end
186
182
 
183
+ #status return values = "sent", "delivered", "completed"
184
+ it "should retrieve the envelope recipients status" do
185
+ VCR.use_cassette("get_envelope_recipients", record: :once) do
186
+ response = @client.get_envelope_recipients(
187
+ envelope_id: @envelope_response["envelopeId"],
188
+ include_tabs: true,
189
+ include_extended: true
190
+ )
191
+ response["signers"].wont_be_nil
192
+ #puts response["signers"]
193
+ end
194
+ end
187
195
  end
188
196
 
189
197
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docusign_rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-19 00:00:00.000000000 Z
12
+ date: 2012-06-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multipart-post
@@ -158,6 +158,7 @@ files:
158
158
  - lib/docusign_rest/client.rb
159
159
  - lib/docusign_rest/configuration.rb
160
160
  - lib/docusign_rest/railtie.rb
161
+ - lib/docusign_rest/utility.rb
161
162
  - lib/docusign_rest/version.rb
162
163
  - lib/multipart_post/parts.rb
163
164
  - lib/tasks/docusign_task.rake