eligible 2.4.3 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/eligible.gemspec CHANGED
@@ -4,24 +4,23 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'eligible/version'
5
5
 
6
6
  Gem::Specification.new do |gem|
7
- gem.name = "eligible"
7
+ gem.name = 'eligible'
8
8
  gem.version = Eligible::VERSION
9
- gem.authors = ["Katelyn Gleaon", "Rodrigo Dominguez", "Andry Brett"]
10
- gem.email = ["k@eligibleapi.com", "rod@eligibleapi.com", "andy@andybrett.com"]
11
- gem.description = 'Eligible is a developer-friendly way to process health care eligibility checks. Learn more at https://www.eligibleapi.com'
9
+ gem.authors = ['Katelyn Gleaon', 'Rodrigo Dominguez', 'Aaron Bedra']
10
+ gem.email = ['k@eligible.com', 'rod@eligible.com', 'abedra@eligible.com']
11
+ gem.description = 'Eligible is a developer-friendly way to process health care eligibility checks. Learn more at https://eligible.com'
12
12
  gem.summary = 'Ruby wrapper for the Eligible API'
13
- gem.homepage = "https://www.eligibleapi.com/"
14
- gem.license = "MIT"
13
+ gem.homepage = 'https://github.com/eligible/eligible-ruby'
14
+ gem.license = 'MIT'
15
15
 
16
- gem.files = `git ls-files`.split($/)
17
- gem.test_files = `git ls-files -- test/*`.split("\n")
18
- gem.require_paths = ["lib"]
16
+ gem.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ gem.require_paths = ['lib']
19
18
 
20
- gem.add_development_dependency('mocha')
21
- gem.add_development_dependency('shoulda')
22
- gem.add_development_dependency('test-unit')
23
- gem.add_development_dependency('rake')
19
+ gem.add_dependency('rest-client', '~> 1.8')
20
+ gem.add_dependency('multi_json', '~> 1.7')
24
21
 
25
- gem.add_dependency('rest-client', '~> 1.6.7')
26
- gem.add_dependency('multi_json', '~> 1.7.9')
22
+ gem.add_development_dependency('rake', '~> 10.5')
23
+ gem.add_development_dependency('rspec', '~> 3.4')
24
+ gem.add_development_dependency('rubocop', '= 0.35.0')
25
+ gem.add_development_dependency('simplecov', '~> 0.11')
27
26
  end
data/lib/eligible.rb CHANGED
@@ -28,13 +28,15 @@ require 'eligible/errors/authentication_error'
28
28
  require 'eligible/errors/api_error'
29
29
  require 'eligible/errors/invalid_request_error'
30
30
 
31
+ # rubocop:disable Metrics/ModuleLength, Style/ClassVars
31
32
  module Eligible
32
33
  @@api_key = nil
33
34
  @@test = false
34
- @@api_base = 'https://gds.eligibleapi.com/v1.1'
35
35
  @@api_version = 1.1
36
+ @@api_base = "https://gds.eligibleapi.com/v#{@@api_version}"
37
+ @@fingerprints = %w(79d62e8a9d59ae687372f8e71345c76d92527fac 4b2c6888ede79d0ee47339dc6fab5a6d0dc3cb0e)
36
38
 
37
- def self.api_url(url='')
39
+ def self.api_url(url = '')
38
40
  @@api_base + url.to_s
39
41
  end
40
42
 
@@ -70,87 +72,98 @@ module Eligible
70
72
  @@api_version
71
73
  end
72
74
 
73
- def self.request(method, url, api_key, params={}, headers={})
75
+ def self.fingerprints
76
+ @@fingerprints
77
+ end
78
+
79
+ def self.add_fingerprint(digest)
80
+ $stderr.puts 'The embedded certificate fingerprint was modified. This should only be done if instructed to by eligible support staff'
81
+ @@fingerprints << digest
82
+ end
83
+
84
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
85
+ def self.request(method, url, api_key, params = {}, headers = {})
74
86
  api_key ||= @@api_key
75
87
  test = self.test
76
- api_key = params[:api_key] if params.has_key?(:api_key)
77
- test = params[:test] if params.has_key?(:test)
88
+ api_key = params[:api_key] if params.key?(:api_key)
89
+ test = params[:test] if params.key?(:test)
78
90
 
79
- raise AuthenticationError.new('No API key provided. (HINT: set your API key using "Eligible.api_key = <API-KEY>".') unless api_key
91
+ fail AuthenticationError, 'No API key provided. (HINT: set your API key using "Eligible.api_key = <API-KEY>".' unless api_key
80
92
 
81
93
  lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
82
- ua = {
83
- :bindings_version => Eligible::VERSION,
84
- :lang => 'ruby',
85
- :lang_version => lang_version,
86
- :platform => RUBY_PLATFORM,
87
- :publisher => 'eligible',
88
- :uname => uname
94
+ debug_info = {
95
+ bindings_version: Eligible::VERSION,
96
+ lang: 'ruby',
97
+ lang_version: lang_version,
98
+ platform: RUBY_PLATFORM,
99
+ publisher: 'eligible',
100
+ uname: uname
89
101
  }
90
102
 
91
103
  # GET requests, parameters on the query string
92
104
  # POST requests, parameters as json in the body
93
- url = self.api_url(url)
105
+ url = api_url(url)
94
106
  case method.to_s.downcase.to_sym
95
- when :get, :head
96
- url += "?api_key=#{api_key}"
97
- if params && params.count > 0
98
- query_string = Util.flatten_params(params).collect { |key, value| "#{key}=#{Util.url_encode(value)}" }.join('&')
99
- url += "&#{query_string}"
100
- end
101
- url +="&test=#{test}"
102
- payload = nil
103
- else
104
- payload = Eligible::JSON.dump(params.merge!({ 'api_key' => api_key, 'test' => test }))
107
+ when :get, :head
108
+ url += "?api_key=#{api_key}"
109
+ if params && params.count > 0
110
+ query_string = Util.flatten_params(params).collect { |key, value| "#{key}=#{Util.url_encode(value)}" }.join('&')
111
+ url += "&#{query_string}"
112
+ end
113
+ url += "&test=#{test}"
114
+ payload = nil
115
+ else
116
+ payload = Eligible::JSON.dump(params.merge!('api_key' => api_key, 'test' => test))
105
117
  end
106
118
 
107
119
  begin
108
- headers = { :x_eligible_client_user_agent => Eligible::JSON.dump(ua) }.merge(headers)
120
+ headers = { x_eligible_debuginfo: Eligible::JSON.dump(debug_info) }.merge(headers)
109
121
  rescue => e
110
122
  headers = {
111
- :x_eligible_client_raw_user_agent => ua.inspect,
112
- :error => "#{e} (#{e.class})"
123
+ x_eligible_client_raw_user_agent: debug_info.inspect,
124
+ error: "#{e} (#{e.class})"
113
125
  }.merge(headers)
114
126
  end
115
127
 
116
128
  headers = {
117
- :user_agent => "Eligible/v1 RubyBindings/#{Eligible::VERSION}",
118
- :authorization => "Bearer #{api_key}",
119
- :content_type => 'application/x-www-form-urlencoded'
129
+ user_agent: "eligible-ruby/#{Eligible::VERSION}",
130
+ authorization: "Bearer #{api_key}",
131
+ content_type: 'application/x-www-form-urlencoded'
120
132
  }.merge(headers)
121
133
 
122
- headers[:eligible_version] = self.api_version if self.api_version
134
+ headers[:eligible_version] = api_version if api_version
123
135
 
124
136
  opts = {
125
- :method => method,
126
- :url => url,
127
- :headers => headers,
128
- :open_timeout => 30,
129
- :payload => payload,
130
- :timeout => 80
137
+ method: method,
138
+ url: url,
139
+ headers: headers,
140
+ open_timeout: 30,
141
+ payload: payload,
142
+ timeout: 80,
143
+ ssl_verify_callback: verify_certificate
131
144
  }
132
145
 
133
146
  begin
134
147
  response = execute_request(opts)
135
-
136
148
  rescue SocketError => e
137
- self.handle_restclient_error(e)
149
+ handle_restclient_error(e)
138
150
  rescue NoMethodError => e
139
151
  # Work around RestClient bug
140
152
  if e.message =~ /\WRequestFailed\W/
141
153
  e = APIConnectionError.new('Unexpected HTTP response code')
142
- self.handle_restclient_error(e)
154
+ handle_restclient_error(e)
143
155
  else
144
156
  raise
145
157
  end
158
+ # rubocop:disable Lint/AssignmentInCondition, Style/AndOr
146
159
  rescue RestClient::ExceptionWithResponse => e
147
160
  if rcode = e.http_code and rbody = e.http_body
148
- self.handle_api_error(rcode, rbody)
161
+ handle_api_error(rcode, rbody)
149
162
  else
150
- self.handle_restclient_error(e)
163
+ handle_restclient_error(e)
151
164
  end
152
165
  rescue RestClient::Exception, Errno::ECONNREFUSED => e
153
- self.handle_restclient_error(e)
166
+ handle_restclient_error(e)
154
167
  end
155
168
 
156
169
  rbody = response.body
@@ -171,6 +184,20 @@ module Eligible
171
184
  [resp, api_key]
172
185
  end
173
186
 
187
+ def self.verify_certificate
188
+ lambda do |preverify_ok, certificate_store|
189
+ return true if test == 'true'
190
+ return false unless preverify_ok
191
+ received = certificate_store.chain.first
192
+ return true unless received.to_der == certificate_store.current_cert.to_der
193
+ valid_fingerprint?(received)
194
+ end
195
+ end
196
+
197
+ def self.valid_fingerprint?(received)
198
+ fingerprints.include?(OpenSSL::Digest::SHA1.hexdigest(received.to_der))
199
+ end
200
+
174
201
  private
175
202
 
176
203
  def self.uname
@@ -181,51 +208,52 @@ module Eligible
181
208
  RestClient::Request.execute(opts)
182
209
  end
183
210
 
211
+ # rubocop:disable Style/SignalException
184
212
  def self.handle_api_error(rcode, rbody)
185
213
  begin
186
214
  error_obj = Eligible::JSON.load(rbody)
187
215
  error_obj = Util.symbolize_names(error_obj)
188
- error = error_obj[:error] or raise EligibleError.new # escape from parsing
216
+ fail EligibleError unless error_obj.key?(:error)
217
+ error = error_obj[:error]
189
218
  rescue MultiJson::DecodeError, EligibleError
190
219
  raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
191
220
  end
192
221
 
222
+ error_msg = error[:details] || error[:reject_reason_description]
223
+
193
224
  case rcode
194
- when 400, 404 then
195
- raise invalid_request_error(error, rcode, rbody, error_obj)
196
- when 401
197
- raise authentication_error(error, rcode, rbody, error_obj)
198
- else
199
- raise api_error(error, rcode, rbody, error_obj)
225
+ when 400, 404 then
226
+ raise invalid_request_error(error_msg, rcode, rbody, error_obj)
227
+ when 401
228
+ raise authentication_error(error_msg, rcode, rbody, error_obj)
229
+ else
230
+ raise api_error(error_msg, rcode, rbody, error_obj)
200
231
  end
201
232
  end
202
233
 
203
- def self.invalid_request_error(error, rcode, rbody, error_obj)
204
- InvalidRequestError.new(error, rcode, rbody, error_obj)
234
+ def self.invalid_request_error(error_msg, rcode, rbody, error_obj)
235
+ InvalidRequestError.new(error_msg, rcode, rbody, error_obj)
205
236
  end
206
237
 
207
- def self.authentication_error(error, rcode, rbody, error_obj)
208
- AuthenticationError.new(error[0][:message], rcode, rbody, error_obj)
238
+ def self.authentication_error(error_msg, rcode, rbody, error_obj)
239
+ AuthenticationError.new(error_msg, rcode, rbody, error_obj)
209
240
  end
210
241
 
211
- def self.api_error(error, rcode, rbody, error_obj)
212
- APIError.new(error[0][:message], rcode, rbody, error_obj)
242
+ def self.api_error(error_msg, rcode, rbody, error_obj)
243
+ APIError.new(error_msg, rcode, rbody, error_obj)
213
244
  end
214
245
 
215
246
  def self.handle_restclient_error(e)
216
247
  case e
217
- when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
218
- message = "Could not connect to Eligible (#{@@api_base}). Please check your internet connection and try again."
219
- when RestClient::SSLCertificateNotVerified
220
- message = "Could not verify Eligible's SSL certificate."
221
- when SocketError
222
- message = 'Unexpected error communicating when trying to connect to Eligible.'
223
- else
224
- message = 'Unexpected error communicating with Eligible. If this problem persists, let us know at support@eligible.com.'
248
+ when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
249
+ message = "Could not connect to Eligible (#{@@api_base}). Please check your internet connection and try again."
250
+ when RestClient::SSLCertificateNotVerified
251
+ message = "Could not verify Eligible's SSL certificate."
252
+ when SocketError
253
+ message = 'Unexpected error communicating when trying to connect to Eligible.'
254
+ else
255
+ message = 'Unexpected error communicating with Eligible. If this problem persists, let us know at support@eligible.com.'
225
256
  end
226
- message += "\n\n(Network error: #{e.message})"
227
- raise APIConnectionError.new(message)
257
+ fail APIConnectionError, "#{message}\n\n(Network error: #{e.message})"
228
258
  end
229
-
230
259
  end
231
-
@@ -1,16 +1,14 @@
1
1
  module Eligible
2
2
  class APIResource < EligibleObject
3
-
4
3
  def self.class_name
5
- self.name.split('::')[-1]
4
+ name.split('::').last
6
5
  end
7
6
 
8
- def self.url()
7
+ def self.url
9
8
  if self == APIResource
10
- raise NotImplementedError.new('APIResource is an abstract class. You should perform actions on its subclasses (Plan, Service, etc.)')
9
+ fail NotImplementedError, 'APIResource is an abstract class. You should perform actions on its subclasses (Plan, Service, etc.)'
11
10
  end
12
11
  "/#{CGI.escape(class_name.downcase)}/"
13
12
  end
14
-
15
13
  end
16
- end
14
+ end
@@ -1,28 +1,22 @@
1
1
  module Eligible
2
2
  class Claim < APIResource
3
+ def self.get(params, api_key = nil)
4
+ response, api_key = Eligible.request(:get, "/claims/acknowledgements/#{params[:reference_id]}.json", api_key, params)
5
+ Util.convert_to_eligible_object(response, api_key)
6
+ end
3
7
 
4
- class << self
5
-
6
- def get(params, api_key = nil)
7
- response, api_key = Eligible.request(:get, "/claims/acknowledgements/#{params[:reference_id]}.json", api_key, params)
8
- Util.convert_to_eligible_object(response, api_key)
9
- end
10
-
11
- def post(params, api_key = nil)
12
- response, api_key = Eligible.request(:post, '/claims.json', api_key, params)
13
- Util.convert_to_eligible_object(response, api_key)
14
- end
15
-
16
- def all(api_key = nil)
17
- response, api_key = Eligible.request(:get, '/claims/acknowledgements.json', api_key)
18
- Util.convert_to_eligible_object(response, api_key)
19
- end
8
+ def self.post(params, api_key = nil)
9
+ response, api_key = Eligible.request(:post, '/claims.json', api_key, params)
10
+ Util.convert_to_eligible_object(response, api_key)
11
+ end
20
12
 
13
+ def self.all(api_key = nil)
14
+ response, api_key = Eligible.request(:get, '/claims/acknowledgements.json', api_key)
15
+ Util.convert_to_eligible_object(response, api_key)
21
16
  end
22
17
 
23
18
  def status
24
19
  error ? nil : to_hash
25
20
  end
26
-
27
21
  end
28
- end
22
+ end
@@ -1,24 +1,23 @@
1
1
  module Eligible
2
2
  class Coverage < APIResource
3
+ def self.get(params, api_key = nil)
4
+ response, api_key = Eligible.request(:get, '/coverage/all.json', api_key, params)
5
+ Util.convert_to_eligible_object(response, api_key)
6
+ end
3
7
 
4
- class << self
5
-
6
- def get(params, api_key=nil)
7
- response, api_key = Eligible.request(:get, '/coverage/all.json', api_key, params)
8
- Util.convert_to_eligible_object(response, api_key)
9
- end
10
-
11
- def batch_post(params, api_key=nil)
12
- response, api_key = Eligible.request(:post, '/coverage/all/batch.json', api_key, params)
13
- Util.convert_to_eligible_object(response, api_key)
14
- end
15
-
16
- def batch_medicare_post(params, api_key=nil)
17
- response, api_key = Eligible.request(:post, '/medicare/coverage/batch.json', api_key, params)
18
- Util.convert_to_eligible_object(response, api_key)
19
- end
8
+ def self.cost_estimate(params, api_key = nil)
9
+ response, api_key = Eligible.request(:get, '/coverage/cost_estimate.json', api_key, params)
10
+ Util.convert_to_eligible_object(response, api_key)
11
+ end
20
12
 
13
+ def self.batch_post(params, api_key = nil)
14
+ response, api_key = Eligible.request(:post, '/coverage/all/batch.json', api_key, params)
15
+ Util.convert_to_eligible_object(response, api_key)
21
16
  end
22
17
 
18
+ def self.batch_medicare_post(params, api_key = nil)
19
+ response, api_key = Eligible.request(:post, '/medicare/coverage/batch.json', api_key, params)
20
+ Util.convert_to_eligible_object(response, api_key)
21
+ end
23
22
  end
24
- end
23
+ end
@@ -1,16 +1,13 @@
1
1
  module Eligible
2
2
  class Demographic < APIResource
3
- class << self
4
-
5
- def get(params, api_key=nil)
6
- response, api_key = Eligible.request(:get, '/demographic/all.json', api_key, params)
7
- Util.convert_to_eligible_object(response, api_key)
8
- end
3
+ def self.get(params, api_key = nil)
4
+ response, api_key = Eligible.request(:get, '/demographic/all.json', api_key, params)
5
+ Util.convert_to_eligible_object(response, api_key)
6
+ end
9
7
 
10
- def batch_post(params, api_key=nil)
11
- response, api_key = Eligible.request(:post, '/demographic/all/batch.json', api_key, params)
12
- Util.convert_to_eligible_object(response, api_key)
13
- end
8
+ def self.batch_post(params, api_key = nil)
9
+ response, api_key = Eligible.request(:post, '/demographic/all/batch.json', api_key, params)
10
+ Util.convert_to_eligible_object(response, api_key)
14
11
  end
15
12
  end
16
13
  end
@@ -1,18 +1,16 @@
1
1
  module Eligible
2
-
3
2
  class EligibleObject
4
3
  include Enumerable
5
4
 
6
5
  attr_accessor :api_key
7
6
  attr_accessor :eligible_id
7
+ # rubocop:disable Style/ClassVars
8
8
  @@permanent_attributes = Set.new([:api_key, :error, :balance, :address, :dob])
9
9
 
10
10
  # The default :id method is deprecated and isn't useful to us
11
- if method_defined?(:id)
12
- undef :id
13
- end
11
+ undef :id if method_defined?(:id)
14
12
 
15
- def initialize(id=nil, api_key=nil)
13
+ def initialize(id = nil, api_key = nil)
16
14
  @api_key = api_key
17
15
  @values = {}
18
16
  # This really belongs in APIResource, but not putting it there allows us
@@ -22,13 +20,14 @@ module Eligible
22
20
  self.eligible_id = id if id
23
21
  end
24
22
 
25
- def self.construct_from(values, api_key=nil)
26
- obj = self.new(values[:eligible_id], api_key)
23
+ def self.construct_from(values, api_key = nil)
24
+ obj = new(values[:eligible_id], api_key)
27
25
  obj.refresh_from(values, api_key)
28
26
  obj
29
27
  end
30
28
 
31
- def refresh_from(values, api_key, partial=false)
29
+ # rubocop:disable Metrics/AbcSize
30
+ def refresh_from(values, api_key, partial = false)
32
31
  @api_key = api_key
33
32
 
34
33
  removed = partial ? Set.new : Set.new(@values.keys - values.keys)
@@ -54,7 +53,7 @@ module Eligible
54
53
  end
55
54
 
56
55
  def [](k)
57
- k = k.to_sym if k.kind_of?(String)
56
+ k = k.to_sym if k.is_a?(String)
58
57
  @values[k]
59
58
  end
60
59
 
@@ -70,7 +69,7 @@ module Eligible
70
69
  @values.values
71
70
  end
72
71
 
73
- def to_json(*a)
72
+ def to_json
74
73
  Eligible::JSON.dump(@values)
75
74
  end
76
75
 
@@ -90,7 +89,7 @@ module Eligible
90
89
 
91
90
  def metaclass
92
91
  class << self; self; end
93
- end
92
+ end
94
93
 
95
94
  def remove_accessors(keys)
96
95
  metaclass.instance_eval do
@@ -115,7 +114,6 @@ module Eligible
115
114
  end
116
115
  end
117
116
  end
118
- end
117
+ end
119
118
  end
120
-
121
- end
119
+ end