docusigner 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/README.md +107 -0
- data/Rakefile +19 -0
- data/docusigner.gemspec +19 -0
- data/lib/docusigner/account.rb +27 -0
- data/lib/docusigner/base.rb +93 -0
- data/lib/docusigner/billing_plan.rb +8 -0
- data/lib/docusigner/brand.rb +5 -0
- data/lib/docusigner/connection.rb +24 -0
- data/lib/docusigner/custom_field.rb +5 -0
- data/lib/docusigner/document.rb +5 -0
- data/lib/docusigner/envelope.rb +55 -0
- data/lib/docusigner/folder.rb +5 -0
- data/lib/docusigner/group.rb +5 -0
- data/lib/docusigner/login_information.rb +16 -0
- data/lib/docusigner/multipart.rb +122 -0
- data/lib/docusigner/oauth2.rb +24 -0
- data/lib/docusigner/recipient.rb +17 -0
- data/lib/docusigner/settings.rb +21 -0
- data/lib/docusigner/tab.rb +5 -0
- data/lib/docusigner/template.rb +5 -0
- data/lib/docusigner/user.rb +29 -0
- data/lib/docusigner.rb +25 -0
- data/test/test.pdf +0 -0
- data/test/test_helper.rb +11 -0
- data/test/unit/account_test.rb +405 -0
- data/test/unit/base_test.rb +35 -0
- data/test/unit/envelope_test.rb +141 -0
- data/test/unit/login_test.rb +66 -0
- data/test/unit/oauth_test.rb +26 -0
- metadata +146 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Docusigner
|
2
|
+
|
3
|
+
This gem is meant to be a simple ActiveResource based interface to DocuSign's REST api. Where applicable, objects know about their association relationships.
|
4
|
+
|
5
|
+
You can read more about DocuSign's REST API:
|
6
|
+
|
7
|
+
* [Developer Center](http://www.docusign.com/developers-center)
|
8
|
+
* [API Documentation (PDF)](http://www.docusign.com/sites/default/files/REST_API_Guide_v2.pdf)
|
9
|
+
|
10
|
+
*SPECIAL NOTE*: I do not yet have access to the production server, so please consider this a beta. It has only been tested against the demo server and against the expected responses provided by the above API guide.
|
11
|
+
|
12
|
+
|
13
|
+
## Requirements
|
14
|
+
|
15
|
+
* `reactive_resource`
|
16
|
+
* `multipart_post`
|
17
|
+
|
18
|
+
This library will handle multipart post requests to DocuSign and provide the correct headers for the individual posts. The `multipart_post` gem is required for its internals, but not used as it did not allow the headers DocuSign expects.
|
19
|
+
|
20
|
+
## Setup
|
21
|
+
|
22
|
+
You can use either the X-DocuSign-Authentication header or an OAuth2 bearer token to access the API. Configuration for your app is simple.
|
23
|
+
|
24
|
+
### X-DocuSign-Authentication
|
25
|
+
|
26
|
+
Docusigner::Base.authentication = {
|
27
|
+
:username => "your_username_here",
|
28
|
+
:password => "your_password_here",
|
29
|
+
:integrator_key => "your_integrator_key_here"
|
30
|
+
}
|
31
|
+
|
32
|
+
### OAuth2
|
33
|
+
|
34
|
+
Docusigner::Base.token = "your_api_token"
|
35
|
+
|
36
|
+
Additionally, you can easily request (or revoke) a token through the API.
|
37
|
+
|
38
|
+
# request an OAuth2 token
|
39
|
+
token = Docusigner::Oauth2.token("username", "password", "integrator_key")
|
40
|
+
|
41
|
+
# revoke an OAuth2 token
|
42
|
+
Docusigner::Oauth2.revoke("token")
|
43
|
+
|
44
|
+
### Domain
|
45
|
+
|
46
|
+
By default, the API points to the development platform at https://demo.docusign.net. Changing to the live site is simple:
|
47
|
+
|
48
|
+
Docusigner::Base.site = "https://www.docusign.net/restapi/v2"
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
Once you've configured the client, accessing resources is easy. This client is based off of [reactive_resource](http://github.com/justinweiss/reactive_resource) which is built off of [active_resource](http://api.rubyonrails.org/classes/ActiveResource/Base.html). Code should look similar to using ActiveRecord objects.
|
53
|
+
|
54
|
+
### Examples:
|
55
|
+
|
56
|
+
#### Fetch basic account information
|
57
|
+
|
58
|
+
# find the account
|
59
|
+
account = Docusigner::Account.find(1234)
|
60
|
+
|
61
|
+
# access basic attributes
|
62
|
+
account.id
|
63
|
+
=> 1234
|
64
|
+
account.name
|
65
|
+
=> "My account name"
|
66
|
+
|
67
|
+
# list templates
|
68
|
+
account.templates
|
69
|
+
=> [#<Docusigner::Template>, #<Docusigner::Template>]
|
70
|
+
|
71
|
+
#### Create an envelope
|
72
|
+
|
73
|
+
envelope = Docusigner::Envelope.new({
|
74
|
+
:account_id => 1234,
|
75
|
+
:emailSubject => "Fee Agreement",
|
76
|
+
:emailBlurb => "Please sign the attached document"
|
77
|
+
:recipients => {
|
78
|
+
:signers => [
|
79
|
+
{
|
80
|
+
:email => "signer@gmail.com",
|
81
|
+
:name => "Bob Smith",
|
82
|
+
:recipientId => 1,
|
83
|
+
:clientUserId => 123, # if you want to do
|
84
|
+
:tabs => {
|
85
|
+
# can add tabs here
|
86
|
+
},
|
87
|
+
}
|
88
|
+
]
|
89
|
+
},
|
90
|
+
:documents => [
|
91
|
+
{
|
92
|
+
:name => "Fee Agreement",
|
93
|
+
:documentId => 333,
|
94
|
+
}
|
95
|
+
],
|
96
|
+
:status => Docusigner::Envelope::Status::SENT
|
97
|
+
})
|
98
|
+
envelope.add_document(File.open("/path/to/document.pdf"), 333)
|
99
|
+
envelope.save
|
100
|
+
|
101
|
+
For the most part, the complex data structures expected as parameters can be expressed with nested hashes when creating elements.
|
102
|
+
|
103
|
+
## Contributing
|
104
|
+
|
105
|
+
If you would like to contribute, please fork my [repository](http://github.com/chingor13/docusigner) and send me a pull request. Please include tests.
|
106
|
+
|
107
|
+
I have not implemented every API endpoint, as I do not have enough time right now.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rdoc/task'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
task :default => :test
|
7
|
+
task :build => :test
|
8
|
+
|
9
|
+
Rake::TestTask.new do |t|
|
10
|
+
t.libs << "test"
|
11
|
+
t.test_files = FileList['test/**/*_test.rb']
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
Rake::RDocTask.new do |rd|
|
16
|
+
rd.main = "README.rdoc"
|
17
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
18
|
+
rd.rdoc_dir = 'doc'
|
19
|
+
end
|
data/docusigner.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "docusigner"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.description = "Unofficial gem for accessing the DocuSign REST API"
|
5
|
+
s.summary = "Unofficial gem for accessing the DocuSign REST API"
|
6
|
+
s.add_dependency "reactive_resource", ">= 0.7.2"
|
7
|
+
s.add_dependency "multipart-post"
|
8
|
+
|
9
|
+
s.add_development_dependency "shoulda"
|
10
|
+
s.add_development_dependency "webmock"
|
11
|
+
|
12
|
+
s.author = "Jeff Ching"
|
13
|
+
s.email = "jeff@chingr.com"
|
14
|
+
s.homepage = "http://github.com/chingor13/docusigner"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Docusigner
|
2
|
+
class Account < Docusigner::Base
|
3
|
+
attr_accessor :id
|
4
|
+
|
5
|
+
has_one :billing_plan
|
6
|
+
has_one :settings, :class_name => "Docusigner::Settings"
|
7
|
+
|
8
|
+
has_many :brands
|
9
|
+
has_many :custom_fields
|
10
|
+
|
11
|
+
# define this manually because the index action requires a from_date
|
12
|
+
def envelopes(from_date, options = {})
|
13
|
+
Docusigner::Envelope.find(:all, :params => options.reverse_merge({:account_id => id, :from_date => from_date}))
|
14
|
+
end
|
15
|
+
|
16
|
+
has_many :folders
|
17
|
+
has_many :groups
|
18
|
+
has_many :templates
|
19
|
+
has_many :users
|
20
|
+
|
21
|
+
def self.find_single(id, options)
|
22
|
+
super(id, options).tap do |r|
|
23
|
+
r.id = id
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'reactive_resource'
|
2
|
+
require 'docusigner/multipart'
|
3
|
+
module Docusigner
|
4
|
+
class Base < ReactiveResource::Base
|
5
|
+
self.site = "https://demo.docusign.net/restapi/v2"
|
6
|
+
self.format = :json
|
7
|
+
self.include_root_in_json = false
|
8
|
+
|
9
|
+
# allow you to attach documents
|
10
|
+
include Docusigner::Multipart::Resource
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# we want to inherit headers for authentication
|
14
|
+
def headers
|
15
|
+
@headers ||= begin
|
16
|
+
superclass.respond_to?(:headers) ? superclass.headers.dup : {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def connection(refresh = false)
|
21
|
+
if defined?(@connection) || self == Docusigner::Base
|
22
|
+
@connection = Docusigner::Connection.new(site, format) if refresh || @connection.nil? || !@connection.is_a?(Docusigner::Connection)
|
23
|
+
@connection.timeout = timeout if timeout
|
24
|
+
@connection.ssl_options = ssl_options if ssl_options
|
25
|
+
@connection
|
26
|
+
else
|
27
|
+
superclass.connection
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def token=(token)
|
32
|
+
headers['Authorization'] = "Bearer #{token}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def authorization=(options = {})
|
36
|
+
headers['X-DocuSign-Authentication'] = "<DocuSignCredentials><Username>%{username}</Username><Password>%{password}</Password><IntegratorKey>%{integrator_key}</IntegratorKey></DocuSignCredentials>" % options
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# we want to automatically set the foreign keys if they are provided
|
42
|
+
def instantiate_record(record, prefix_options = {})
|
43
|
+
super(record, prefix_options).tap do |r|
|
44
|
+
prefix_options.each do |k, v|
|
45
|
+
if r.respond_to?("#{k}=")
|
46
|
+
r.send("#{k}=", v)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# some of DocuSign's responses contain metadata about the response (e.g. number of records returned)
|
53
|
+
def instantiate_collection(data, prefix_options = {})
|
54
|
+
if data.is_a?(Hash)
|
55
|
+
# if the data has the collection name as a root element, use that to build the records
|
56
|
+
if data.has_key?(collection_name)
|
57
|
+
super(data[collection_name], prefix_options)
|
58
|
+
else
|
59
|
+
instantiate_flattened_collection(data, prefix_options)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
super(data, prefix_options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def instantiate_flattened_collection(data, prefix_options)
|
67
|
+
flattened = []
|
68
|
+
data.each do |type, array|
|
69
|
+
array.each do |obj|
|
70
|
+
flattened << instantiate_record(obj.merge(:type => type), prefix_options)
|
71
|
+
end if array.is_a?(Array)
|
72
|
+
end
|
73
|
+
flattened
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# the json should skip the root element
|
78
|
+
def to_json(opts = {})
|
79
|
+
as_json.to_json
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
# we want any generated resources for generated models to extend from this base class
|
85
|
+
# mainly because we don't want the include the root in the json representation
|
86
|
+
def create_resource_for(resource_name)
|
87
|
+
resource = self.class.const_set(resource_name, Class.new(Docusigner::Base))
|
88
|
+
resource.prefix = self.class.prefix
|
89
|
+
resource.site = self.class.site
|
90
|
+
resource
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Docusigner
|
2
|
+
class Connection < ActiveResource::Connection
|
3
|
+
def post(path, body = '', headers = {})
|
4
|
+
if body.is_a?(Array)
|
5
|
+
with_auth do
|
6
|
+
req = Docusigner::Multipart::Post.new(path, body, build_request_headers(headers, :post, self.site.merge(path)))
|
7
|
+
handle_response(http.request(req))
|
8
|
+
end
|
9
|
+
else
|
10
|
+
super(path, body, headers)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def put(path, body = '', headers = {})
|
15
|
+
if body.is_a?(Array)
|
16
|
+
req = Docusigner::Multipart::Put.new(path, body, headers)
|
17
|
+
with_auth { request(:request, req, build_request_headers(headers, :put, self.site.merge(path))) }
|
18
|
+
else
|
19
|
+
super(path, body, headers)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Docusigner
|
2
|
+
class Envelope < Docusigner::Base
|
3
|
+
module Status
|
4
|
+
CREATED = "created"
|
5
|
+
DELETED = "deleted"
|
6
|
+
SENT = "sent"
|
7
|
+
DELIVERED = "delivered"
|
8
|
+
SIGNED = "signed"
|
9
|
+
COMPLETED = "completed"
|
10
|
+
DECLINED = "declined"
|
11
|
+
VOIDED = "voided"
|
12
|
+
TIMED_OUT = "timedout"
|
13
|
+
AUTHORITATIVE_COPY = "authoritativecopy"
|
14
|
+
TRANSFER_COMPLETED = "transfercompleted"
|
15
|
+
TEMPLATE = "template"
|
16
|
+
CORRECT = "correct"
|
17
|
+
end
|
18
|
+
|
19
|
+
belongs_to :account
|
20
|
+
|
21
|
+
has_many :documents
|
22
|
+
has_many :recipients
|
23
|
+
|
24
|
+
def id
|
25
|
+
attributes["envelopeId"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def send!
|
29
|
+
update_attribute(:status, Docusigner::Envelope::Status::SENT)
|
30
|
+
end
|
31
|
+
|
32
|
+
def void!(reason)
|
33
|
+
update_attributes({
|
34
|
+
:status => Docusigner::Envelope::Status::VOIDED,
|
35
|
+
:voidReason => reason
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
def recipient_url(params = {})
|
40
|
+
resp = post("views/recipient", prefix_options, params.to_json)
|
41
|
+
self.class.format.decode(resp.body)
|
42
|
+
end
|
43
|
+
|
44
|
+
def sender_url(params = {})
|
45
|
+
resp = post("views/sender", prefix_options, params.to_json)
|
46
|
+
self.class.format.decode(resp.body)
|
47
|
+
end
|
48
|
+
|
49
|
+
def correct_url(params = {})
|
50
|
+
resp = post("views/correct", prefix_options, params.to_json)
|
51
|
+
self.class.format.decode(resp.body)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Docusigner
|
2
|
+
class LoginInformation < Docusigner::Base
|
3
|
+
singleton
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def change_password(email, current_password, new_password, options = {})
|
7
|
+
body = {
|
8
|
+
"currentPassword" => current_password,
|
9
|
+
"email" => email,
|
10
|
+
"newPassword" => new_password
|
11
|
+
}.merge(options).to_json
|
12
|
+
resp = put(:password, {}, body)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'net/http/post/multipart'
|
2
|
+
require 'active_support/all'
|
3
|
+
|
4
|
+
# this class leverages some parts of the multipart-post gem,
|
5
|
+
# but we need to change some headers
|
6
|
+
module Docusigner
|
7
|
+
module Multipart
|
8
|
+
module Multipartable
|
9
|
+
DEFAULT_BOUNDARY = "-----------RubyMultipartPost"
|
10
|
+
def initialize(path, params, headers={}, opts = {})
|
11
|
+
boundary = opts[:boundary] || DEFAULT_BOUNDARY
|
12
|
+
super(path, headers)
|
13
|
+
parts = params.map {|v| Docusigner::Multipart::Parts.build(boundary, v, opts)}
|
14
|
+
parts << ::Parts::EpiloguePart.new(boundary)
|
15
|
+
ios = parts.map{|p| p.to_io }
|
16
|
+
self.set_content_type("multipart/form-data", { "boundary" => boundary })
|
17
|
+
self.content_length = parts.inject(0) {|sum,i| sum + i.length }
|
18
|
+
self.body_stream = CompositeReadIO.new(*ios)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Parts
|
23
|
+
def self.build(boundary, value, opts = {})
|
24
|
+
if value.is_a?(Array)
|
25
|
+
self.build(boundary, value.first, opts.merge(value.last))
|
26
|
+
elsif value.is_a?(String)
|
27
|
+
DataPart.new(boundary, value, opts)
|
28
|
+
elsif value.is_a?(UploadIO)
|
29
|
+
DocumentPart.new(boundary, value, opts)
|
30
|
+
else
|
31
|
+
DataPart.new(boundary, value, opts)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class DataPart < StringIO
|
36
|
+
def initialize(boundary, data, opts = {})
|
37
|
+
@format = opts[:format] || :json
|
38
|
+
@content_type = {
|
39
|
+
:json => "application/json",
|
40
|
+
:xml => "application/xml"
|
41
|
+
}[@format]
|
42
|
+
super(build(boundary, data, opts))
|
43
|
+
end
|
44
|
+
def to_io
|
45
|
+
self
|
46
|
+
end
|
47
|
+
protected
|
48
|
+
def build(boundary, value, opts = {})
|
49
|
+
[
|
50
|
+
"--#{boundary}",
|
51
|
+
"Content-Type: #{@content_type}",
|
52
|
+
"Content-Disposition: form-data",
|
53
|
+
"",
|
54
|
+
value,
|
55
|
+
""
|
56
|
+
].join("\r\n")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class DocumentPart
|
61
|
+
attr_reader :length
|
62
|
+
def initialize(boundary, upload_io, opts = {})
|
63
|
+
@upload_io = upload_io
|
64
|
+
head = build(boundary, @upload_io, opts)
|
65
|
+
foot = "\r\n"
|
66
|
+
@output_io = CompositeReadIO.new(StringIO.new(head), @upload_io.io, StringIO.new(foot))
|
67
|
+
@length = head.length + file_length + foot.length
|
68
|
+
end
|
69
|
+
def to_io
|
70
|
+
@output_io
|
71
|
+
end
|
72
|
+
protected
|
73
|
+
|
74
|
+
def file_length
|
75
|
+
@file_length ||= @upload_io.respond_to?(:length) ? @upload_io.length : File.size(@upload_io.local_path)
|
76
|
+
end
|
77
|
+
|
78
|
+
def build(boundary, io, opts = {})
|
79
|
+
[
|
80
|
+
"--#{boundary}",
|
81
|
+
%(Content-Type: #{io.content_type}),
|
82
|
+
%(Content-Disposition: file; filename="#{opts[:name]}"; documentId=#{opts[:document_id]}),
|
83
|
+
%(Content-Length: #{file_length}),
|
84
|
+
"",
|
85
|
+
""
|
86
|
+
].join("\r\n")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Post < Net::HTTP::Post
|
92
|
+
include Docusigner::Multipart::Multipartable
|
93
|
+
end
|
94
|
+
|
95
|
+
class Put < Net::HTTP::Put
|
96
|
+
include Docusigner::Multipart::Multipartable
|
97
|
+
end
|
98
|
+
|
99
|
+
module Resource
|
100
|
+
def add_document(file, document_id)
|
101
|
+
@documents ||= []
|
102
|
+
@documents << [file, {:document_id => document_id}]
|
103
|
+
end
|
104
|
+
|
105
|
+
def encode
|
106
|
+
if documents.present?
|
107
|
+
[super, *documents]
|
108
|
+
else
|
109
|
+
super
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
def documents
|
116
|
+
@documents ||= []
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Docusigner
|
2
|
+
class Oauth2 < Docusigner::Base
|
3
|
+
singleton
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def token(username, password, integrator_key)
|
7
|
+
@headers = {
|
8
|
+
"Accept" => "application/json",
|
9
|
+
"Content-Type" => "application/x-www-form-urlencoded"
|
10
|
+
}
|
11
|
+
body = "grant_type=password&client_id=#{integrator_key}&username=#{username}&password=#{password}&scope=api"
|
12
|
+
resp = post(:token, {}, body)
|
13
|
+
format.decode(resp.body)["access_token"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def revoke(token)
|
17
|
+
@headers = {
|
18
|
+
"Authorization" => "Bearer #{token}"
|
19
|
+
}
|
20
|
+
post(:revoke, {}, "")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Docusigner
|
2
|
+
class Recipient < Docusigner::Base
|
3
|
+
module Status
|
4
|
+
CREATED = "created"
|
5
|
+
SENT = "sent"
|
6
|
+
DELIVERED = "delivered"
|
7
|
+
SIGNED = "signed"
|
8
|
+
DECLINED = "declined"
|
9
|
+
COMPLETED = "completed"
|
10
|
+
FAX_PENDING = "faxpending"
|
11
|
+
AUTORESPONDED = "autoresponded"
|
12
|
+
end
|
13
|
+
|
14
|
+
belongs_to :envelope
|
15
|
+
has_many :tabs
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Docusigner
|
2
|
+
class Settings < Docusigner::Base
|
3
|
+
singleton
|
4
|
+
|
5
|
+
belongs_to :account
|
6
|
+
|
7
|
+
def [](setting)
|
8
|
+
as = accountSettings.detect{|as| as.name == setting}
|
9
|
+
as ? as.attributes["value"] : nil
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def instantiate_record(record, prefix_options)
|
14
|
+
super({
|
15
|
+
:accountSettings => record,
|
16
|
+
:account_id => prefix_options[:account_id]
|
17
|
+
}, prefix_options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Docusigner
|
2
|
+
class User < Docusigner::Base
|
3
|
+
belongs_to :account
|
4
|
+
|
5
|
+
# DocuSign does not permit this endpoint
|
6
|
+
def update
|
7
|
+
raise "Not permitted"
|
8
|
+
end
|
9
|
+
|
10
|
+
# the create endpoint requires attributes to be nested under newUsers
|
11
|
+
def as_json
|
12
|
+
{ "newUsers" => [super] }
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def id_from_response(response)
|
18
|
+
json = JSON.parse(response.body)
|
19
|
+
json["newUsers"].first["userId"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def load(attributes, remove_root = false)
|
23
|
+
if attributes.is_a?(Array)
|
24
|
+
attributes = attributes.first
|
25
|
+
end
|
26
|
+
super(attributes, remove_root)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/docusigner.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'docusigner/base'
|
2
|
+
module Docusigner
|
3
|
+
# extensions
|
4
|
+
autoload :Connection, "docusigner/connection"
|
5
|
+
autoload :Multipart, "docusigner/multipart"
|
6
|
+
|
7
|
+
# REST models
|
8
|
+
autoload :Account, "docusigner/account"
|
9
|
+
autoload :BillingPlan, "docusigner/billing_plan"
|
10
|
+
autoload :Brand, "docusigner/brand"
|
11
|
+
autoload :CustomField, "docusigner/custom_field"
|
12
|
+
autoload :Document, "docusigner/document"
|
13
|
+
autoload :Envelope, "docusigner/envelope"
|
14
|
+
autoload :Folder, "docusigner/folder"
|
15
|
+
autoload :Group, "docusigner/group"
|
16
|
+
autoload :LoginInformation, "docusigner/login_information"
|
17
|
+
autoload :Recipient, "docusigner/recipient"
|
18
|
+
autoload :Settings, "docusigner/settings"
|
19
|
+
autoload :Tab, "docusigner/tab"
|
20
|
+
autoload :Template, "docusigner/template"
|
21
|
+
autoload :User, "docusigner/user"
|
22
|
+
|
23
|
+
# other models
|
24
|
+
autoload :Oauth2, "docusigner/oauth2"
|
25
|
+
end
|
data/test/test.pdf
ADDED
Binary file
|