eligible 1.0 → 3.0.0.beta17

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.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +76 -0
  3. data/.codeclimate.yml +23 -0
  4. data/.gitignore +3 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +1158 -0
  7. data/.ruby-version +1 -0
  8. data/CHANGELOG.md +170 -0
  9. data/Gemfile +4 -0
  10. data/Gemfile.lock +66 -0
  11. data/LICENSE +1 -1
  12. data/README.md +700 -93
  13. data/Rakefile +12 -2
  14. data/eligible.gemspec +14 -16
  15. data/lib/eligible/api_resource.rb +49 -5
  16. data/lib/eligible/calculator_deploy_url.rb +7 -0
  17. data/lib/eligible/claim.rb +22 -6
  18. data/lib/eligible/coverage.rb +19 -0
  19. data/lib/eligible/coverage_resource.rb +23 -0
  20. data/lib/eligible/customer.rb +19 -0
  21. data/lib/eligible/demographic.rb +6 -35
  22. data/lib/eligible/eligible_object.rb +12 -15
  23. data/lib/eligible/encryptor.rb +121 -0
  24. data/lib/eligible/enrollment.rb +23 -0
  25. data/lib/eligible/errors/eligible_error.rb +6 -3
  26. data/lib/eligible/errors/invalid_request_error.rb +4 -0
  27. data/lib/eligible/icd.rb +16 -0
  28. data/lib/eligible/json.rb +5 -15
  29. data/lib/eligible/lockbox.rb +39 -0
  30. data/lib/eligible/medicare.rb +11 -0
  31. data/lib/eligible/oauth_token.rb +9 -0
  32. data/lib/eligible/ocr.rb +15 -0
  33. data/lib/eligible/original_signature_pdf.rb +45 -0
  34. data/lib/eligible/payer.rb +17 -0
  35. data/lib/eligible/payer_mapping.rb +37 -0
  36. data/lib/eligible/payment.rb +11 -0
  37. data/lib/eligible/preauth_resource.rb +11 -0
  38. data/lib/eligible/precert.rb +15 -0
  39. data/lib/eligible/provider_model.rb +7 -0
  40. data/lib/eligible/public_key.rb +27 -0
  41. data/lib/eligible/received_pdf.rb +26 -0
  42. data/lib/eligible/referral.rb +11 -0
  43. data/lib/eligible/risk_assessment.rb +15 -0
  44. data/lib/eligible/session_token.rb +11 -0
  45. data/lib/eligible/ticket.rb +40 -0
  46. data/lib/eligible/util.rb +29 -38
  47. data/lib/eligible/v1_0/action.rb +9 -0
  48. data/lib/eligible/v1_0/attribute.rb +9 -0
  49. data/lib/eligible/v1_0/charge.rb +13 -0
  50. data/lib/eligible/v1_0/claim.rb +25 -0
  51. data/lib/eligible/v1_0/claim_service_line.rb +9 -0
  52. data/lib/eligible/v1_0/contract.rb +9 -0
  53. data/lib/eligible/v1_0/device.rb +9 -0
  54. data/lib/eligible/v1_0/discount.rb +9 -0
  55. data/lib/eligible/v1_0/enrollment.rb +17 -0
  56. data/lib/eligible/v1_0/estimate.rb +29 -0
  57. data/lib/eligible/v1_0/estimate_service_line.rb +17 -0
  58. data/lib/eligible/v1_0/fee.rb +21 -0
  59. data/lib/eligible/v1_0/fee_refund.rb +29 -0
  60. data/lib/eligible/v1_0/file.rb +17 -0
  61. data/lib/eligible/v1_0/file_link.rb +13 -0
  62. data/lib/eligible/v1_0/insurance_company.rb +21 -0
  63. data/lib/eligible/v1_0/insurance_company_alias.rb +9 -0
  64. data/lib/eligible/v1_0/insurance_policy.rb +9 -0
  65. data/lib/eligible/v1_0/patient_question.rb +9 -0
  66. data/lib/eligible/v1_0/patient_questionnaire.rb +9 -0
  67. data/lib/eligible/v1_0/patient_record.rb +9 -0
  68. data/lib/eligible/v1_0/patient_statement.rb +54 -0
  69. data/lib/eligible/v1_0/patient_statement_service_line.rb +13 -0
  70. data/lib/eligible/v1_0/payment_report.rb +21 -0
  71. data/lib/eligible/v1_0/product.rb +9 -0
  72. data/lib/eligible/v1_0/provider.rb +9 -0
  73. data/lib/eligible/v1_0/remark.rb +21 -0
  74. data/lib/eligible/v1_0/reports/accuracy_stats.rb +23 -0
  75. data/lib/eligible/v1_0/reports/estimate_friction.rb +23 -0
  76. data/lib/eligible/v1_0/reports/in_scope_distribution.rb +23 -0
  77. data/lib/eligible/v1_0/rest_api_base.rb +44 -0
  78. data/lib/eligible/v1_0/rule.rb +13 -0
  79. data/lib/eligible/v1_0/session.rb +21 -0
  80. data/lib/eligible/v1_0/transaction.rb +21 -0
  81. data/lib/eligible/v1_0/treatment.rb +9 -0
  82. data/lib/eligible/v1_0/value_list.rb +9 -0
  83. data/lib/eligible/v1_0/value_list_item.rb +9 -0
  84. data/lib/eligible/v1_0/verification.rb +17 -0
  85. data/lib/eligible/version.rb +1 -1
  86. data/lib/eligible/visit_type.rb +11 -0
  87. data/lib/eligible/x12.rb +8 -0
  88. data/lib/eligible.rb +320 -110
  89. metadata +148 -67
  90. data/CONTRIBUTORS +0 -1
  91. data/lib/eligible/plan.rb +0 -42
  92. data/lib/eligible/service.rb +0 -39
  93. data/test/test_eligible.rb +0 -279
  94. data/test/test_helper.rb +0 -67
data/Rakefile CHANGED
@@ -1,4 +1,14 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
+ require 'rubocop/rake_task'
4
+ require 'rspec/core/rake_task'
3
5
 
4
- Rake::TestTask.new(:test)
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: [:spec]
9
+
10
+ desc 'Runs code coverage'
11
+ task :rcov do
12
+ ENV['COVERAGE'] = 'true'
13
+ Rake::Task[:spec].invoke
14
+ end
data/eligible.gemspec CHANGED
@@ -4,25 +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 = ["Andy Brett"]
10
- gem.email = ["andy@andybrett.com"]
11
- gem.description = 'Eligible is a developer-friendly way to process health care eligibility checks. Learn more at https://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://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.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
- gem.test_files = `git ls-files -- test/*`.split("\n")
19
- 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']
20
18
 
21
- gem.add_development_dependency('mocha')
22
- gem.add_development_dependency('shoulda')
23
- gem.add_development_dependency('test-unit')
24
- gem.add_development_dependency('rake')
19
+ gem.add_dependency('rest-client', '~> 2.0.0')
20
+ gem.add_dependency('multi_json', '~> 1.7')
25
21
 
26
- gem.add_dependency('rest-client', '~> 1.4')
27
- gem.add_dependency('multi_json', '>= 1.0.4', '< 2')
22
+ gem.add_development_dependency('rake', '~> 12.0')
23
+ gem.add_development_dependency('rspec', '~> 3.4')
24
+ gem.add_development_dependency('rspec_junit_formatter', '~> 0.3.0')
25
+ gem.add_development_dependency('simplecov', '~> 0.11')
28
26
  end
@@ -1,14 +1,58 @@
1
1
  module Eligible
2
2
  class APIResource < EligibleObject
3
3
  def self.class_name
4
- self.name.split('::')[-1]
4
+ name.split('::').last
5
5
  end
6
6
 
7
- def self.url()
7
+ def self.api_url(base, params = nil, param_id = nil)
8
+ if params.nil?
9
+ "/#{base}"
10
+ else
11
+ id = Util.value(params, param_id)
12
+ "/#{base}/#{id}"
13
+ end
14
+ end
15
+
16
+ def self.url
8
17
  if self == APIResource
9
- raise NotImplementedError.new('APIResource is an abstract class. You should perform actions on its subclasses (Plan, Service, etc.)')
18
+ fail NotImplementedError, 'APIResource is an abstract class. You should perform actions on its subclasses (Plan, Service, etc.)'
10
19
  end
11
- "/#{CGI.escape(class_name.downcase)}/all.json"
20
+ "/#{CGI.escape(class_name.downcase)}/"
21
+ end
22
+
23
+ def self.endpoint_name
24
+ self.const_get('ENDPOINT_NAME')
25
+ end
26
+
27
+ def self.require_param(value, name)
28
+ fail ArgumentError, "#{name} of the #{class_name} is required" if value.nil? || (value.is_a?(String) && value.empty?)
29
+ end
30
+
31
+ def self.required_param_validation(params:, required_params:)
32
+ return if required_params.nil? || !required_params.is_a?(Array)
33
+
34
+ required_params.each do |required_param_name|
35
+ required_param = Util.value(params, required_param_name)
36
+ require_param(required_param, required_param_name)
37
+ end
38
+ end
39
+
40
+ def self.rest_api_params(id_or_params)
41
+ id_or_params.is_a?(Hash) ? id_or_params : { id: id_or_params }
42
+ end
43
+
44
+ def self.send_request(method, url, params, opts)
45
+ headers = opts.clone
46
+ client_secret = headers.delete(:client_secret)
47
+ api_key = headers.delete(:api_key)
48
+ api_key = client_secret unless client_secret.nil?
49
+
50
+ required_param_validation(params: params, required_params: headers.delete(:required_params))
51
+
52
+ # Here rest_api_version is related to New REST API Endpoints
53
+ params = self.const_defined?(:REST_API_VERSION) ? params.merge(rest_api_version: self::REST_API_VERSION) : params
54
+ response, api_key = Eligible.request(method, url, api_key, params, headers)
55
+ Util.convert_to_eligible_object(response, api_key)
12
56
  end
13
57
  end
14
- end
58
+ end
@@ -0,0 +1,7 @@
1
+ module Eligible
2
+ class CalculatorDeployUrl < APIResource
3
+ def self.fetch_or_create(params, opts = {})
4
+ send_request :get, '/calculator_deploy_urls.json', params, opts
5
+ end
6
+ end
7
+ end
@@ -1,12 +1,28 @@
1
1
  module Eligible
2
2
  class Claim < APIResource
3
- def self.get(params, api_key=nil)
4
- response, api_key = Eligible.request(:get, url, api_key, params)
5
- Util.convert_to_eligible_object(response, api_key)
3
+ def self.ack(params, opts = {})
4
+ reference_id = Util.value(params, :reference_id)
5
+ send_request :get, "/claims/#{reference_id}/acknowledgements.json", params, opts.merge(required_params: [:reference_id])
6
6
  end
7
7
 
8
- def status
9
- error ? nil : to_hash
8
+ def self.post(params, opts = {})
9
+ send_request :post, '/claims.json', params, opts
10
+ end
11
+
12
+ def self.acks(params, opts = {})
13
+ send_request :get, '/claims/acknowledgements.json', params, opts
14
+ end
15
+
16
+ def self.payment_report(params, opts = {})
17
+ reference_id = Util.value(params, :reference_id)
18
+ require_param(reference_id, 'Reference id')
19
+ id = Util.value(params, :id)
20
+ url = id.nil? ? "/claims/#{reference_id}/payment_reports" : "/claims/#{reference_id}/payment_reports/#{id}"
21
+ send_request :get, url, params, opts
22
+ end
23
+
24
+ def self.payment_reports(params, opts = {})
25
+ send_request :get, '/claims/payment_reports.json', params, opts
10
26
  end
11
27
  end
12
- end
28
+ end
@@ -0,0 +1,19 @@
1
+ module Eligible
2
+ class Coverage < CoverageResource
3
+ def self.get_uri
4
+ return '/coverage/all.json'
5
+ end
6
+
7
+ def self.post_uri
8
+ return '/coverage/all/batch.json'
9
+ end
10
+
11
+ def self.cost_estimate(params, opts = {})
12
+ send_request :get, '/coverage/cost_estimates.json', params, opts
13
+ end
14
+
15
+ def self.batch_medicare_post(params, opts = {})
16
+ send_request :post, '/medicare/coverage/batch.json', params, opts
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module Eligible
2
+ class CoverageResource < APIResource
3
+ def self.get(params, opts = {})
4
+ send_request :get, get_uri, params, opts
5
+ end
6
+
7
+ def self.post(params, opts = {})
8
+ send_request :post, post_uri, params, opts
9
+ end
10
+
11
+ def self.get_uri
12
+ fail NotImplementedError, "Please implement class method #{self}.get_uri"
13
+ end
14
+
15
+ def self.post_uri
16
+ fail NotImplementedError, "Please implement class method #{self}.post_uri"
17
+ end
18
+
19
+ class << self
20
+ alias_method :batch_post, :post
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Eligible
2
+ class Customer < APIResource
3
+ def self.get(params, opts = {})
4
+ send_request :get, api_url('customers', params, :customer_id), params, opts.merge(required_params: [:customer_id])
5
+ end
6
+
7
+ def self.post(params, opts = {})
8
+ send_request :post, api_url('customers'), params, opts
9
+ end
10
+
11
+ def self.update(params, opts = {})
12
+ send_request :put, api_url('customers', params, :customer_id), params, opts.merge(required_params: [:customer_id])
13
+ end
14
+
15
+ def self.all(params, opts = {})
16
+ send_request :get, api_url('customers'), params, opts
17
+ end
18
+ end
19
+ end
@@ -1,40 +1,11 @@
1
1
  module Eligible
2
- class Demographic < APIResource
3
- COMMON_ATTRIBUTES = [:timestamp, :eligible_id, :mapping_version]
4
- ZIP_ATTRIBUTES = [:zip]
5
- EMPLOYER_ATTRIBUTES = [:group_id, :group_name]
6
- ADDRESS_ATTRIBUTES = [:address]
7
- DOB_ATTRIBUTES = [:dob]
8
-
9
- def self.get(params, api_key=nil)
10
- response, api_key = Eligible.request(:get, url, api_key, params)
11
- Util.convert_to_eligible_object(response, api_key)
12
- end
13
-
14
- def all
15
- error ? nil : to_hash
16
- end
17
-
18
- def zip
19
- keys = COMMON_ATTRIBUTES
20
- h = to_hash.select { |k, v| keys.include?(k) }
21
- h[:zip] = to_hash[:address][:zip]
22
- error ? nil : h
23
- end
24
-
25
- def employer
26
- keys = COMMON_ATTRIBUTES + EMPLOYER_ATTRIBUTES
27
- error ? nil : to_hash.select { |k, v| keys.include?(k) }
28
- end
29
-
30
- def address
31
- keys = COMMON_ATTRIBUTES + ADDRESS_ATTRIBUTES
32
- error ? nil : to_hash.select { |k, v| keys.include?(k) }
2
+ class Demographic < CoverageResource
3
+ def self.get_uri
4
+ return '/demographic/all.json'
33
5
  end
34
6
 
35
- def dob
36
- keys = COMMON_ATTRIBUTES + DOB_ATTRIBUTES
37
- error ? nil : to_hash.select { |k, v| keys.include?(k) }
7
+ def self.post_uri
8
+ return '/demographic/all/batch.json'
38
9
  end
39
10
  end
40
- end
11
+ 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
+
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,13 @@ 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
+ def refresh_from(values, api_key, partial = false)
32
30
  @api_key = api_key
33
31
 
34
32
  removed = partial ? Set.new : Set.new(@values.keys - values.keys)
@@ -47,14 +45,14 @@ module Eligible
47
45
  @unsaved_values.delete(k)
48
46
  end
49
47
  values.each do |k, v|
50
- @values[k] = v#Util.convert_to_eligible_object(v, api_key)
48
+ @values[k] = v
51
49
  @transient_values.delete(k)
52
50
  @unsaved_values.delete(k)
53
51
  end
54
52
  end
55
53
 
56
54
  def [](k)
57
- k = k.to_sym if k.kind_of?(String)
55
+ k = k.to_sym if k.is_a?(String)
58
56
  @values[k]
59
57
  end
60
58
 
@@ -70,7 +68,7 @@ module Eligible
70
68
  @values.values
71
69
  end
72
70
 
73
- def to_json(*a)
71
+ def to_json
74
72
  Eligible::JSON.dump(@values)
75
73
  end
76
74
 
@@ -90,7 +88,7 @@ module Eligible
90
88
 
91
89
  def metaclass
92
90
  class << self; self; end
93
- end
91
+ end
94
92
 
95
93
  def remove_accessors(keys)
96
94
  metaclass.instance_eval do
@@ -115,7 +113,6 @@ module Eligible
115
113
  end
116
114
  end
117
115
  end
118
- end
116
+ end
119
117
  end
120
-
121
- end
118
+ end
@@ -0,0 +1,121 @@
1
+ require 'openssl'
2
+
3
+ module Eligible
4
+ # A simple wrapper for the standard OpenSSL library
5
+ module Encryptor
6
+ extend self
7
+ # The default options to use when calling the <tt>encrypt</tt> and <tt>decrypt</tt> methods
8
+ #
9
+ # Defaults to { algorithm: 'aes-256-gcm',
10
+ # auth_data: '',
11
+ # insecure_mode: false,
12
+ # hmac_iterations: 2000,
13
+ # v2_gcm_iv: false }
14
+ #
15
+ # Run 'openssl list-cipher-commands' in your terminal to view a list all cipher algorithms that are supported on your platform
16
+ def default_options
17
+ @default_options ||= {
18
+ algorithm: 'aes-256-cbc',
19
+ auth_data: '',
20
+ insecure_mode: false,
21
+ hmac_iterations: 2000,
22
+ v2_gcm_iv: false
23
+ }
24
+ end
25
+
26
+ # Encrypts a <tt>:value</tt> with a specified <tt>:key</tt> and <tt>:iv</tt>.
27
+ #
28
+ # Optionally accepts <tt>:salt</tt>, <tt>:auth_data</tt>, <tt>:algorithm</tt>, <tt>:hmac_iterations</tt>, and <tt>:insecure_mode</tt> options.
29
+ #
30
+ # Example
31
+ #
32
+ # encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: 'some secret key', iv: 'some unique value', salt: 'another unique value')
33
+ # # or
34
+ # encrypted_value = Encryptor.encrypt('some string to encrypt', key: 'some secret key', iv: 'some unique value', salt: 'another unique value')
35
+ def encrypt(*args, &block)
36
+ crypt :encrypt, *args, &block
37
+ end
38
+
39
+ # Decrypts a <tt>:value</tt> with a specified <tt>:key</tt> and <tt>:iv</tt>.
40
+ #
41
+ # Optionally accepts <tt>:salt</tt>, <tt>:auth_data</tt>, <tt>:algorithm</tt>, <tt>:hmac_iterations</tt>, and <tt>:insecure_mode</tt> options.
42
+ #
43
+ # Example
44
+ #
45
+ # decrypted_value = Encryptor.decrypt(value: 'some encrypted string', key: 'some secret key', iv: 'some unique value', salt: 'another unique value')
46
+ # # or
47
+ # decrypted_value = Encryptor.decrypt('some encrypted string', key: 'some secret key', iv: 'some unique value', salt: 'another unique value')
48
+ def decrypt(*args, &block)
49
+ crypt :decrypt, *args, &block
50
+ end
51
+
52
+ protected
53
+
54
+ def crypt(cipher_method, *args) #:nodoc:
55
+ options = default_options.merge(value: args.first).merge(args.last.is_a?(Hash) ? args.last : {})
56
+ raise ArgumentError.new('must specify a key') if options[:key].to_s.empty?
57
+ cipher = OpenSSL::Cipher.new(options[:algorithm])
58
+ cipher.send(cipher_method)
59
+
60
+ unless options[:insecure_mode]
61
+ raise ArgumentError.new("key must be #{cipher.key_len} bytes or longer") if options[:key].bytesize < cipher.key_len
62
+ raise ArgumentError.new('must specify an iv') if options[:iv].to_s.empty?
63
+ raise ArgumentError.new("iv must be #{cipher.iv_len} bytes or longer") if options[:iv].bytesize < cipher.iv_len
64
+ end
65
+
66
+ if options[:iv]
67
+ # This is here for backwards compatibility for Encryptor v2.0.0.
68
+ cipher.iv = options[:iv] if options[:v2_gcm_iv]
69
+ if options[:salt].nil?
70
+ # Use a non-salted cipher.
71
+ # This behaviour is retained for backwards compatibility. This mode
72
+ # is not secure and new deployments should use the :salt options
73
+ # wherever possible.
74
+ cipher.key = options[:key]
75
+ else
76
+ # Use an explicit salt (which can be persisted into a database on a
77
+ # per-column basis, for example). This is the preferred (and more
78
+ # secure) mode of operation.
79
+ cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(options[:key], options[:salt], options[:hmac_iterations], cipher.key_len)
80
+ end
81
+ cipher.iv = options[:iv] unless options[:v2_gcm_iv]
82
+ else
83
+ # This is deprecated and needs to be changed.
84
+ cipher.pkcs5_keyivgen(options[:key])
85
+ end
86
+
87
+ yield cipher, options if block_given?
88
+
89
+ value = options[:value]
90
+
91
+ if cipher.authenticated?
92
+ if encryption?(cipher_method)
93
+ cipher.auth_data = options[:auth_data]
94
+ else
95
+ value = extract_cipher_text(options[:value])
96
+ cipher.auth_tag = extract_auth_tag(options[:value])
97
+ # auth_data must be set after auth_tag has been set when decrypting
98
+ # See http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-auth_data-3D
99
+ cipher.auth_data = options[:auth_data]
100
+ end
101
+ end
102
+
103
+ result = cipher.update(value)
104
+ result << cipher.final
105
+ result << cipher.auth_tag if cipher.authenticated? && encryption?(cipher_method)
106
+ result
107
+ end
108
+
109
+ def encryption?(cipher_method)
110
+ cipher_method == :encrypt
111
+ end
112
+
113
+ def extract_cipher_text(value)
114
+ value[0..-17]
115
+ end
116
+
117
+ def extract_auth_tag(value)
118
+ value[-16..-1]
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,23 @@
1
+ module Eligible
2
+ class Enrollment < APIResource
3
+ def self.get(params, opts = {})
4
+ send_request :get, api_url('enrollment_npis', params, :enrollment_npi_id), params, opts.merge(required_params: [:enrollment_npi_id])
5
+ end
6
+
7
+ def self.list(params, opts = {})
8
+ send_request :get, api_url('enrollment_npis'), params, opts
9
+ end
10
+
11
+ def self.post(params, opts = {})
12
+ send_request :post, api_url('enrollment_npis'), params, opts
13
+ end
14
+
15
+ def self.update(params, opts = {})
16
+ send_request :put, api_url('enrollment_npis', params, :enrollment_npi_id), params, opts.merge(required_params: [:enrollment_npi_id])
17
+ end
18
+
19
+ def enrollment_npis
20
+ values.first[:enrollment_npis]
21
+ end
22
+ end
23
+ end
@@ -4,17 +4,20 @@ module Eligible
4
4
  attr_reader :http_status
5
5
  attr_reader :http_body
6
6
  attr_reader :json_body
7
+ attr_reader :errors
7
8
 
8
- def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
9
+ def initialize(message = nil, http_status = nil, http_body = nil, json_body = nil)
9
10
  @message = message
10
11
  @http_status = http_status
11
12
  @http_body = http_body
12
13
  @json_body = json_body
14
+ @errors = (@json_body || {}).fetch(:errors, [])
13
15
  end
14
16
 
15
17
  def to_s
16
- status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
17
- "#{status_string}#{@message}"
18
+ status_string = @http_status.nil? ? '' : "(Status #{@http_status}) "
19
+ json_errors = errors.presence || (json_body&.key?(:error) ? [json_body] : [])
20
+ "#{status_string}#{@message}: #{json_errors.to_json}"
18
21
  end
19
22
  end
20
23
  end
@@ -0,0 +1,4 @@
1
+ module Eligible
2
+ class InvalidRequestError < EligibleError
3
+ end
4
+ end
@@ -0,0 +1,16 @@
1
+ module Eligible
2
+ class Icd < APIResource
3
+
4
+ def self.list(params, opts = {})
5
+ send_request :get, "/icds/#{Util.value(params, :type)}", params, opts
6
+ end
7
+
8
+ def self.describe(params, opts = {})
9
+ send_request :get, "/icds/#{Util.value(params, :type)}/describe/#{Util.value(params, :code)}", params, opts
10
+ end
11
+
12
+ def self.crosswalk(params, opts = {})
13
+ send_request :get, "/icds/#{Util.value(params, :type)}/crosswalk/#{Util.value(params, :code)}", params, opts
14
+ end
15
+ end
16
+ end
data/lib/eligible/json.rb CHANGED
@@ -1,21 +1,11 @@
1
1
  module Eligible
2
2
  module JSON
3
- if MultiJson.respond_to?(:dump)
4
- def self.dump(*args)
5
- MultiJson.dump(*args)
6
- end
7
-
8
- def self.load(*args)
9
- MultiJson.load(*args)
10
- end
11
- else
12
- def self.dump(*args)
13
- MultiJson.encode(*args)
14
- end
3
+ def self.dump(*args)
4
+ MultiJson.dump(*args)
5
+ end
15
6
 
16
- def self.load(*args)
17
- MultiJson.decode(*args)
18
- end
7
+ def self.load(*args)
8
+ MultiJson.load(*args)
19
9
  end
20
10
  end
21
11
  end
@@ -0,0 +1,39 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Eligible
5
+ class Lockbox < APIResource
6
+ def self.get(params, opts = {})
7
+ send_request :get, api_url('lockboxes', params, :lockbox_id), params, opts.merge(required_params: [:lockbox_id])
8
+ end
9
+
10
+ def self.all(params, opts = {})
11
+ send_request :get, api_url('lockboxes'), params, opts
12
+ end
13
+
14
+ def self.extract_private_key(params)
15
+ private_key = Util.value(params, :private_key)
16
+ fail ArgumentError, "Private key is required for decryption" if private_key.nil?
17
+ private_key
18
+ end
19
+
20
+ def self.delete_private_key!(params)
21
+ params.delete('private_key')
22
+ params.delete(:private_key)
23
+ end
24
+
25
+ def self.decrypt_data(data, encrypted_data_key, private_key)
26
+ pkey = OpenSSL::PKey::RSA.new(private_key)
27
+ aes_key = pkey.private_decrypt(Base64.decode64(encrypted_data_key))
28
+ sha_key = Digest::SHA256.hexdigest(aes_key)
29
+ Encryptor.decrypt(value: Base64.decode64(data), key: sha_key, insecure_mode: true)
30
+ end
31
+
32
+ def self.get_and_decrypt_from_lockbox(params, opts = {})
33
+ private_key = extract_private_key(params)
34
+ delete_private_key!(params)
35
+ req = get(params, opts).to_hash
36
+ decrypt_data(req[:encrypted_data], req[:encrypted_key], private_key)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ module Eligible
2
+ class Medicare < CoverageResource
3
+ def self.get_uri
4
+ return '/medicare/coverage.json'
5
+ end
6
+
7
+ def self.post_uri
8
+ return '/medicare/coverage/batch.json'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eligible
4
+ class OauthToken < APIResource
5
+ def self.post(params, opts = {})
6
+ send_request :post, '/oauth/token', params, opts
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module Eligible
2
+ class Ocr < APIResource
3
+ def self.setup_file(params)
4
+ file = Util.value(params, :file)
5
+ params[:file] = File.new(file, 'rb') if file.is_a?(String)
6
+ end
7
+
8
+ def self.post(params, opts = {})
9
+ setup_file(params)
10
+ send_request :post, '/card_scans.json', params, opts
11
+ end
12
+
13
+ private_class_method :setup_file
14
+ end
15
+ end