signnow-ruby 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ module Signnow
2
+ module Request
3
+ class Connection
4
+ include Helpers
5
+ attr_reader :https
6
+
7
+ def initialize(request_info)
8
+ @info = request_info
9
+ end
10
+
11
+ def setup_https
12
+ @https = Net::HTTP.new(api_url, Net::HTTP.https_default_port)
13
+ @https.use_ssl = true
14
+ @https.verify_mode = OpenSSL::SSL::VERIFY_NONE
15
+ end
16
+
17
+ def request
18
+ https.start do |connection|
19
+ https.request(https_request)
20
+ end
21
+ end
22
+
23
+ protected
24
+
25
+ def authentication
26
+ return {} unless @info.authentication[:type]
27
+ case @info.authentication[:type]
28
+ when :basic
29
+ raise AuthenticationError unless Signnow.encoded_app_credentials
30
+ {'Authorization' => "Basic #{Signnow.encoded_app_credentials}"}
31
+ when :user_token
32
+ raise AuthenticationError unless @info.authentication[:token]
33
+ {'Authorization' => "Bearer #{@info.authentication[:token]}"}
34
+ else
35
+ {}
36
+ end
37
+ end
38
+
39
+ def https_request
40
+ https_request = case @info.http_method
41
+ when :post
42
+ Net::HTTP::Post.new(@info.url, authentication)
43
+ when :put
44
+ Net::HTTP::Put.new(@info.url, authentication)
45
+ when :delete
46
+ Net::HTTP::Delete.new(@info.url, authentication)
47
+ else
48
+ Net::HTTP::Get.new(@info.path_with_params(@info.url, @info.data), authentication)
49
+ end
50
+
51
+ if [:post, :put].include?(@info.http_method)
52
+ if @info.use_form_data?
53
+ https_request.set_form_data(@info.data)
54
+ else
55
+ https_request.body = JSON.generate(normalize_params(@info.data))
56
+ end
57
+ end
58
+
59
+ https_request
60
+ end
61
+
62
+ # Returns the api url foir this request or default
63
+ def api_url
64
+ domain + '.' + API_BASE
65
+ end
66
+
67
+ # Returns the domain for the current request or the default one
68
+ def domain
69
+ return @info.subdomain if @info
70
+ DOMAIN_BASE
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,36 @@
1
+ module Signnow
2
+ module Request
3
+ module Helpers
4
+ def flatten_hash_keys(old_hash, new_hash={}, keys=nil)
5
+ old_hash.each do |key, value|
6
+ key = key.to_s
7
+ if value.is_a?(Hash)
8
+ all_keys_formatted = keys + "[#{key}]"
9
+ flatten_hash_keys(value, new_hash, all_keys_formatted)
10
+ else
11
+ new_hash[key] = value
12
+ end
13
+ end
14
+ new_hash
15
+ end
16
+
17
+ def normalize_params(params, key=nil)
18
+ params = flatten_hash_keys(params) if params.is_a?(Hash)
19
+ result = {}
20
+ params.each do |key, value|
21
+ case value
22
+ when Hash
23
+ result[key.to_s] = normalize_params(value)
24
+ when Array
25
+ value.each_with_index do |item_value, index|
26
+ result["#{key.to_s}[#{index}]"] = item_value.to_s
27
+ end
28
+ else
29
+ result[key.to_s] = value.to_s
30
+ end
31
+ end
32
+ result
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,50 @@
1
+ module Signnow
2
+ module Request
3
+ class Info
4
+ attr_accessor :http_method, :api_url, :data, :subdomain, :authentication, :base_path, :options
5
+
6
+ def initialize(http_method, subdomain, api_url, data, options={})
7
+ @http_method = http_method
8
+ @subdomain = subdomain || Signnow.domain
9
+ @api_url = api_url
10
+ @data = data
11
+ @base_path = Signnow.base_path
12
+ @authentication = {}
13
+ @authentication[:type] = options.delete(:auth_type)
14
+ @authentication[:token] = options.delete(:auth_token)
15
+ @options = options
16
+ end
17
+
18
+ def url
19
+ url = ''
20
+ url += "/#{base_path}" if base_path
21
+ url += "/#{api_url}"
22
+ if has_id?
23
+ url += "/#{data[:id]}"
24
+ data.delete(:id)
25
+ end
26
+
27
+ url
28
+ end
29
+
30
+ def path_with_params(path, params)
31
+ unless params.empty?
32
+ encoded_params = URI.encode_www_form(params)
33
+ [path, encoded_params].join("?")
34
+ else
35
+ path
36
+ end
37
+ end
38
+
39
+ def use_form_data?
40
+ options.fetch(:use_form_data, false)
41
+ end
42
+
43
+ protected
44
+
45
+ def has_id?
46
+ !data[:id].nil?
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,52 @@
1
+ module Signnow
2
+ module Request
3
+ class Validator
4
+ attr_reader :info
5
+ attr_accessor :response
6
+
7
+ def initialize(info)
8
+ @info = info
9
+ end
10
+
11
+ def validated_data_for(incoming_response)
12
+ self.response = incoming_response
13
+ verify_response_code
14
+ info.data = JSON.parse(response.body)
15
+ validate_response_data
16
+ info.data
17
+ end
18
+
19
+ protected
20
+
21
+ def verify_response_code
22
+ raise AuthenticationError if response.code.to_i == 401
23
+ raise NotFound if response.code.to_i == 404
24
+ raise APIError if response.code.to_i >= 500
25
+ end
26
+
27
+ def validate_response_data(body=nil)
28
+ body ||= info.data
29
+ if body.is_a?(Hash)
30
+ if body['404']
31
+ fail NotFound.new(body['404'])
32
+ elsif body['error']
33
+ handle_api_error(body['code'], body['error'])
34
+ elsif body['errors']
35
+ body['errors'].each do |error|
36
+ handle_api_error(error['code'], error['message'])
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def handle_api_error(code, message)
43
+ error = case code
44
+ when 1539 then InvalidToken.new(message)
45
+ when 65536 then EmptyDocuments.new(message)
46
+ else APIError.new(message)
47
+ end
48
+ fail error
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,31 @@
1
+ module Signnow
2
+ class User < Base
3
+ include Signnow::Operations::Show
4
+
5
+ attr_accessor :id, :email, :first_name, :last_name, :attributes, :active,
6
+ :type, :pro, :created, :emails, :identity, :subscriptions, :credits,
7
+ :has_atticus_access, :is_logged_in, :teams
8
+
9
+ # Initializes the object using the given attributes
10
+ #
11
+ # @param [Hash] attributes The attributes to use for initialization
12
+ def initialize(attributes = {})
13
+ super(attributes)
14
+ parse_booleans
15
+ end
16
+
17
+ def email
18
+ return @email if @email
19
+ return unless emails
20
+ emails.first
21
+ end
22
+
23
+ # Parses Boolean fileds.
24
+ def parse_booleans
25
+ @active = ( active == '1' ) if active and active.is_a? String
26
+ @pro = ( pro == 1 ) if pro and pro.is_a? Integer
27
+ end
28
+ protected :parse_booleans
29
+
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Signnow
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "signnow/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "signnow-ruby"
7
+ s.version = Signnow::VERSION
8
+ s.authors = ["Andres Bravo"]
9
+ s.email = ["hola@andresbravo.com"]
10
+ s.homepage = "https://github.com/andresbravog/signnow-ruby"
11
+ s.summary = %q{API wrapper for Signnow.}
12
+ s.description = %q{API wrapper for Signnow.}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_dependency "json"
20
+ s.add_development_dependency "rspec"
21
+ s.add_development_dependency "pry"
22
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+
3
+ describe Signnow::Base do
4
+ describe "#parse_timestamps" do
5
+ context "given #created is present" do
6
+ it "creates a Time object" do
7
+ base = Signnow::Base.new(created: 1358300444)
8
+ base.created.class.should eql(Time)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,53 @@
1
+ require "spec_helper"
2
+
3
+ describe Signnow::Client do
4
+ let(:valid_attributes) do
5
+ JSON.parse %{
6
+ {
7
+ "id": "23f2f9dc10e0f12883f78e91296207640dede6d1",
8
+ "first_name": "Test",
9
+ "last_name": "User",
10
+ "active": "1",
11
+ "type": 1,
12
+ "pro": 0,
13
+ "created": "1358945328",
14
+ "emails": ["hola+test@andresbravo.com"],
15
+ "identity": {
16
+ "Identified": "No",
17
+ "Status": "First attempt",
18
+ "OKToRetry": true
19
+ },
20
+ "subscriptions": [],
21
+ "credits": 0,
22
+ "has_atticus_access": false,
23
+ "is_logged_in": true,
24
+ "teams": []
25
+ }
26
+ }
27
+ end
28
+
29
+ let (:client) do
30
+ Signnow::Client.new(user_access_token)
31
+ end
32
+
33
+ let(:user_access_token) { '_user_access_token_' }
34
+
35
+ describe "#initialize" do
36
+ it 'initializes attributes correctly' do
37
+ client.access_token.should eql(user_access_token)
38
+ end
39
+ end
40
+
41
+ describe "#perform!" do
42
+ before :each do
43
+ allow(Signnow).to receive(:request).and_return(valid_attributes)
44
+ end
45
+ it "executes requests inside the block" do
46
+ expect(Signnow).to receive(:request).with(:get, nil, "user", {}, { auth_type: :user_token, auth_token: user_access_token })
47
+ client.perform! do |token|
48
+ Signnow::User.show(access_token: token)
49
+ end
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,188 @@
1
+ require "spec_helper"
2
+
3
+ describe Signnow::Document do
4
+ let(:valid_attributes) do
5
+ JSON.parse %{
6
+ {
7
+ "id": "9e30bb3094e00abc291016a7a597ba1840a6d6ec",
8
+ "user_id": "adb16da39e5ecc448e2aa4aec8a34a8158fa137a",
9
+ "document_name": "sample.pdf",
10
+ "page_count": "1",
11
+ "created": "1358300444",
12
+ "updated": "1358300444",
13
+ "original_filename": "sample.pdf",
14
+ "thumbnail": {
15
+ "small": "https:\/\/api.signnow.com\/document\/9e30bb3094e00abc291016a7a597ba1840a6d6ec\/thumbnail?size=small",
16
+ "medium": "https:\/\/api.signnow.com\/document\/9e30bb3094e00abc291016a7a597ba1840a6d6ec\/thumbnail?size=medium",
17
+ "large": "https:\/\/api.signnow.com\/document\/9e30bb3094e00abc291016a7a597ba1840a6d6ec\/thumbnail?size=large"
18
+ },
19
+ "signatures": [],
20
+ "seals": [],
21
+ "texts": [],
22
+ "inserts": [],
23
+ "tags": [],
24
+ "fields": [],
25
+ "requests": [],
26
+ "notary_invites": [],
27
+ "version_time": "1358300444"
28
+ }
29
+ }
30
+ end
31
+
32
+ let(:valid_all_response) do
33
+ JSON.parse %{
34
+ [{
35
+ "id": "9e30bb3094e00abc291016a7a597ba1840a6d6ec",
36
+ "user_id": "adb16da39e5ecc448e2aa4aec8a34a8158fa137a",
37
+ "document_name": "sample.pdf",
38
+ "page_count": "1",
39
+ "created": "1358300444",
40
+ "updated": "1358300444",
41
+ "original_filename": "sample.pdf",
42
+ "thumbnail": {
43
+ "small": "https:\/\/api.signnow.com\/document\/9e30bb3094e00abc291016a7a597ba1840a6d6ec\/thumbnail?size=small",
44
+ "medium": "https:\/\/api.signnow.com\/document\/9e30bb3094e00abc291016a7a597ba1840a6d6ec\/thumbnail?size=medium",
45
+ "large": "https:\/\/api.signnow.com\/document\/9e30bb3094e00abc291016a7a597ba1840a6d6ec\/thumbnail?size=large"
46
+ },
47
+ "signatures": [],
48
+ "seals": [],
49
+ "texts": [],
50
+ "inserts": [],
51
+ "tags": [],
52
+ "fields": [],
53
+ "requests": [],
54
+ "notary_invites": [],
55
+ "version_time": "1358300444"
56
+ }, {
57
+ "id": "321e1c74ada708f442bf3f9529ed0d44b3628796",
58
+ "user_id": "adb16da39e5ecc448e2aa4aec8a34a8158fa137a",
59
+ "document_name": "sample.pdf",
60
+ "page_count": "1",
61
+ "created": "1341248871",
62
+ "updated": "1341248871",
63
+ "original_filename": "sample.pdf",
64
+ "thumbnail": {
65
+ "small": "https:\/\/api.signnow.com\/document\/321e1c74ada708f442bf3f9529ed0d44b3628796\/thumbnail?size=small",
66
+ "medium": "https:\/\/api.signnow.com\/document\/321e1c74ada708f442bf3f9529ed0d44b3628796\/thumbnail?size=medium",
67
+ "large": "https:\/\/api.signnow.com\/document\/321e1c74ada708f442bf3f9529ed0d44b3628796\/thumbnail?size=large"
68
+ },
69
+ "signatures": [],
70
+ "seals": [],
71
+ "texts": [],
72
+ "inserts": [],
73
+ "tags": [],
74
+ "fields": [],
75
+ "requests": [{
76
+ "unique_id": "1cb9d8e6b26d6ececc4f4c9d08aaaa28e10fa80f",
77
+ "id": "1cb9d8e6b26d6ececc4f4c9d08aaaa28e10fa80f",
78
+ "user_id": "adb16da39e5ecc448e2aa4aec8a34a8158fa137a",
79
+ "created": "1349903957",
80
+ "originator_email": "user@test.com",
81
+ "signer_email": "signer@test.com"
82
+ }],
83
+ "notary_invites": [],
84
+ "version_time": "1341248871"
85
+ }, {
86
+ "id": "1db4c6ba33332f655cb2eda468743c1d040ae079",
87
+ "user_id": "adb16da39e5ecc448e2aa4aec8a34a8158fa137a",
88
+ "document_name": "sample.pdf",
89
+ "page_count": "1",
90
+ "created": "1335819071",
91
+ "updated": "1335819071",
92
+ "original_filename": "sample.pdf",
93
+ "thumbnail": {
94
+ "small": "https:\/\/api.signnow.com\/document\/1db4c6ba33332f655cb2eda468743c1d040ae079\/thumbnail?size=small",
95
+ "medium": "https:\/\/api.signnow.com\/document\/1db4c6ba33332f655cb2eda468743c1d040ae079\/thumbnail?size=medium",
96
+ "large": "https:\/\/api.signnow.com\/document\/1db4c6ba33332f655cb2eda468743c1d040ae079\/thumbnail?size=large"
97
+ },
98
+ "signatures": [],
99
+ "seals": [],
100
+ "texts": [],
101
+ "inserts": [],
102
+ "tags": [],
103
+ "fields": [],
104
+ "requests": [{
105
+ "unique_id": "5d8ce45d8e27c7ed717d7d23117200c72442ee3e",
106
+ "id": "5d8ce45d8e27c7ed717d7d23117200c72442ee3e",
107
+ "user_id": "adb16da39e5ecc448e2aa4aec8a34a8158fa137a",
108
+ "created": "1335819085",
109
+ "originator_email": "jane@test.com",
110
+ "signer_email": "signer@test.com"
111
+ }],
112
+ "notary_invites": [],
113
+ "version_time": "1335819071"
114
+ }]
115
+ }
116
+ end
117
+
118
+ let (:document) do
119
+ Signnow::Document.new(valid_attributes)
120
+ end
121
+
122
+ let(:user_access_token) { '_user_access_token_' }
123
+
124
+ describe "#initialize" do
125
+ it 'initializes all attributes correctly' do
126
+ document.id.should eql('9e30bb3094e00abc291016a7a597ba1840a6d6ec')
127
+ document.user_id.should eql('adb16da39e5ecc448e2aa4aec8a34a8158fa137a')
128
+ document.document_name.should eql('sample.pdf')
129
+ document.page_count.should eql('1')
130
+ document.original_filename.should eql('sample.pdf')
131
+ end
132
+ end
133
+
134
+ describe ".show" do
135
+ let(:document_show) { Signnow::Document.show(access_token: user_access_token, id: document.id ) }
136
+ before :each do
137
+ allow(Signnow).to receive(:request).and_return(valid_attributes)
138
+ end
139
+ it "makes a new GET request using the correct API endpoint to receive a specific user" do
140
+ expect(Signnow).to receive(:request).with(:get, nil, "document/#{document.id}", {}, { auth_type: :user_token, auth_token: user_access_token })
141
+ document_show
142
+ end
143
+ it 'returns a user with the correct id' do
144
+ expect(document_show.id).to eql('9e30bb3094e00abc291016a7a597ba1840a6d6ec')
145
+ end
146
+ it 'returns a user with the correct document_name' do
147
+ expect(document_show.document_name).to eql('sample.pdf')
148
+ end
149
+ it 'returns a user with the correct original_filename' do
150
+ expect(document_show.original_filename).to eql('sample.pdf')
151
+ end
152
+ end
153
+
154
+ describe ".download_link" do
155
+ let(:document_download_link) { Signnow::Document.download_link(access_token: user_access_token, id: document.id ) }
156
+ let(:valid_link_attributes) do
157
+ JSON.parse %{
158
+ {
159
+ "link": "https://signnow.com/dispatch?route=onetimedownload&document_download_id=67de624701a70cdfe208b5c537f61fefa48b410a"
160
+ }
161
+ }
162
+ end
163
+ before :each do
164
+ allow(Signnow).to receive(:request).and_return(valid_link_attributes)
165
+ end
166
+ it "makes a new GET request using the correct API endpoint to receive a specific user" do
167
+ expect(Signnow).to receive(:request).with(:post, nil, "document/#{document.id}/download/link", {}, { auth_type: :user_token, auth_token: user_access_token })
168
+ document_download_link
169
+ end
170
+ it 'returns a user with the correct link' do
171
+ expect(document_download_link).to eql('https://signnow.com/dispatch?route=onetimedownload&document_download_id=67de624701a70cdfe208b5c537f61fefa48b410a')
172
+ end
173
+ end
174
+
175
+ describe ".all" do
176
+ let(:document_all) { Signnow::Document.all(access_token: user_access_token) }
177
+ before :each do
178
+ allow(Signnow).to receive(:request).and_return(valid_all_response)
179
+ end
180
+ it "makes a new POST request using the correct API endpoint" do
181
+ expect(Signnow).to receive(:request).with(:get, nil, "user/documentsv2", {}, { auth_type: :user_token, auth_token: user_access_token })
182
+ document_all
183
+ end
184
+ it 'returns a user with the correct id' do
185
+ expect(document_all.first.id).to eql('9e30bb3094e00abc291016a7a597ba1840a6d6ec')
186
+ end
187
+ end
188
+ end