eligible 1.0 → 3.0.0.beta17

Sign up to get free protection for your applications and to get access to all the features.
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