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.
- checksums.yaml +7 -0
- data/.circleci/config.yml +76 -0
- data/.codeclimate.yml +23 -0
- data/.gitignore +3 -0
- data/.rspec +3 -0
- data/.rubocop.yml +1158 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +170 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +66 -0
- data/LICENSE +1 -1
- data/README.md +700 -93
- data/Rakefile +12 -2
- data/eligible.gemspec +14 -16
- data/lib/eligible/api_resource.rb +49 -5
- data/lib/eligible/calculator_deploy_url.rb +7 -0
- data/lib/eligible/claim.rb +22 -6
- data/lib/eligible/coverage.rb +19 -0
- data/lib/eligible/coverage_resource.rb +23 -0
- data/lib/eligible/customer.rb +19 -0
- data/lib/eligible/demographic.rb +6 -35
- data/lib/eligible/eligible_object.rb +12 -15
- data/lib/eligible/encryptor.rb +121 -0
- data/lib/eligible/enrollment.rb +23 -0
- data/lib/eligible/errors/eligible_error.rb +6 -3
- data/lib/eligible/errors/invalid_request_error.rb +4 -0
- data/lib/eligible/icd.rb +16 -0
- data/lib/eligible/json.rb +5 -15
- data/lib/eligible/lockbox.rb +39 -0
- data/lib/eligible/medicare.rb +11 -0
- data/lib/eligible/oauth_token.rb +9 -0
- data/lib/eligible/ocr.rb +15 -0
- data/lib/eligible/original_signature_pdf.rb +45 -0
- data/lib/eligible/payer.rb +17 -0
- data/lib/eligible/payer_mapping.rb +37 -0
- data/lib/eligible/payment.rb +11 -0
- data/lib/eligible/preauth_resource.rb +11 -0
- data/lib/eligible/precert.rb +15 -0
- data/lib/eligible/provider_model.rb +7 -0
- data/lib/eligible/public_key.rb +27 -0
- data/lib/eligible/received_pdf.rb +26 -0
- data/lib/eligible/referral.rb +11 -0
- data/lib/eligible/risk_assessment.rb +15 -0
- data/lib/eligible/session_token.rb +11 -0
- data/lib/eligible/ticket.rb +40 -0
- data/lib/eligible/util.rb +29 -38
- data/lib/eligible/v1_0/action.rb +9 -0
- data/lib/eligible/v1_0/attribute.rb +9 -0
- data/lib/eligible/v1_0/charge.rb +13 -0
- data/lib/eligible/v1_0/claim.rb +25 -0
- data/lib/eligible/v1_0/claim_service_line.rb +9 -0
- data/lib/eligible/v1_0/contract.rb +9 -0
- data/lib/eligible/v1_0/device.rb +9 -0
- data/lib/eligible/v1_0/discount.rb +9 -0
- data/lib/eligible/v1_0/enrollment.rb +17 -0
- data/lib/eligible/v1_0/estimate.rb +29 -0
- data/lib/eligible/v1_0/estimate_service_line.rb +17 -0
- data/lib/eligible/v1_0/fee.rb +21 -0
- data/lib/eligible/v1_0/fee_refund.rb +29 -0
- data/lib/eligible/v1_0/file.rb +17 -0
- data/lib/eligible/v1_0/file_link.rb +13 -0
- data/lib/eligible/v1_0/insurance_company.rb +21 -0
- data/lib/eligible/v1_0/insurance_company_alias.rb +9 -0
- data/lib/eligible/v1_0/insurance_policy.rb +9 -0
- data/lib/eligible/v1_0/patient_question.rb +9 -0
- data/lib/eligible/v1_0/patient_questionnaire.rb +9 -0
- data/lib/eligible/v1_0/patient_record.rb +9 -0
- data/lib/eligible/v1_0/patient_statement.rb +54 -0
- data/lib/eligible/v1_0/patient_statement_service_line.rb +13 -0
- data/lib/eligible/v1_0/payment_report.rb +21 -0
- data/lib/eligible/v1_0/product.rb +9 -0
- data/lib/eligible/v1_0/provider.rb +9 -0
- data/lib/eligible/v1_0/remark.rb +21 -0
- data/lib/eligible/v1_0/reports/accuracy_stats.rb +23 -0
- data/lib/eligible/v1_0/reports/estimate_friction.rb +23 -0
- data/lib/eligible/v1_0/reports/in_scope_distribution.rb +23 -0
- data/lib/eligible/v1_0/rest_api_base.rb +44 -0
- data/lib/eligible/v1_0/rule.rb +13 -0
- data/lib/eligible/v1_0/session.rb +21 -0
- data/lib/eligible/v1_0/transaction.rb +21 -0
- data/lib/eligible/v1_0/treatment.rb +9 -0
- data/lib/eligible/v1_0/value_list.rb +9 -0
- data/lib/eligible/v1_0/value_list_item.rb +9 -0
- data/lib/eligible/v1_0/verification.rb +17 -0
- data/lib/eligible/version.rb +1 -1
- data/lib/eligible/visit_type.rb +11 -0
- data/lib/eligible/x12.rb +8 -0
- data/lib/eligible.rb +320 -110
- metadata +148 -67
- data/CONTRIBUTORS +0 -1
- data/lib/eligible/plan.rb +0 -42
- data/lib/eligible/service.rb +0 -39
- data/test/test_eligible.rb +0 -279
- data/test/test_helper.rb +0 -67
data/Rakefile
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/testtask'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
require 'rspec/core/rake_task'
|
3
5
|
|
4
|
-
|
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 =
|
7
|
+
gem.name = 'eligible'
|
8
8
|
gem.version = Eligible::VERSION
|
9
|
-
gem.authors = [
|
10
|
-
gem.email = [
|
11
|
-
gem.description = 'Eligible is a developer-friendly way to process health care eligibility checks. Learn more at https://
|
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 =
|
14
|
-
gem.license =
|
13
|
+
gem.homepage = 'https://github.com/eligible/eligible-ruby'
|
14
|
+
gem.license = 'MIT'
|
15
15
|
|
16
|
-
gem.files = `git ls-files`.split(
|
17
|
-
|
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.
|
22
|
-
gem.
|
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.
|
27
|
-
gem.
|
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
|
-
|
4
|
+
name.split('::').last
|
5
5
|
end
|
6
6
|
|
7
|
-
def self.
|
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
|
-
|
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)}/
|
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
|
data/lib/eligible/claim.rb
CHANGED
@@ -1,12 +1,28 @@
|
|
1
1
|
module Eligible
|
2
2
|
class Claim < APIResource
|
3
|
-
def self.
|
4
|
-
|
5
|
-
|
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
|
9
|
-
|
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
|
data/lib/eligible/demographic.rb
CHANGED
@@ -1,40 +1,11 @@
|
|
1
1
|
module Eligible
|
2
|
-
class Demographic <
|
3
|
-
|
4
|
-
|
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
|
36
|
-
|
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 =
|
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
|
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.
|
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
|
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? ?
|
17
|
-
|
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
|
data/lib/eligible/icd.rb
ADDED
@@ -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
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
17
|
-
|
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
|
data/lib/eligible/ocr.rb
ADDED
@@ -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
|