redox 0.1.6 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4cf3d701269f03a48d3cd1fe870e2f124aeb705c643c4cd3826ad5b352317276
4
- data.tar.gz: 562cc78ba951b7a58a3365612e09e38f114d7a7ab8ceb80b6e0e1c73b5592c7a
3
+ metadata.gz: af07742de7bb1cc39182903d8246bb8a59dfdb1b660fd629568ee799eef03f90
4
+ data.tar.gz: e088a4fbc1bd383ee5ea0570d6b3933a4d4eccafe1d7a70aa37de49925fe1beb
5
5
  SHA512:
6
- metadata.gz: fdd640751e3226609ac29cf6be385b7ae52487df1e46fa925e8e984cdf1e0458066f14e16a464dfa43d1201f9b0efcd988442c4eea26fd770a9b57d10e5cfd48
7
- data.tar.gz: a339571774b1058f4b646d0d2667bee605def7683c2aa47e152f41bfb3cb51872915a91d120a8b7da5e75c89da0ff0b46ef7292931c3da9cd5941b74fd31b73e
6
+ metadata.gz: efc0fe9382fecb0a4a738796e7dfb89f2b17438e5dbe4730a5de6357dea040f043e8f41130e331780a58263e61416581c024ff4ddcb90c28230f2d2a99bfa9e9
7
+ data.tar.gz: b6ec18283534dde1ca34cf053dad85df88fa9335e08ce4e631e81b0723f00f81a7ff9e72f6e417b3acb5cfa59410238912557cd5ea22890d1120018e5a478b0d
@@ -4,7 +4,47 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [0.1.6] - Unreleased
7
+ ## [1.1.1] - 2020-06-15
8
+ ### Changed
9
+ - Bugfix with patient param in update
10
+
11
+ ## [1.1.0] - 2020-06-15
12
+ ### Changed
13
+ - Moving to Requst classes instead of mixing in logic to models (kept backwards compatibility)
14
+ - Added potential matches to responses and implemented for patient search
15
+
16
+ =======
17
+ ### Added
18
+ - PotentialMatches class
19
+
20
+ ## [1.0.2] - 2019-06-04
21
+ ### Changed
22
+ - Added Extensions to all Redox models
23
+ - Fixed warnings in gemspec
24
+ - Fixed author emails in gemspec
25
+ - Removed packaging of bin and files starting with '.'
26
+ - Added helper file to translate Redox JSON to Hashie model.
27
+
28
+ ### Removed
29
+ - Unused response model.
30
+
31
+ ## [1.0.1] - 2019-05-02
32
+ ### Changed
33
+ - Check for high-level key as symbol
34
+
35
+ ## [1.0.0] - 2019-05-01
36
+ ### Changed
37
+ - How it works
38
+
39
+ ### Added
40
+ - Connection, RedoxClient, Authorization
41
+ - Global configuration block
42
+ - Gems httparty, hashie
43
+
44
+ ### Removed
45
+ - Identifiers, Demographics
46
+
47
+ ## [0.1.6] - 2019-04-22
8
48
  ### Added
9
49
  - Meta model
10
50
  - Response and Authentication classes
@@ -58,7 +98,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
58
98
  ### Added
59
99
  - Initial Release
60
100
 
61
- [Unreleased]: https://github.com/WeInfuse/redox/compare/v0.1.5...HEAD
101
+ [1.1.1]: https://github.com/WeInfuse/redox/compare/v1.1.0...v1.1.1
102
+ [1.1.0]: https://github.com/WeInfuse/redox/compare/v1.0.2...v1.1.0
103
+ [1.0.2]: https://github.com/WeInfuse/redox/compare/v1.0.1...v1.0.2
104
+ [1.0.1]: https://github.com/WeInfuse/redox/compare/v1.0.0...v1.0.1
105
+ [1.0.0]: https://github.com/WeInfuse/redox/compare/v0.1.6...v1.0.0
106
+ [0.1.6]: https://github.com/WeInfuse/redox/compare/v0.1.5...v0.1.6
62
107
  [0.1.5]: https://github.com/WeInfuse/redox/compare/v0.1.4...v0.1.5
63
108
  [0.1.4]: https://github.com/WeInfuse/redox/compare/0.1.3...v0.1.4
64
109
  [0.1.3]: https://github.com/WeInfuse/redox/compare/0.1.2...0.1.3
data/README.md CHANGED
@@ -21,34 +21,90 @@ Or install it yourself as:
21
21
 
22
22
  ## Usage
23
23
 
24
+ ### Setup
25
+
26
+ Make sure you're [configured](#configuration)!
27
+
24
28
  ```ruby
25
- source = {
26
- Name: 'Redox Dev Tools',
27
- ID: ENV['REDOX_SRC_ID']
28
- }
29
+ patient = Redox::Models::Patient.new
30
+ patient.demographics.first_name = 'Joe'
31
+ patient.demographics['LastName'] = 'Joerson'
32
+ patient.add_identifier(type: 'TheType', value: 'x13005')
33
+
34
+ meta = Redox::Models::Meta.new
35
+ meta.set_source(name: 'MySource', id: '123-584')
36
+ meta.add_destination(name: 'TheDest', id: '973-238')
37
+ ```
38
+
39
+ ### Create
40
+
41
+ ```ruby
42
+ response = patient.create(meta: meta)
43
+ ```
44
+
45
+ ### Update
46
+
47
+ ```ruby
48
+ response = patient.update(meta: meta)
49
+ ```
50
+
51
+ ### Search
52
+
53
+ ```ruby
54
+ response = Redox::Models::Patient.query(patient, meta: meta)
55
+ ```
56
+
57
+ ### Response
58
+
59
+ The response object is a base `Redox::Models::Model` class.
60
+
61
+ With the HTTParty response object
62
+ ```ruby
63
+ response.response
64
+ #<HTTParty::Response:0x7fa354c1fbe8>
65
+
66
+ response.response.ok?
67
+ true
68
+ ```
29
69
 
30
- destinations = [
31
- {
32
- Name: 'Redox EMR',
33
- ID: ENV['REDOX_DEST_ID']
70
+ And any `Model` objects that were returned
71
+ ```ruby
72
+ response.patient
73
+ {
74
+ "Identifiers"=> [
75
+ {"IDType"=>"MR", "ID"=>"0000000003"},
76
+ {"ID"=>"e3fedf48-c8bf-4728-845f-cb810001b571", "IDType"=>"EHRID"}
77
+ ],
78
+ "Demographics"=> {
79
+ "Race"=>"Black",
80
+ "SSN"=>"303-03-0003",
81
+ "Nickname"=>"Walt"
82
+ ...
34
83
  }
35
- ]
36
-
37
- redox = Redox::Redox.new(
38
- api_key: ENV['REDOX_KEY'],
39
- secret: ENV['REDOX_SECRET'],
40
- source: source,
41
- destinations: destinations,
42
- test: true
43
- )
44
-
45
- redox.add_patient(
46
- Identifiers: [...],
47
- Demographics: {
48
- FirstName: 'Joe'
49
- ...
84
+ "PCP"=> {
85
+ "NPI"=>nil,
50
86
  }
51
- )
87
+ }
88
+
89
+ response.meta
90
+ {
91
+ "EventDateTime"=>"2019-04-26T20:03:00.304866Z",
92
+ "DataModel"=>"PatientAdmin",
93
+ ...
94
+ "Transmission"=>{"ID"=>797225234},
95
+ "Message"=>{"ID"=>1095117817}
96
+ }
97
+ ```
98
+
99
+ ### Configuration
100
+
101
+ ```ruby
102
+ Redox.configure do |c|
103
+ c.api_key = ENV['REDOX_API_KEY']
104
+ c.secret = ENV['REDOX_SECRET']
105
+ c.api_endpoint = 'http://hello.com' # Defaults to Redox endpoint
106
+ c.token_expiry_padding = 120 # Defaults to 60 seconds
107
+ end
52
108
  ```
53
109
 
54
110
  ## Development
@@ -1,160 +1,85 @@
1
- require 'json'
2
- require 'net/http'
3
- require 'uri'
4
- require 'openssl'
1
+ require 'httparty'
2
+ require 'hashie'
5
3
  require 'redox/version'
6
4
  require 'redox/redox_exception'
5
+ require 'redox/connection'
7
6
  require 'redox/authentication'
8
- require 'redox/response'
9
7
  require 'redox/models/model'
10
8
  require 'redox/models/meta'
11
9
  require 'redox/models/patient'
12
- require 'redox/models/demographics'
13
- require 'redox/models/identifiers'
10
+ require 'redox/models/patient/demographics'
11
+ require 'redox/models/patient/identifier'
12
+ require 'redox/models/patient/insurance'
13
+ require 'redox/models/patient/p_c_p'
14
+ require 'redox/models/potential_matches'
15
+ require 'redox/request/patient_admin'
16
+ require 'redox/request/patient_search'
14
17
 
15
18
  module Redox
16
- # Redox API client
17
- class Redox
18
- DEFAULT_URI = 'https://api.redoxengine.com/'.freeze
19
+ class Configuration
20
+ attr_accessor :api_key, :secret
19
21
 
20
- # Instantiates a new Redox connection object
21
- #
22
- # @param [String] api_key API key for the connection
23
- # @param [String] secret API secret for the connection
24
- # @param [Hash] source source information
25
- # @param [Array<Hash>] destinations list of destinations
26
- # @param [Boolean] test whether to use test mode
27
- # @example
28
- # redox = Redox::Redox.new(
29
- # api_key: ENV['REDOX_KEY'],
30
- # secret: ENV['REDOX_SECRET'],
31
- # source: source,
32
- # destinations: destinations,
33
- # test: true
34
- # )
35
- def initialize(api_key:, secret:, source: nil, destinations: nil, facility_code: nil, test: true, uri: DEFAULT_URI)
36
- @api_key = api_key
37
- @secret = secret
38
- @meta = Models::Meta.new
39
- destinations.each {|dest| @meta.add_destination(dest['Name'], dest['ID']) } if destinations
40
- @meta.source = source if source
41
- @meta.facility_code = facility_code if facility_code
42
- @meta.test = test
43
- @connection = nil
44
- @authentication = nil
45
- @uri = uri
22
+ def initialize
23
+ @api_key = nil
24
+ @secret = nil
46
25
  end
47
26
 
48
- # Send NewPatient message
49
- #
50
- # @param [Hash] patient_params data to send in the Patient JSON object
51
- # @return [Hash] parsed response object
52
- # @example
53
- # Redox::Redox.new(*connection_params).add_patient(
54
- # Identifiers: [],
55
- # Demographics: {
56
- # FirstName: 'Joe'
57
- # }
58
- # )
59
- def add_patient(patient_params, meta = nil)
60
- meta = @meta.merge(Models::Meta.from_h(Models::Patient::ADD[:meta]).merge(meta))
61
- body = meta.to_h.merge(Patient: patient_params)
62
-
63
- return request(
64
- endpoint: Models::Patient::ADD[:endpoint],
65
- body: body.to_json,
66
- model: Models::Patient
67
- )
27
+ def api_endpoint=(endpoint)
28
+ Connection.base_uri(endpoint.freeze)
68
29
  end
69
30
 
70
- # Send PatientUpdate message
71
- #
72
- # @param [Hash] patient_params data to send in the Patient JSON object
73
- # @return [Hash] parsed response object
74
- # @example
75
- # Redox::Redox.new(*connection_params).update_patient(
76
- # Identifiers: [],
77
- # Demographics: {
78
- # FirstName: 'Joe'
79
- # }
80
- # )
81
- def update_patient(patient_params, meta = nil)
82
- meta = @meta.merge(Models::Meta.from_h(Models::Patient::UPDATE[:meta]).merge(meta))
83
- body = meta.to_h.merge(Patient: patient_params)
84
-
85
- return request(
86
- endpoint: Models::Patient::UPDATE[:endpoint],
87
- body: body.to_json,
88
- model: Models::Patient
89
- )
31
+ def api_endpoint
32
+ return Connection.base_uri
90
33
  end
91
34
 
92
- # Send PatientSearch query
93
- #
94
- # @param [Hash] patient_params data to send in the Patient JSON object
95
- # @return [Hash] Redox Patient object
96
- # @example
97
- # Redox::Redox.new(*connection_params).search_patients(
98
- # Identifiers: [],
99
- # Demographics: {
100
- # FirstName: 'Joe'
101
- # }
102
- # )
103
- def search_patients(patient_params, meta = nil)
104
- meta = @meta.merge(Models::Meta.from_h(Models::Patient::SEARCH[:meta]).merge(meta))
105
- body = meta.to_h.merge(Patient: patient_params)
106
-
107
- return request(
108
- endpoint: Models::Patient::SEARCH[:endpoint],
109
- body: body.to_json,
110
- model: Models::Patient
111
- )
35
+ def token_expiry_padding=(time_in_seconds)
36
+ Authentication.token_expiry_padding = time_in_seconds
112
37
  end
113
38
 
114
- private
115
-
116
- attr_reader :api_key, :secret, :meta
117
-
118
- def request(endpoint: , body: , header: {}, model: nil, authorize: true)
119
- header = {
120
- 'Content-Type' => 'application/json'
121
- }.merge(header)
122
-
123
- header = header.merge(authenticate.access_header) if authorize
124
-
125
- request = Net::HTTP::Post.new(endpoint, header)
126
- request.body = body
127
-
128
- return Response.new(connection.request(request), model)
39
+ def token_expiry_padding
40
+ return Authentication.token_expiry_padding
129
41
  end
130
42
 
131
- def authenticate
132
- return @authentication if @authentication
43
+ def to_h
44
+ return {
45
+ api_key: @api_key,
46
+ secret: @secret,
47
+ api_endpoint: api_endpoint,
48
+ token_expiry_padding: token_expiry_padding
49
+ }
50
+ end
133
51
 
134
- redox_response = request(
135
- endpoint: Authentication::ENDPOINT,
136
- body: { apiKey: api_key, secret: secret }.to_json,
137
- model: Authentication,
138
- authorize: false
139
- )
52
+ def from_h(h)
53
+ self.api_key = h[:api_key]
54
+ self.secret = h[:secret]
55
+ self.api_endpoint = h[:api_endpoint]
56
+ self.token_expiry_padding = h[:token_expiry_padding]
140
57
 
141
- if (false == redox_response.success?)
142
- raise RedoxException.from_response(redox_response.http_response, msg: 'Authentication')
143
- end
58
+ return self
59
+ end
60
+ end
144
61
 
145
- @authentication = redox_response.model
62
+ class << self
63
+ def configuration
64
+ @configuration ||= Configuration.new
146
65
  end
147
66
 
148
- def connection
149
- return @connection if @connection
67
+ def configure
68
+ yield(configuration)
69
+ end
70
+ end
150
71
 
151
- uri = URI.parse(@uri)
152
- http = Net::HTTP.new(uri.host, uri.port)
153
- http.use_ssl = true
154
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
155
- http.verify_depth = 5
72
+ # Redox API client
73
+ class RedoxClient
74
+ class << self
75
+ def connection
76
+ Redox.configuration.token_expiry_padding = 60 if Redox.configuration.token_expiry_padding.nil?
77
+ @connection ||= Connection.new
78
+ end
156
79
 
157
- @connection = http
80
+ def release
81
+ @connection = nil
82
+ end
158
83
  end
159
84
  end
160
85
  end
@@ -1,19 +1,77 @@
1
1
  module Redox
2
- class Authentication
3
- ENDPOINT = '/auth/authenticate'.freeze
2
+ class Authentication < Connection
3
+ attr_accessor :response
4
4
 
5
- def initialize(response_body)
6
- @body = response_body
5
+ BASE_ENDPOINT = '/auth'.freeze
6
+
7
+ AUTH_ENDPOINT = "#{BASE_ENDPOINT}/authenticate".freeze
8
+ REFRESH_ENDPOINT = "#{BASE_ENDPOINT}/refreshToken".freeze
9
+
10
+ class << self
11
+ attr_accessor :token_expiry_padding
12
+
13
+ @@token_expiry_padding = 0
14
+ end
15
+
16
+ def initialize
17
+ @response = nil
18
+ end
19
+
20
+ def authenticate
21
+ if (self.expires?)
22
+ if (self.refresh_token)
23
+ request = {
24
+ body: { apiKey: Redox.configuration.api_key, refreshToken: self.refresh_token },
25
+ endpoint: REFRESH_ENDPOINT
26
+ }
27
+ else
28
+ request = {
29
+ body: { apiKey: Redox.configuration.api_key, secret: Redox.configuration.secret },
30
+ endpoint: AUTH_ENDPOINT
31
+ }
32
+ end
33
+
34
+ response = self.request(**request, auth: false)
35
+
36
+ if (false == response.ok?)
37
+ @response = nil
38
+ raise RedoxException.from_response(response, msg: 'Authentication')
39
+ else
40
+ @response = response
41
+ end
42
+ end
43
+
44
+ return self
7
45
  end
8
46
 
9
47
  def access_token
10
- return @body['accessToken']
48
+ return @response['accessToken'] if @response
49
+ end
50
+
51
+ def expiry
52
+ return @response['expires'] if @response
53
+ end
54
+
55
+ def refresh_token
56
+ return @response['refreshToken'] if @response
57
+ end
58
+
59
+ def expires?(seconds_from_now = Authentication.token_expiry_padding)
60
+ if (self.expiry)
61
+ return DateTime.strptime(self.expiry, Models::Meta::FROM_DATETIME_FORMAT).to_time.utc <= (Time.now + seconds_from_now).utc
62
+ else
63
+ return true
64
+ end
11
65
  end
12
66
 
13
67
  def access_header
14
68
  return {
15
- 'Authorization' => "Bearer #{access_token}",
69
+ 'Authorization' => "Bearer #{self.access_token}",
16
70
  }
17
71
  end
72
+
73
+ def expire!
74
+ @response = nil
75
+ end
18
76
  end
19
77
  end
@@ -0,0 +1,28 @@
1
+ module Redox
2
+ class Connection
3
+ DEFAULT_ENDPOINT = '/endpoint'.freeze
4
+
5
+ include HTTParty
6
+
7
+ base_uri 'https://api.redoxengine.com/'.freeze
8
+
9
+ headers 'Content-Type' => 'application/json'
10
+
11
+ format :json
12
+
13
+ def request(endpoint: DEFAULT_ENDPOINT, body: nil, headers: {}, auth: true)
14
+ body = body.to_json if body.is_a?(Hash)
15
+ headers = auth_header.merge(headers) if auth
16
+
17
+ self.class.post(endpoint, body: body, headers: headers)
18
+ end
19
+
20
+ private
21
+
22
+ def auth_header
23
+ @auth ||= Authentication.new
24
+
25
+ return @auth.authenticate.access_header
26
+ end
27
+ end
28
+ end
@@ -1,79 +1,45 @@
1
1
  module Redox
2
2
  module Models
3
3
  class Meta < Model
4
- KEY = 'Meta'.freeze
5
-
6
- DEFAULT = -> () {
7
- return {
8
- KEY => {
9
- 'DataModel' => nil,
10
- 'EventType' => nil,
11
- 'EventDateTime' => Time.now.strftime("%Y-%m-%dT%H:%M:%S.%6NZ"),
12
- 'Test' => true,
13
- 'Source' => {},
14
- 'Destinations' => [],
15
- 'FacilityCode' => nil
16
- }
17
- }
18
- }
19
-
20
- def initialize(data = DEFAULT.call)
21
- super(data)
22
- end
23
-
24
- def add_destination(name, id)
25
- self.inner['Destinations'] << Meta.build_subscription(name, id)
4
+ TO_DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%6NZ'.freeze
5
+ FROM_DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%N%Z'.freeze
6
+
7
+ property :DataModel, from: :data_model, required: false
8
+ property :EventType, from: :event_type, required: false
9
+ property :EventDateTime, from: :event_date_time, default: ->() { Time.now.utc.strftime(TO_DATETIME_FORMAT) }
10
+ property :Test, from: :test, default: true
11
+ property :Source, from: :source, required: false
12
+ property :Destinations, from: :destinations, required: false
13
+ property :FacilityCode, from: :facility_code, required: false
14
+
15
+ alias_method :data_model, :DataModel
16
+ alias_method :event_type, :EventType
17
+ alias_method :event_date_time, :EventDateTime
18
+ alias_method :test, :Test
19
+ alias_method :source, :Source
20
+ alias_method :destinations, :Destinations
21
+ alias_method :facility_code, :FacilityCode
22
+
23
+ def add_destination(name: , id: )
24
+ self[:Destinations] ||= []
25
+ self[:Destinations] << Meta.build_subscription(name: name, id: id)
26
26
 
27
27
  return self
28
28
  end
29
29
 
30
- def set_source(name, id)
31
- self.source = Meta.build_subscription(name, id)
32
-
33
- return self
34
- end
35
-
36
- def source=(source)
37
- self.inner['Source'] = source
38
- end
39
-
40
- def facility_code=(facility_code)
41
- self.inner['FacilityCode'] = facility_code.to_s
42
- end
43
-
44
- def test=(test)
45
- self.inner['Test'] = (true == test)
46
- end
47
-
48
- def merge(other)
49
- if (other.is_a?(Hash))
50
- if (other.include?(KEY))
51
- self.inner.merge!(other[KEY])
52
- else
53
- self.inner.merge!(other)
54
- end
55
- elsif (other.is_a?(self.class))
56
- self.inner.merge!(other.inner.select {|k,v| !v.nil?})
57
- end
30
+ def set_source(name: , id: )
31
+ self[:Source] = Meta.build_subscription(name: name, id: id)
58
32
 
59
33
  return self
60
34
  end
61
35
 
62
- def to_h
63
- return JSON.parse(@data.to_json)
64
- end
65
-
66
36
  class << self
67
- def build_subscription(name, id)
37
+ def build_subscription(name: , id:)
68
38
  return {
69
39
  'ID' => id,
70
40
  'Name' => name
71
41
  }
72
42
  end
73
-
74
- def from_h(hash)
75
- return Meta.new.merge(hash)
76
- end
77
43
  end
78
44
  end
79
45
  end
@@ -1,27 +1,60 @@
1
1
  module Redox
2
2
  module Models
3
- class Model
4
- KEY = nil
3
+ class Model < Hashie::Trash
4
+ include Hashie::Extensions::IgnoreUndeclared
5
+ include Hashie::Extensions::IndifferentAccess
5
6
 
6
- def initialize(data)
7
- @data = data.freeze
7
+ property :Meta, from: :meta, required: false
8
+ property :Patient, from: :patient, required: false
9
+ property :PotentialMatches, from: :potential_matches, required: false
10
+ property :Extensions, from: :extensions, required: false
11
+ property :response, required: false
12
+
13
+ alias_method :potential_matches, :PotentialMatches
14
+ alias_method :patient, :Patient
15
+ alias_method :meta, :Meta
16
+
17
+ def initialize(data = {})
18
+ if data.is_a?(Hash)
19
+ if data.include?(key)
20
+ data = data[key]
21
+ elsif data.include?(key.to_sym)
22
+ data = data[key.to_sym]
23
+ end
24
+ end
25
+
26
+ super(data)
8
27
  end
9
28
 
10
- def valid?
11
- return @data.is_a?(Hash) && @data.include?(self.class::KEY)
29
+ def to_h
30
+ return { key => super.to_h }
12
31
  end
13
32
 
14
- def raw
15
- return self.inner(key: nil)
33
+ def to_json
34
+ return self.to_h.to_json
16
35
  end
17
36
 
18
- def inner(key: self.class::KEY)
19
- if key.nil?
20
- return @data
21
- else
22
- return @data[key]
37
+ class << self
38
+ def from_response(response)
39
+ model = Model.new
40
+ model.response = response
41
+
42
+ %w[Meta Patient PotentialMatches].each do |k|
43
+ begin
44
+ model.send("#{k}=", Module.const_get("Redox::Models::#{k}").new(response[k])) if response[k]
45
+ rescue
46
+ end
47
+ end
48
+
49
+ return model
23
50
  end
24
51
  end
52
+
53
+ private
54
+
55
+ def key
56
+ return self.class.to_s.split('::').last.to_s
57
+ end
25
58
  end
26
59
  end
27
60
  end
@@ -1,40 +1,50 @@
1
1
  module Redox
2
2
  module Models
3
3
  class Patient < Model
4
- attr_reader :demographics, :identifiers
5
-
6
- KEY = 'Patient'.freeze
7
-
8
- SEARCH = {
9
- meta: {
10
- 'DataModel' => 'PatientSearch',
11
- 'EventType' => 'Query'
12
- },
13
- endpoint: '/query'
14
- }
15
-
16
- ADD = {
17
- meta: {
18
- 'DataModel' => 'PatientAdmin',
19
- 'EventType' => 'NewPatient'
20
- },
21
- endpoint: '/endpoint'
22
- }
23
-
24
- UPDATE = {
25
- meta: {
26
- 'DataModel' => 'PatientAdmin',
27
- 'EventType' => 'PatientUpdate'
28
- },
29
- endpoint: '/endpoint'
30
- }
31
-
32
- def initialize(data)
33
- super(data)
34
-
35
- if (self.valid?)
36
- @demographics = Demographics.new(self.inner)
37
- @identifiers = Identifiers.new(self.inner)
4
+ property :Identifiers, from: :identifiers, required: false, default: []
5
+ property :Insurances, from: :insurances, required: false, default: []
6
+ property :Demographics, from: :demographics, required: false
7
+ property :PCP, from: :primary_care_provider, required: false
8
+
9
+ alias_method :identifiers, :Identifiers
10
+ alias_method :insurances, :Insurances
11
+
12
+ def demographics
13
+ self[:Demographics] = Demographics.new(self[:Demographics]) unless self[:Demographics].is_a?(Redox::Models::Demographics)
14
+ self[:Demographics] ||= Demographics.new
15
+ end
16
+
17
+ def insurances
18
+ self[:Insurances] = self[:Insurances].map {|ins| ins.is_a?(Redox::Models::Insurance) ? ins : Insurance.new(ins) }
19
+ end
20
+
21
+ def primary_care_provider
22
+ self[:PCP] ||= PCP.new
23
+ end
24
+
25
+ def add_identifier(type: , value: )
26
+ self[:Identifiers] << Identifier.new({'ID' => value, 'IDType' => type})
27
+
28
+ return self
29
+ end
30
+
31
+ def add_insurance(data = {})
32
+ self[:Insurances] << Insurance.new(data)
33
+
34
+ return self
35
+ end
36
+
37
+ def update(meta: Meta.new)
38
+ Redox::Request::PatientAdmin.update(patient: self, meta: meta)
39
+ end
40
+
41
+ def create(meta: Meta.new)
42
+ Redox::Request::PatientAdmin.create(patient: self, meta: meta)
43
+ end
44
+
45
+ class << self
46
+ def query(params, meta: Meta.new)
47
+ return Redox::Request::PatientSearch.query(params, meta: meta)
38
48
  end
39
49
  end
40
50
  end
@@ -0,0 +1,34 @@
1
+ module Redox
2
+ module Models
3
+ class Demographics < Model
4
+ property :FirstName, required: false, from: :first_name
5
+ property :MiddleName, required: false, from: :middle_name
6
+ property :LastName, required: false, from: :last_name
7
+ property :DOB, required: false, from: :dob
8
+ property :SSN, required: false, from: :ssn
9
+ property :Sex, required: false, from: :sex
10
+ property :Race, required: false, from: :race
11
+ property :IsHispanic, required: false
12
+ property :MaritalStatus, required: false, from: :martial_status
13
+ property :IsDeceased, required: false
14
+ property :DeathDateTime, required: false
15
+ property :Language, required: false, from: :language
16
+ property :EmailAddresses, required: false, default: []
17
+ property :Citizenship, required: false, default: []
18
+ property :Address, required: false, default: {}
19
+ property :PhoneNumber, required: false, default: {}
20
+
21
+ alias_method :first_name, :FirstName
22
+ alias_method :middle_name, :MiddleName
23
+ alias_method :last_name, :LastName
24
+ alias_method :dob, :DOB
25
+ alias_method :ssn, :SSN
26
+ alias_method :sex, :Sex
27
+ alias_method :race, :Race
28
+ alias_method :martial_status, :MaritalStatus
29
+ alias_method :language, :Language
30
+ alias_method :address, :Address
31
+ alias_method :phone_number, :PhoneNumber
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ module Redox
2
+ module Models
3
+ class Identifier < Model
4
+ property :ID, from: :id
5
+ property :IDType, from: :id_type
6
+
7
+ alias_method :id, :ID
8
+ alias_method :id_type, :IDType
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ module Redox
2
+ module Models
3
+ class Insurance < Model
4
+ property :Plan, required: false, from: :plan, default: {}
5
+ property :MemberNumber, required: false, from: :member_number
6
+ property :Company, required: false, from: :company, default: {}
7
+ property :GroupNumber, required: false, from: :group_number
8
+ property :GroupName, required: false, from: :group_name
9
+ property :EffectiveDate, required: false, from: :effective_date
10
+ property :ExpirationDate, required: false, from: :expiration_date
11
+ property :PolicyNumber, required: false, from: :policy_number
12
+ property :AgreementType, required: false, from: :agreement_type
13
+ property :CoverageType, required: false, from: :coverage_type
14
+ property :Insured, required: false, from: :insured, default: {}
15
+
16
+ alias_method :plan, :Plan
17
+ alias_method :member_number, :MemberNumber
18
+ alias_method :company, :Company
19
+ alias_method :group_number, :GroupNumber
20
+ alias_method :group_name, :GroupName
21
+ alias_method :effective_date, :EffectiveDate
22
+ alias_method :expiration_date, :ExpirationDate
23
+ alias_method :policy_number, :PolicyNumber
24
+ alias_method :agreement_type, :AgreementType
25
+ alias_method :coverage_type, :CoverageType
26
+ alias_method :insured, :Insured
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ module Redox
2
+ module Models
3
+ class PCP < Model
4
+ property :NPI, from: :npi
5
+ property :FirstName, required: false, from: :first_name
6
+ property :LastName, required: false, from: :last_name
7
+
8
+ alias_method :npi, :NPI
9
+ alias_method :first_name, :FirstName
10
+ alias_method :last_name, :LastName
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Redox
2
+ module Models
3
+ class PotentialMatches < Array
4
+ def initialize(data = [])
5
+ if false == data.nil?
6
+ super(data.map {|patient| Redox::Models::Patient.new(patient) })
7
+ else
8
+ super([])
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Redox
2
+ module Request
3
+ class PatientAdmin
4
+ CREATE_META = Redox::Models::Meta.new(EventType: 'NewPatient', DataModel: 'PatientAdmin')
5
+ UPDATE_META = Redox::Models::Meta.new(EventType: 'PatientUpdate', DataModel: 'PatientAdmin')
6
+
7
+ def self.create(patient: , meta: Redox::Models::Meta.new)
8
+ meta = CREATE_META.merge(meta)
9
+ return Redox::Models::Model.from_response((RedoxClient.connection.request(body: Redox::Request::PatientAdmin.build_body(patient, meta))))
10
+ end
11
+
12
+ def self.update(patient: , meta: Redox::Models::Meta.new)
13
+ meta = UPDATE_META.merge(meta)
14
+ return Redox::Models::Model.from_response((RedoxClient.connection.request(body: Redox::Request::PatientAdmin.build_body(patient, meta))))
15
+ end
16
+
17
+ def self.build_body(params, meta)
18
+ meta = Redox::Models::Meta.new.merge(meta)
19
+
20
+ return meta.to_h.merge(params.to_h)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ module Redox
2
+ module Request
3
+ class PatientSearch
4
+ QUERY_ENDPOINT = '/query'.freeze
5
+ QUERY_META = Redox::Models::Meta.new(EventType: 'Query', DataModel: 'PatientSearch')
6
+
7
+ def self.query(params, meta: Redox::Models::Meta.new)
8
+ meta = QUERY_META.merge(meta)
9
+ return Redox::Models::Model.from_response((RedoxClient.connection.request(endpoint: QUERY_ENDPOINT, body: Redox::Request::PatientAdmin.build_body(params, meta))))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Redox
2
- VERSION = '0.1.6'.freeze
2
+ VERSION = '1.1.1'.freeze
3
3
  end
@@ -5,8 +5,8 @@ require 'redox/version'
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'redox'
7
7
  spec.version = Redox::VERSION
8
- spec.authors = ['Alexander Clark']
9
- spec.email = ['sasha.jackal@gmail.com']
8
+ spec.authors = ['Alexander Clark', 'Mike Crockett']
9
+ spec.email = ['alexander.clark@weinfuse.com', 'mike.crockett@weinfuse.com']
10
10
 
11
11
  spec.summary = 'Ruby wrapper for the Redox Engine API'
12
12
  spec.homepage = 'https://github.com/WeInfuse/redox'
@@ -20,14 +20,18 @@ Gem::Specification.new do |spec|
20
20
  end
21
21
 
22
22
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
- f.match(%r{^(test|spec|features)/})
23
+ f.match?(%r{^(test|spec|features|bin|helpers|)/}) || f.match?(%r{^(\.[[:alnum:]]+)})
24
24
  end
25
+
25
26
  spec.bindir = 'exe'
26
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
28
  spec.require_paths = ['lib']
29
+ spec.licenses = ['MIT']
28
30
 
29
- spec.add_development_dependency 'bundler'
30
- spec.add_development_dependency 'byebug'
31
+ spec.add_dependency 'httparty', '~> 0.17'
32
+ spec.add_dependency 'hashie', '~> 3.5'
33
+ spec.add_development_dependency 'bundler', '>=1', '<3'
34
+ spec.add_development_dependency 'byebug', '~> 11'
31
35
  spec.add_development_dependency 'minitest', '~> 5.0'
32
36
  spec.add_development_dependency 'rake', '~> 10.0'
33
37
  spec.add_development_dependency 'webmock', '~> 3.1'
metadata CHANGED
@@ -1,43 +1,78 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Clark
8
+ - Mike Crockett
8
9
  autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2019-04-22 00:00:00.000000000 Z
12
+ date: 2020-06-16 00:00:00.000000000 Z
12
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '0.17'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '0.17'
28
+ - !ruby/object:Gem::Dependency
29
+ name: hashie
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '3.5'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '3.5'
13
42
  - !ruby/object:Gem::Dependency
14
43
  name: bundler
15
44
  requirement: !ruby/object:Gem::Requirement
16
45
  requirements:
17
46
  - - ">="
18
47
  - !ruby/object:Gem::Version
19
- version: '0'
48
+ version: '1'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '3'
20
52
  type: :development
21
53
  prerelease: false
22
54
  version_requirements: !ruby/object:Gem::Requirement
23
55
  requirements:
24
56
  - - ">="
25
57
  - !ruby/object:Gem::Version
26
- version: '0'
58
+ version: '1'
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '3'
27
62
  - !ruby/object:Gem::Dependency
28
63
  name: byebug
29
64
  requirement: !ruby/object:Gem::Requirement
30
65
  requirements:
31
- - - ">="
66
+ - - "~>"
32
67
  - !ruby/object:Gem::Version
33
- version: '0'
68
+ version: '11'
34
69
  type: :development
35
70
  prerelease: false
36
71
  version_requirements: !ruby/object:Gem::Requirement
37
72
  requirements:
38
- - - ">="
73
+ - - "~>"
39
74
  - !ruby/object:Gem::Version
40
- version: '0'
75
+ version: '11'
41
76
  - !ruby/object:Gem::Dependency
42
77
  name: minitest
43
78
  requirement: !ruby/object:Gem::Requirement
@@ -96,34 +131,36 @@ dependencies:
96
131
  version: '0.9'
97
132
  description:
98
133
  email:
99
- - sasha.jackal@gmail.com
134
+ - alexander.clark@weinfuse.com
135
+ - mike.crockett@weinfuse.com
100
136
  executables: []
101
137
  extensions: []
102
138
  extra_rdoc_files: []
103
139
  files:
104
- - ".circleci/config.yml"
105
- - ".editorconfig"
106
- - ".gitignore"
107
140
  - CHANGELOG.md
108
141
  - CODE_OF_CONDUCT.md
109
142
  - Gemfile
110
143
  - README.md
111
144
  - Rakefile
112
- - bin/console
113
- - bin/setup
114
145
  - lib/redox.rb
115
146
  - lib/redox/authentication.rb
116
- - lib/redox/models/demographics.rb
117
- - lib/redox/models/identifiers.rb
147
+ - lib/redox/connection.rb
118
148
  - lib/redox/models/meta.rb
119
149
  - lib/redox/models/model.rb
120
150
  - lib/redox/models/patient.rb
151
+ - lib/redox/models/patient/demographics.rb
152
+ - lib/redox/models/patient/identifier.rb
153
+ - lib/redox/models/patient/insurance.rb
154
+ - lib/redox/models/patient/p_c_p.rb
155
+ - lib/redox/models/potential_matches.rb
121
156
  - lib/redox/redox_exception.rb
122
- - lib/redox/response.rb
157
+ - lib/redox/request/patient_admin.rb
158
+ - lib/redox/request/patient_search.rb
123
159
  - lib/redox/version.rb
124
160
  - redox.gemspec
125
161
  homepage: https://github.com/WeInfuse/redox
126
- licenses: []
162
+ licenses:
163
+ - MIT
127
164
  metadata:
128
165
  allowed_push_host: https://rubygems.org
129
166
  post_install_message:
@@ -1,23 +0,0 @@
1
- version: 2
2
- jobs:
3
- build:
4
- working_directory: ~/weinfuse_api
5
- docker:
6
- - image: circleci/ruby:2.4
7
- steps:
8
- - checkout
9
- - type: restore-cache
10
- key: redox_{{ checksum "Gemfile.lock" }}
11
- key: redox
12
- - run: bundle install --path vendor/bundle --jobs 20 --retry 5
13
- - type: cache-save
14
- key: redox_{{ checksum "Gemfile.lock" }}
15
- key: redox
16
- paths:
17
- - vendor/bundle
18
- - type: shell
19
- command: |
20
- bundle exec rake test
21
- - type: store_test_results
22
- path: /tmp/test-results
23
-
@@ -1,10 +0,0 @@
1
- root=true
2
-
3
- [*]
4
- indent_style = space
5
- indent_size = 2
6
- end_of_line = lf
7
- charset = utf-8
8
- trim_trailing_whitespace = true
9
- insert_final_newline = true
10
-
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- /redox*.gem
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'bundler/setup'
4
- require 'json'
5
- require 'redox'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,7 +0,0 @@
1
- module Redox
2
- module Models
3
- class Demographics < Model
4
- KEY = 'Demographics'.freeze
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- module Redox
2
- module Models
3
- class Identifiers < Model
4
- KEY = 'Identifiers'.freeze
5
- end
6
- end
7
- end
@@ -1,14 +0,0 @@
1
- module Redox
2
- class Response
3
- attr_reader :model, :http_response
4
-
5
- def initialize(response, model_class = nil)
6
- @http_response = response
7
- @model = model_class.new(JSON.parse(response.body)) if !model_class.nil? && self.success?
8
- end
9
-
10
- def success?
11
- return @http_response.is_a?(Net::HTTPOK)
12
- end
13
- end
14
- end