hellosign-ruby-sdk 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +15 -0
  4. data/Gemfile +13 -0
  5. data/Guardfile +14 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +58 -0
  8. data/Rakefile +16 -0
  9. data/hellosign-ruby-sdk.gemspec +27 -0
  10. data/lib/hello_sign.rb +32 -0
  11. data/lib/hello_sign/api.rb +7 -0
  12. data/lib/hello_sign/api/account.rb +69 -0
  13. data/lib/hello_sign/api/embedded.rb +27 -0
  14. data/lib/hello_sign/api/oauth.rb +63 -0
  15. data/lib/hello_sign/api/signature_request.rb +286 -0
  16. data/lib/hello_sign/api/team.rb +88 -0
  17. data/lib/hello_sign/api/template.rb +77 -0
  18. data/lib/hello_sign/api/unclaimed_draft.rb +127 -0
  19. data/lib/hello_sign/client.rb +211 -0
  20. data/lib/hello_sign/configuration.rb +56 -0
  21. data/lib/hello_sign/error.rb +48 -0
  22. data/lib/hello_sign/resource.rb +8 -0
  23. data/lib/hello_sign/resource/account.rb +24 -0
  24. data/lib/hello_sign/resource/base_resource.rb +60 -0
  25. data/lib/hello_sign/resource/embedded.rb +24 -0
  26. data/lib/hello_sign/resource/resource_array.rb +33 -0
  27. data/lib/hello_sign/resource/signature_request.rb +25 -0
  28. data/lib/hello_sign/resource/team.rb +24 -0
  29. data/lib/hello_sign/resource/template.rb +25 -0
  30. data/lib/hello_sign/resource/unclaimed_draft.rb +25 -0
  31. data/lib/hello_sign/version.rb +3 -0
  32. data/spec/fixtures/account.json +1 -0
  33. data/spec/fixtures/embedded.json +6 -0
  34. data/spec/fixtures/error.json +6 -0
  35. data/spec/fixtures/file.json +0 -0
  36. data/spec/fixtures/signature_request.json +2 -0
  37. data/spec/fixtures/signature_requests.json +14 -0
  38. data/spec/fixtures/team.json +19 -0
  39. data/spec/fixtures/template.json +1 -0
  40. data/spec/fixtures/templates.json +11 -0
  41. data/spec/fixtures/token.json +14 -0
  42. data/spec/fixtures/unclaimed_draft.json +6 -0
  43. data/spec/hello_sign/api/account_spec.rb +36 -0
  44. data/spec/hello_sign/api/embedded_spec.rb +19 -0
  45. data/spec/hello_sign/api/oauth_spec.rb +28 -0
  46. data/spec/hello_sign/api/signature_request_spec.rb +126 -0
  47. data/spec/hello_sign/api/team_spec.rb +94 -0
  48. data/spec/hello_sign/api/template_spec.rb +67 -0
  49. data/spec/hello_sign/api/unclaimed_draft_spec.rb +35 -0
  50. data/spec/hello_sign/client_spec.rb +132 -0
  51. data/spec/hello_sign/resource/base_resource_spec.rb +50 -0
  52. data/spec/hello_sign_spec.rb +35 -0
  53. data/spec/spec_helper.rb +78 -0
  54. metadata +216 -0
@@ -0,0 +1,88 @@
1
+ module HelloSign
2
+ module Api
3
+
4
+ #
5
+ # Contains all the api calls for the Team resource.
6
+ # Take a look at our {https://www.hellosign.com/api/reference#Team team api document}
7
+ # for more information about this.
8
+ #
9
+ # @author [hellosign]
10
+ #
11
+ module Team
12
+
13
+ #
14
+ # Returns information about your Team as well as a list of its members.
15
+ # If you do not belong to a Team, HelloSign::Error::NotFound will be raised
16
+ #
17
+ # @return [HelloSign::Resource::Team] your current Team
18
+ #
19
+ # @example
20
+ # team = @client.get_team
21
+ def get_team
22
+ HelloSign::Resource::Team.new get("/team")
23
+ end
24
+
25
+ #
26
+ # Creates a new Team and makes you a member. You must not currently belong to a Team to invoke.
27
+ # @option opts [String] name The name of your Team
28
+ #
29
+ # @return [HelloSign::Resource::Team] new created Team
30
+ #
31
+ # @example
32
+ # team = @client.create_team :name => 'Team America World Police'
33
+ def create_team opts
34
+ HelloSign::Resource::Team.new post("/team/create", :body => opts)
35
+ end
36
+
37
+ #
38
+ # Updates the name of your Team.
39
+ # @option opts [String] name The name of your Team
40
+ #
41
+ # @return [HelloSign::Resource::Team] a Team object
42
+ #
43
+ # @example
44
+ # team = @client.update_team :name => 'New Team Name'
45
+ def update_team opts
46
+ HelloSign::Resource::Team.new post("/team", :body => opts)
47
+ end
48
+
49
+ #
50
+ # Deletes your Team. Can only be invoked when you have a Team with only one member (yourself).
51
+ #
52
+ # @example
53
+ # team = @client.destroy_team
54
+ def destroy_team
55
+ post("/team/destroy")
56
+ end
57
+
58
+ #
59
+ # Adds or invites a user (specified using the email_address parameter) to your Team.
60
+ # If the user does not currently have a HelloSign Account, a new one will be created for them.
61
+ # If the user currently has a paid subscription, they will not automatically join the Team but instead will be sent an invitation to join.
62
+ # If a user is already a part of another Team, a "team_invite_failed" error will be returned.
63
+ # @option opts [String] account_id The id of the Account of the user to invite to your Team. The account id prevails if both email_address and acccount_id are provided.
64
+ # @option opts [String] email_address The email address of the Account of the user to invite to your Team. The account id prevails if both email_address and acccount_id are provided.
65
+ #
66
+ # @return [HelloSign::Resource::Team] updated Team object
67
+ #
68
+ # @example
69
+ # team = @client.add_member_to_team :email_address => 'george@example.com'
70
+ def add_member_to_team opts
71
+ HelloSign::Resource::Team.new post("/team/add_member", :body => opts)
72
+ end
73
+
74
+
75
+ #
76
+ # Removes a user from your Team. If the user had an outstanding invitation to your Team the invitation will be expired.
77
+ # @option opts [String] account_id The id of the Account of the user to invite to your Team. The account id prevails if both email_address and acccount_id are provided.
78
+ # @option opts [String] email_address The email address of the Account of the user to invite to your Team. The account id prevails if both email_address and acccount_id are provided.
79
+ #
80
+ # @return [HelloSign::Resource::Team] updated Team object
81
+ # @example
82
+ # team = @client.remove_member_from_team :email_address => 'george@example.com'
83
+ def remove_member_from_team opts
84
+ HelloSign::Resource::Team.new post("/team/remove_member", :body => opts)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,77 @@
1
+ module HelloSign
2
+ module Api
3
+
4
+ #
5
+ # Contains all the api calls for the Template resource.
6
+ # Take a look at our {https://www.hellosign.com/api/templatesAndApiWalkthrough template form api document}
7
+ # for more information about this.
8
+ #
9
+ # @author [hellosign]
10
+ #
11
+ module Template
12
+
13
+ #
14
+ # Retrieves the Template specified by the id parameter.
15
+ # @option opts [String] template_id The id of the Template to retrieve.
16
+ #
17
+ # @return [HelloSign::Resource::Template] a Template object
18
+ #
19
+ # @example
20
+ # template = @client.get_template :template_id => 'f57db65d3f933b5316d398057a36176831451a35'
21
+ #
22
+ def get_template opts
23
+ HelloSign::Resource::Template.new get("/template/#{opts[:template_id]}")
24
+ end
25
+
26
+ #
27
+ # Retrieves the Templates for the current user account.
28
+ #
29
+ # @option opts [Integer] page (1) Which page number of the Template List to return.
30
+ #
31
+ # @return [HelloSign::Resource::ResourceArray] a ResourceArray object
32
+ # @example
33
+ # templates = @client.get_templates :page => 1
34
+ #
35
+ def get_templates opts={}
36
+ path = '/template/list'
37
+ path += opts[:page] ? "?page=#{opts[:page]}" : ""
38
+ HelloSign::Resource::ResourceArray.new get(path, opts), "templates", HelloSign::Resource::Template
39
+ end
40
+
41
+
42
+ #
43
+ # Gives the specified Account on your team access to a Template
44
+ #
45
+ # @option opts [String] template_id The id of the Template to give access to.
46
+ # @option opts [String] account_id The id of the Account to get access. The account_id prevails if account_id and email_address are both provided.
47
+ # @option opts [String] email_address The email address of the Account to give access to the Template. The account_id prevails if account_id and email_address are both provided.
48
+ #
49
+ # @return [Template] a Template object
50
+ # @example
51
+ # templates = @client.add_user_to_template :template_id => 'f57db65d3f933b5316d398057a36176831451a35', :email_address => 'george@example.com'
52
+ #
53
+ def add_user_to_template opts
54
+ path = "/template/add_user/#{opts[:template_id]}"
55
+ opts.delete(:template_id)
56
+ HelloSign::Resource::Template.new post(path, :body => opts)
57
+ end
58
+
59
+ #
60
+ # Removes the specified Account's access to the specified Template.
61
+ # The user can be designated using their account ID or email address.
62
+ # @option opts [String] template_id The id of the Template to remove the Account access to.
63
+ # @option opts [String] account_id The id of the Account to remove access to the Template. The account_id prevails if account_id and email_address are both provided.
64
+ # @option opts [String] email_address The email address of the Account to remove access to the Template. The account_id prevails if account_id and email_address are both provided.
65
+ #
66
+ # @return [Template] a Template object
67
+ # @example
68
+ # templates = @client.remove_user_from_template :template_id => 'f57db65d3f933b5316d398057a36176831451a35', :email_address => 'george@example.com'
69
+ #
70
+ def remove_user_from_template opts
71
+ path = "/template/remove_user/#{opts[:template_id]}"
72
+ opts.delete(:template_id)
73
+ HelloSign::Resource::Template.new post(path, :body => opts)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,127 @@
1
+ module HelloSign
2
+ module Api
3
+
4
+ #
5
+ # Contains all the api calls for the UnclaimedDraft resource.
6
+ # Take a look at our {https://www.hellosign.com/api/reference#UnclaimedDraft unclaimed draft document}
7
+ # for more information about this.
8
+ #
9
+ # @author [hellosign]
10
+ #
11
+ module UnclaimedDraft
12
+
13
+ #
14
+ # Creates a new Draft that can be claimed using the claim URL.
15
+ # The first authenticated user to access the URL will claim the Draft and will be shown either the "Sign and send" or the "Request signature" page with the Draft loaded.
16
+ # Subsequent access to the claim URL will result in a 404 not found.
17
+ # If the type is "send_document" then only the file parameter is required.
18
+ # If the type is "request_signature", then the identities of the signers and optionally
19
+ # the location of signing elements on the page are also required.
20
+ # @option opts [Integer] test_mode (0) Whether this is a test, the signature request will not be legally binding if set to 1.
21
+ # @option opts [Array<String>] files Use files to indicate the uploaded file(s) to send for signature. Currently we only support use of either the files parameter or file_urls parameter, not both.
22
+ # @option opts [Array<String>] file_urls Use file_urls to have HelloSign download the file(s) to send for signature. Currently we only support use of either the files parameter or file_urls parameter, not both.
23
+ # @option opts [String] type The type of unclaimed draft to create. Use "send_document" to create a claimable file, and "request_signature" for a claimable signature request. If the type is "request_signature" then signers name and email_address are not optional.
24
+ # @option opts [String] subject The subject in the email that will be sent to the signers.
25
+ # @option opts [String] message The custom message in the email that will be sent to the signers.
26
+ # @option opts [String] signing_redirect_url The URL you want the signer redirected to after they successfully sign. (optional)
27
+ #
28
+ # @option opts [Array<Hash>] signers List of signers, each item is a Hash with these keys:
29
+ # * :name (String) Sender' name
30
+ # * :email_address (String) Sender's email address
31
+ # * :order (Integer) The order the signer is required to sign in (optional)
32
+ # * :pin (Integer) The 4-digit code that will secure this signer's signature page. You must have a business plan to use this feature. (optional)
33
+ # @option opts [Array<String>] cc_email_addresses The email addresses that should be CCed.
34
+ # @option opts [String] form_fields_per_document
35
+ #
36
+ # @return [HelloSign::Resource::UnclaimedDraft] a UnclaimedDraft object
37
+ #
38
+ # @example send_document
39
+ # unclaimed_draft = @client.create_unclaimed_draft(
40
+ # :test_mode => 1,
41
+ # :files => ['NDA.pdf', 'AppendixA.pdf']
42
+ # )
43
+ # @example request_signature
44
+ # unclaimed_draft = @client.create_unclaimed_draft(
45
+ # :test_mode => 1,
46
+ # :type => 'request_signature',
47
+ # :subject => 'The NDA we talked about',
48
+ # :message => 'Please sign this NDA and then we can discuss more. Let me know if you have any questions.',
49
+ # :signers => [{
50
+ # :email_address => 'jack@example.com',
51
+ # :name => 'Jack',
52
+ # :order => 0
53
+ # },{
54
+ # :email_address => 'jill@example.com',
55
+ # :name => 'Jill',
56
+ # :order => 1
57
+ # }
58
+ # ],
59
+ # :cc_email_addresses => ['lawyer@example.com', 'lawyer@example2.com'],
60
+ # :files => ['NDA.pdf', 'AppendixA.pdf']
61
+ # )
62
+ #
63
+ def create_unclaimed_draft opts
64
+ prepare_files opts
65
+ if opts[:type] == 'request_signature'
66
+ prepare_signers opts
67
+ end
68
+
69
+ HelloSign::Resource::UnclaimedDraft.new post('/unclaimed_draft/create', :body => opts)
70
+ end
71
+
72
+
73
+ #
74
+ # Creates a new embedded unclaimed draft object that can be launched in an iframe using the claim URL.
75
+ # @option opts [Integer] test_mode (0) Whether this is a test, the signature request will not be legally binding if set to 1.
76
+ # @option opts [Integer] is_for_embedded_signing set this to 1 to to mix embedded requesting and embedded signing.
77
+ # @option opts [Array<String>] files Use files to indicate the uploaded file(s) to send for signature. Currently we only support use of either the files parameter or file_urls parameter, not both.
78
+ # @option opts [Array<String>] file_urls Use file_urls to have HelloSign download the file(s) to send for signature. Currently we only support use of either the files parameter or file_urls parameter, not both.
79
+ # @option opts [String] type The type of unclaimed draft to create. Should be "request_signature" for a claimable signature request. If the type is "request_signature" then signers name and email_address are not optional.
80
+ # @option opts [String] subject The subject in the email that will be sent to the signers.
81
+ # @option opts [String] requester_email_address The email address of the requester.
82
+ # @option opts [String] message The custom message in the email that will be sent to the signers.
83
+ # @option opts [String] signing_redirect_url The URL you want the signer redirected to after they successfully sign. (optional)
84
+ #
85
+ # @option opts [Array<Hash>] signers List of signers, each item is a Hash with these keys:
86
+ # * :name (String) Sender' name
87
+ # * :email_address (String) Sender's email address
88
+ # * :order (Integer) The order the signer is required to sign in (optional)
89
+ # * :pin (Integer) The 4-digit code that will secure this signer's signature page. You must have a business plan to use this feature. (optional)
90
+ # @option opts [Array<String>] cc_email_addresses The email addresses that should be CCed.
91
+ #
92
+ # @return [HelloSign::Resource::UnclaimedDraft] a UnclaimedDraft object
93
+
94
+ # @example request_signature
95
+ # unclaimed_draft = @client.create_embedded_unclaimed_draft(
96
+ # :test_mode => 1,
97
+ # :type => 'request_signature',
98
+ # :subject => 'The NDA we talked about',
99
+ # :requester_email_address => requester@example.com",
100
+ # :message => 'Please sign this NDA and then we can discuss more. Let me know if you have any questions.',
101
+ # :signers => [{
102
+ # :email_address => 'jack@example.com',
103
+ # :name => 'Jack',
104
+ # :order => 0
105
+ # },{
106
+ # :email_address => 'jill@example.com',
107
+ # :name => 'Jill',
108
+ # :order => 1
109
+ # }
110
+ # ],
111
+ # :cc_email_addresses => ['lawyer@example.com', 'lawyer@example2.com'],
112
+ # :files => ['NDA.pdf', 'AppendixA.pdf']
113
+ # )
114
+ #
115
+ def create_embedded_unclaimed_draft opts
116
+ opts[:client_id] ||= self.client_id
117
+ prepare_files opts
118
+ if opts[:type] == 'request_signature'
119
+ prepare_signers opts
120
+ end
121
+
122
+ HelloSign::Resource::UnclaimedDraft.new post('/unclaimed_draft/create_embedded', :body => opts)
123
+ end
124
+
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,211 @@
1
+ require 'faraday'
2
+ require 'multi_json'
3
+ require 'hello_sign/error'
4
+ require 'hello_sign/configuration'
5
+ require 'hello_sign/resource'
6
+ require 'hello_sign/api'
7
+ require 'logger'
8
+
9
+ module HelloSign
10
+
11
+ #
12
+ # You'll need the HelloSign::Client to do just about everything, from creating
13
+ # signatures to updating account information.
14
+ #
15
+ # @example
16
+ # client = HelloSign::Client.new :email_address => "me@hellosign.com", :password => "mypassword"
17
+ #
18
+ # @author [hellosign]
19
+ class Client
20
+ include Api::Account
21
+ include Api::SignatureRequest
22
+ include Api::Template
23
+ include Api::Team
24
+ include Api::UnclaimedDraft
25
+ include Api::Embedded
26
+ include Api::OAuth
27
+
28
+ attr_accessor :end_point, :oauth_end_point, :api_version, :user_agent, :client_id, :client_secret, :email_address, :password, :api_key, :auth_token, :logging, :log_level
29
+
30
+ ERRORS = {
31
+ 400 => Error::BadRequest,
32
+ 401 => Error::Unauthorized,
33
+ 403 => Error::Forbidden,
34
+ 404 => Error::NotFound,
35
+ 405 => Error::MethodNotAllowed,
36
+ 409 => Error::Conflict,
37
+ 410 => Error::Gone,
38
+ 500 => Error::InternalServerError,
39
+ 502 => Error::BadGateway,
40
+ 503 => Error::ServiceUnavailable
41
+ }
42
+
43
+ #
44
+ # Creates a new HelloSign Client
45
+
46
+ # @option opts [String] email_address email address
47
+ # @option opts [String] password password
48
+ # @option opts [String] api_key Api key
49
+ # @return [HelloSign::Client] a new HelloSign::Client
50
+ def initialize(opts={})
51
+ options = HelloSign.options.merge(opts)
52
+ HelloSign::Configuration::VALID_OPTIONS_KEYS.each do |key|
53
+ self.send("#{key}=", options[key])
54
+ end
55
+ end
56
+
57
+ #
58
+ # Make a http get request
59
+ # @param path [String] relative path of the request
60
+ # @option options [Hash] params params of the url
61
+ #
62
+ def get(path, options={})
63
+ response = request(path, :get, options)
64
+ validate response
65
+ parse response
66
+ end
67
+
68
+ #
69
+ # Make a http post request
70
+ # @param path [String] relative path of the request
71
+ # @option options [Hash] params params of the url
72
+ # @option options [Hash] body request body
73
+ #
74
+ def post(path, options={})
75
+ response = request(path, :post, options)
76
+ validate response
77
+ parse response
78
+ end
79
+
80
+ #
81
+ # Make a http put request
82
+ # @param path [String] relative path of the request
83
+ # @option options [Hash] params params of the url
84
+ # @option options [Hash] body request body
85
+ #
86
+ def put(path, options={})
87
+ response = request(path, :put, options)
88
+ validate response
89
+ parse response
90
+ end
91
+
92
+ #
93
+ # Make a http delete request
94
+ # @param path [String] relative path of the request
95
+ # @option options [Hash] params params of the url
96
+ #
97
+ def delete(path, options={})
98
+ response = request(path, :delete, options)
99
+ validate response
100
+ parse response
101
+ end
102
+
103
+ private
104
+
105
+ def request(path, method, options={})
106
+ make_connection(options).send method do |request|
107
+ if options[:oauth_request]
108
+ request.url "#{path}", options[:params]
109
+ else
110
+ request.url "#{api_version}#{path}", options[:params]
111
+ end
112
+ request.body = options[:body]
113
+ end
114
+ end
115
+
116
+ def make_connection options
117
+ url = options[:oauth_request] ? oauth_end_point : end_point
118
+
119
+ if @logging
120
+ logger = Logger.new(STDOUT)
121
+ logger.level = @log_level
122
+ end
123
+
124
+ connection = Faraday.new(:url => url, :headers => {user_agent: user_agent}) do |faraday|
125
+ faraday.request :multipart
126
+ faraday.request :url_encoded
127
+ faraday.response :logger, logger if @logging
128
+ faraday.adapter :net_http
129
+ end
130
+ if options[:no_auth]
131
+ elsif auth_token
132
+ connection.authorization :Bearer, auth_token
133
+ elsif api_key
134
+ connection.basic_auth api_key, ''
135
+ elsif email_address
136
+ connection.basic_auth email_address, password
137
+ else
138
+ end
139
+ connection
140
+ end
141
+
142
+ def validate(response)
143
+ if response.status >= 400
144
+ raise ERRORS[response.status].new error_message(response)
145
+ end
146
+ end
147
+
148
+ def parse(response)
149
+ if response["content-type"] == "application/pdf"
150
+ response.body
151
+ elsif response.body.strip.empty?
152
+ {}
153
+ else
154
+ MultiJson.load response.body
155
+ end
156
+ end
157
+
158
+ def error_message(response)
159
+ "Server responded with code #{response.status}\n" \
160
+ "Request URI: #{response.to_hash[:url].to_s}\n"\
161
+ "Message: #{response.body}"
162
+ end
163
+
164
+ def prepare_files opts
165
+ if opts[:files]
166
+ opts[:files].each_with_index do |file, index|
167
+ if file.is_a? String
168
+ opts[:"file[#{index}]"] = Faraday::UploadIO.new(file, 'application/pdf')
169
+ elsif defined? ActionDispatch::Http::UploadedFile
170
+ if file.is_a? ActionDispatch::Http::UploadedFile
171
+ opts[:"file[#{index}]"] = UploadIO.new(file.tempfile, 'application/pdf')
172
+ end
173
+ else
174
+ raise HelloSign::Error::NotSupportedType.new "#{file.class} is not a supported. Must be a string or ActionDispatch::Http::UploadedFile"
175
+ end
176
+ end
177
+ opts.delete(:files)
178
+ elsif opts[:file_urls]
179
+ opts[:file_urls].each_with_index do |file, index|
180
+ opts[:"file_url[#{index}]"] = file
181
+ end
182
+ opts.delete(:file_urls)
183
+ end
184
+ end
185
+
186
+ def prepare_signers opts
187
+ prepare opts, :signers
188
+ end
189
+
190
+ def prepare_ccs opts
191
+ prepare opts, :ccs
192
+ end
193
+
194
+ def prepare opts, key
195
+ return unless opts[key]
196
+ opts[key].each_with_index do |value, index|
197
+ if value.is_a? String
198
+ opts[:"#{key}[#{index}]"] = value
199
+ else
200
+ if value[:role]
201
+ opts[:"#{key}[#{value[:role]}]"] = value
202
+ value.delete(:role)
203
+ else
204
+ opts[:"#{key}[#{index}]"] = value
205
+ end
206
+ end
207
+ end
208
+ opts.delete(key)
209
+ end
210
+ end
211
+ end