fhir_client 3.0.7 → 3.1.0
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 +4 -4
- data/.travis.yml +1 -1
- data/README.md +9 -0
- data/fhir_client.gemspec +1 -1
- data/lib/fhir_client.rb +1 -0
- data/lib/fhir_client/client.rb +46 -12
- data/lib/fhir_client/ext/model.rb +15 -15
- data/lib/fhir_client/ext/reference.rb +2 -2
- data/lib/fhir_client/resource_address.rb +70 -33
- data/lib/fhir_client/return_preferences.rb +8 -0
- data/lib/fhir_client/sections/crud.rb +95 -34
- data/lib/fhir_client/sections/feed.rb +1 -1
- data/lib/fhir_client/sections/history.rb +1 -1
- data/lib/fhir_client/sections/operations.rb +25 -10
- data/lib/fhir_client/sections/search.rb +6 -6
- data/lib/fhir_client/sections/transactions.rb +4 -2
- data/lib/fhir_client/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0652e0b08ba87a20e874e73ea62fca47611c6d3631681f6d9b6316e1a3e08528
|
4
|
+
data.tar.gz: 3980e902188d8fa627f8c9a8c72fb010ecffbc3c8fb056bd5a3a78cc50aebe05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2f783cbbd33c2a437d6c45ea01ce3ba7aa60ca277d18b2dcb7dd0e09fcd037e854f1b6fd1be3d6487c296ef129280d3bbca2cbe2aa85fa5e0ac6d7e86bdfbaf
|
7
|
+
data.tar.gz: eb13cd8355a0116d1b30043231881a7748fa164c39a5b2075f2d5517f99f353934d4b14121e056b0e0117077e3c070a28b5edf3f256792993e5614499c824956
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -84,6 +84,15 @@ patient = FHIR::DSTU2::Patient.read('example')
|
|
84
84
|
patient = client.read(FHIR::DSTU2::Patient, 'example').resource
|
85
85
|
```
|
86
86
|
|
87
|
+
### Configuration
|
88
|
+
|
89
|
+
You can specify additional properties for the `client`:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
client.additional_headers = {Prefer: 'return=representation'}
|
93
|
+
client.proxy = 'https://your-proxy.com/'
|
94
|
+
```
|
95
|
+
|
87
96
|
### CRUD Examples
|
88
97
|
```ruby
|
89
98
|
# read an existing patient with id "example"
|
data/fhir_client.gemspec
CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_dependency 'rest-client', '~> 2.0'
|
31
31
|
spec.add_dependency 'tilt', '>= 1.1'
|
32
32
|
|
33
|
-
spec.add_development_dependency 'bundler', '~>
|
33
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
34
34
|
spec.add_development_dependency 'rake', '~> 10.0'
|
35
35
|
spec.add_development_dependency 'pry'
|
36
36
|
spec.add_development_dependency 'webmock'
|
data/lib/fhir_client.rb
CHANGED
@@ -13,6 +13,7 @@ end
|
|
13
13
|
require_relative 'fhir_client/client'
|
14
14
|
require_relative 'fhir_client/resource_address'
|
15
15
|
require_relative 'fhir_client/resource_format'
|
16
|
+
require_relative 'fhir_client/return_preferences'
|
16
17
|
require_relative 'fhir_client/patch_format'
|
17
18
|
require_relative 'fhir_client/client_exception'
|
18
19
|
require_relative 'fhir_client/version'
|
data/lib/fhir_client/client.rb
CHANGED
@@ -23,6 +23,12 @@ module FHIR
|
|
23
23
|
attr_accessor :fhir_version
|
24
24
|
attr_accessor :cached_capability_statement
|
25
25
|
attr_accessor :additional_headers
|
26
|
+
attr_accessor :proxy
|
27
|
+
attr_accessor :exception_class
|
28
|
+
|
29
|
+
attr_accessor :use_accept_header
|
30
|
+
attr_accessor :use_accept_charset
|
31
|
+
attr_accessor :use_return_preference
|
26
32
|
|
27
33
|
# Call method to initialize FHIR client. This method must be invoked
|
28
34
|
# with a valid base server URL prior to using the client.
|
@@ -31,12 +37,19 @@ module FHIR
|
|
31
37
|
# @param default_format Default Format Mime type
|
32
38
|
# @return
|
33
39
|
#
|
34
|
-
def initialize(base_service_url, default_format: FHIR::Formats::ResourceFormat::RESOURCE_XML)
|
40
|
+
def initialize(base_service_url, default_format: FHIR::Formats::ResourceFormat::RESOURCE_XML, proxy: nil)
|
35
41
|
@base_service_url = base_service_url
|
36
42
|
FHIR.logger.info "Initializing client with #{@base_service_url}"
|
37
43
|
@use_format_param = false
|
44
|
+
@use_accept_header = true
|
45
|
+
@use_accept_charset = true
|
38
46
|
@default_format = default_format
|
39
47
|
@fhir_version = :stu3
|
48
|
+
@use_return_preference = false
|
49
|
+
@return_preference = FHIR::Formats::ReturnPreferences::REPRESENTATION
|
50
|
+
@exception_class = ClientException
|
51
|
+
@proxy = proxy
|
52
|
+
|
40
53
|
set_no_auth
|
41
54
|
end
|
42
55
|
|
@@ -74,6 +87,20 @@ module FHIR
|
|
74
87
|
end
|
75
88
|
end
|
76
89
|
|
90
|
+
#
|
91
|
+
# Instructs the client to specify the minimal Prefer Header where applicable
|
92
|
+
def use_minimal_preference
|
93
|
+
@use_return_preference = true
|
94
|
+
@return_preference = FHIR::Formats::ReturnPreferences::MINIMAL
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Instructs the client to specify the representation Prefer Header where applicable
|
99
|
+
def use_representation_preference
|
100
|
+
@use_return_preference = true
|
101
|
+
@return_preference = FHIR::Formats::ReturnPreferences::REPRESENTATION
|
102
|
+
end
|
103
|
+
|
77
104
|
def versioned_resource_class(klass)
|
78
105
|
if @fhir_version == :stu3
|
79
106
|
FHIR.const_get(klass)
|
@@ -102,6 +129,8 @@ module FHIR
|
|
102
129
|
@use_basic_auth = false
|
103
130
|
@security_headers = {}
|
104
131
|
@client = RestClient
|
132
|
+
@client.proxy = proxy unless proxy.nil?
|
133
|
+
@client
|
105
134
|
end
|
106
135
|
|
107
136
|
# Set the client to use HTTP Basic Authentication
|
@@ -113,6 +142,8 @@ module FHIR
|
|
113
142
|
@use_oauth2_auth = false
|
114
143
|
@use_basic_auth = true
|
115
144
|
@client = RestClient
|
145
|
+
@client.proxy = proxy unless proxy.nil?
|
146
|
+
@client
|
116
147
|
end
|
117
148
|
|
118
149
|
# Set the client to use Bearer Token Authentication
|
@@ -123,6 +154,8 @@ module FHIR
|
|
123
154
|
@use_oauth2_auth = false
|
124
155
|
@use_basic_auth = true
|
125
156
|
@client = RestClient
|
157
|
+
@client.proxy = proxy unless proxy.nil?
|
158
|
+
@client
|
126
159
|
end
|
127
160
|
|
128
161
|
# Set the client to use OpenID Connect OAuth2 Authentication
|
@@ -142,6 +175,7 @@ module FHIR
|
|
142
175
|
raise_errors: true
|
143
176
|
}
|
144
177
|
client = OAuth2::Client.new(client, secret, options)
|
178
|
+
client.connection.proxy(proxy) unless proxy.nil?
|
145
179
|
@client = client.client_credentials.get_token
|
146
180
|
end
|
147
181
|
|
@@ -248,7 +282,7 @@ module FHIR
|
|
248
282
|
@default_format = nil
|
249
283
|
|
250
284
|
formats.each do |frmt|
|
251
|
-
reply = get 'metadata', fhir_headers(
|
285
|
+
reply = get 'metadata', fhir_headers({accept: "#{frmt}"})
|
252
286
|
next unless reply.code == 200
|
253
287
|
begin
|
254
288
|
@cached_capability_statement = parse_reply(FHIR::CapabilityStatement, frmt, reply)
|
@@ -272,7 +306,7 @@ module FHIR
|
|
272
306
|
end
|
273
307
|
|
274
308
|
def resource_url(options)
|
275
|
-
FHIR::ResourceAddress.
|
309
|
+
FHIR::ResourceAddress.resource_url(options, @use_format_param)
|
276
310
|
end
|
277
311
|
|
278
312
|
def full_resource_url(options)
|
@@ -280,9 +314,7 @@ module FHIR
|
|
280
314
|
end
|
281
315
|
|
282
316
|
def fhir_headers(options = {})
|
283
|
-
|
284
|
-
|
285
|
-
FHIR::ResourceAddress.new.fhir_headers(options, @use_format_param)
|
317
|
+
FHIR::ResourceAddress.fhir_headers(options, additional_headers, @default_format, @use_accept_header, @use_accept_charset)
|
286
318
|
end
|
287
319
|
|
288
320
|
def parse_reply(klass, format, response)
|
@@ -348,8 +380,10 @@ module FHIR
|
|
348
380
|
# Extract the request payload in the specified format, defaults to XML
|
349
381
|
def request_payload(resource, headers)
|
350
382
|
if headers
|
351
|
-
format_specified = headers[
|
352
|
-
if format_specified.
|
383
|
+
format_specified = headers['Content-Type']
|
384
|
+
if format_specified.nil?
|
385
|
+
resource.to_xml
|
386
|
+
elsif format_specified.downcase.include?('xml')
|
353
387
|
resource.to_xml
|
354
388
|
elsif format_specified.downcase.include?('json')
|
355
389
|
resource.to_json
|
@@ -383,7 +417,7 @@ module FHIR
|
|
383
417
|
|
384
418
|
def clean_headers(headers)
|
385
419
|
headers.delete_if { |k, v| (k.nil? || v.nil?) }
|
386
|
-
|
420
|
+
FHIR::ResourceAddress.convert_symbol_headers(headers)
|
387
421
|
end
|
388
422
|
|
389
423
|
def scrubbed_response_headers(result)
|
@@ -393,10 +427,10 @@ module FHIR
|
|
393
427
|
end
|
394
428
|
end
|
395
429
|
|
396
|
-
def get(path, headers)
|
430
|
+
def get(path, headers = {})
|
397
431
|
url = Addressable::URI.parse(build_url(path)).to_s
|
398
432
|
FHIR.logger.info "GETTING: #{url}"
|
399
|
-
headers = clean_headers(headers)
|
433
|
+
headers = clean_headers(headers) unless headers.empty?
|
400
434
|
if @use_oauth2_auth
|
401
435
|
# @client.refresh!
|
402
436
|
begin
|
@@ -572,7 +606,7 @@ module FHIR
|
|
572
606
|
url = URI(build_url(path)).to_s
|
573
607
|
FHIR.logger.info "PATCHING: #{url}"
|
574
608
|
headers = clean_headers(headers)
|
575
|
-
payload = request_patch_payload(patchset, headers['
|
609
|
+
payload = request_patch_payload(patchset, headers['Content-Type'])
|
576
610
|
if @use_oauth2_auth
|
577
611
|
# @client.refresh!
|
578
612
|
begin
|
@@ -50,7 +50,7 @@ module FHIR
|
|
50
50
|
|
51
51
|
private
|
52
52
|
def handle_response(response)
|
53
|
-
raise
|
53
|
+
raise client.exception_class.new "Server returned #{response.code}.", response if response.code.between?(400, 599)
|
54
54
|
response.resource
|
55
55
|
end
|
56
56
|
end
|
@@ -66,59 +66,59 @@ module FHIR
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def read(id, client = self.client)
|
69
|
-
handle_response client.read(self, id)
|
69
|
+
handle_response client.exception_class, client.read(self, id)
|
70
70
|
end
|
71
71
|
|
72
72
|
def read_with_summary(id, summary, client = self.client)
|
73
|
-
handle_response client.read(self, id, client.default_format, summary)
|
73
|
+
handle_response client.exception_class, client.read(self, id, client.default_format, summary)
|
74
74
|
end
|
75
75
|
|
76
76
|
def vread(id, version_id, client = self.client)
|
77
|
-
handle_response client.vread(self, id, version_id)
|
77
|
+
handle_response client.exception_class, client.vread(self, id, version_id)
|
78
78
|
end
|
79
79
|
|
80
80
|
def resource_history(client = self.client)
|
81
|
-
handle_response client.resource_history(self)
|
81
|
+
handle_response client.exception_class, client.resource_history(self)
|
82
82
|
end
|
83
83
|
|
84
84
|
def resource_history_as_of(last_update, client = self.client)
|
85
|
-
handle_response client.resource_history_as_of(self, last_update)
|
85
|
+
handle_response client.exception_class, client.resource_history_as_of(self, last_update)
|
86
86
|
end
|
87
87
|
|
88
88
|
def resource_instance_history(id, client = self.client)
|
89
|
-
handle_response client.resource_instance_history(self, id)
|
89
|
+
handle_response client.exception_class, client.resource_instance_history(self, id)
|
90
90
|
end
|
91
91
|
|
92
92
|
def resource_instance_history_as_of(id, last_update, client = self.client)
|
93
|
-
handle_response client.resource_instance_history_as_of(self, id, last_update)
|
93
|
+
handle_response client.exception_class, client.resource_instance_history_as_of(self, id, last_update)
|
94
94
|
end
|
95
95
|
|
96
96
|
def search(params = {}, client = self.client)
|
97
|
-
handle_response client.search(self, search: { parameters: params })
|
97
|
+
handle_response client.exception_class, client.search(self, search: { parameters: params })
|
98
98
|
end
|
99
99
|
|
100
100
|
def create(model, client = self.client)
|
101
101
|
model = new(model) unless model.is_a?(self)
|
102
|
-
handle_response client.create(model)
|
102
|
+
handle_response client.exception_class, client.create(model)
|
103
103
|
end
|
104
104
|
|
105
105
|
def conditional_create(model, params, client = self.client)
|
106
106
|
model = new(model) unless model.is_a?(self)
|
107
|
-
handle_response client.conditional_create(model, params)
|
107
|
+
handle_response client.exception_class, client.conditional_create(model, params)
|
108
108
|
end
|
109
109
|
|
110
110
|
def partial_update(id, patchset, options = {})
|
111
|
-
handle_response client.partial_update(self, id, patchset, options)
|
111
|
+
handle_response client.exception_class, client.partial_update(self, id, patchset, options)
|
112
112
|
end
|
113
113
|
|
114
114
|
def all(client = self.client)
|
115
|
-
handle_response client.read_feed(self)
|
115
|
+
handle_response client.exception_class, client.read_feed(self)
|
116
116
|
end
|
117
117
|
|
118
118
|
private
|
119
119
|
|
120
|
-
def handle_response(response)
|
121
|
-
raise
|
120
|
+
def handle_response(exception_class, response)
|
121
|
+
raise exception_class.new "Server returned #{response.code}.", response if response.code.between?(400, 599)
|
122
122
|
response.resource
|
123
123
|
end
|
124
124
|
end
|
@@ -59,7 +59,7 @@ module FHIR
|
|
59
59
|
if relative? || reference == client.full_resource_url(resource: resource_class, id: reference_id)
|
60
60
|
read_client = client
|
61
61
|
else
|
62
|
-
read_client = FHIR::Client.new base_uri, default_format: client.default_format
|
62
|
+
read_client = FHIR::Client.new base_uri, default_format: client.default_format, proxy: client.proxy
|
63
63
|
end
|
64
64
|
resource_class.read(reference_id, read_client)
|
65
65
|
end
|
@@ -69,7 +69,7 @@ module FHIR
|
|
69
69
|
if relative? || reference == client.full_resource_url(resource: resource_class, id: reference_id)
|
70
70
|
read_client = client
|
71
71
|
else
|
72
|
-
read_client = FHIR::Client.new base_uri, default_format: client.default_format
|
72
|
+
read_client = FHIR::Client.new base_uri, default_format: client.default_format, proxy: client.proxy
|
73
73
|
end
|
74
74
|
resource_class.vread(reference_id, version_id, read_client)
|
75
75
|
end
|
@@ -6,46 +6,83 @@ module FHIR
|
|
6
6
|
format: 'application/fhir+xml'
|
7
7
|
}.freeze
|
8
8
|
|
9
|
-
DEFAULT_CHARSET = '
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
options.delete(:category)
|
9
|
+
DEFAULT_CHARSET = 'utf-8'.freeze
|
10
|
+
DEFAULT_CONTENT_TYPE = 'application/fhir+xml'.freeze # time to default to json?
|
11
|
+
|
12
|
+
#
|
13
|
+
# Normalize submitted header key value pairs
|
14
|
+
#
|
15
|
+
# 'content-type', 'Content-Type', and :content_type would all represent the content-type header
|
16
|
+
#
|
17
|
+
# Assumes symbols like :content_type to be "content-type"
|
18
|
+
# if for some odd reason the Header string representation contains underscores it would need to be specified
|
19
|
+
# as a string (i.e. options {"Underscore_Header" => 'why not hyphens'})
|
20
|
+
# Note that servers like apache or nginx consider that invalid anyways and drop them
|
21
|
+
# http://httpd.apache.org/docs/trunk/new_features_2_4.html
|
22
|
+
# http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers
|
23
|
+
def self.normalize_headers(to_be_normalized, to_symbol = true, capitalized = false)
|
24
|
+
to_be_normalized.inject({}) do |result, (key, value)|
|
25
|
+
key = key.to_s.downcase.split(/-|_/)
|
26
|
+
key.map!(&:capitalize) if capitalized
|
27
|
+
key = to_symbol ? key.join('_').to_sym : key.join('-')
|
28
|
+
result[key] = value.to_s
|
29
|
+
result
|
31
30
|
end
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
def self.convert_symbol_headers headers
|
34
|
+
headers.inject({}) do |result, (key, value)|
|
35
|
+
if key.is_a? Symbol
|
36
|
+
key = key.to_s.split(/_/).map(&:capitalize).join('-')
|
37
|
+
end
|
38
|
+
result[key] = value.to_s
|
39
|
+
result
|
39
40
|
end
|
41
|
+
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
# Returns normalized HTTP Headers
|
44
|
+
# header key value pairs can be supplied with keys specified as symbols or strings
|
45
|
+
# keys will be normalized to symbols.
|
46
|
+
# e.g. the keys :accept, "accept", and "Accept" all represent the Accept HTTP Header
|
47
|
+
# @param [Hash] options key value pairs for the http headerx
|
48
|
+
# @return [Hash] The normalized FHIR Headers
|
49
|
+
def self.fhir_headers(headers = {}, additional_headers = {}, format = DEFAULT_CONTENT_TYPE, use_accept_header = true, use_accept_charset = true)
|
50
|
+
# normalizes header names to be case-insensitive
|
51
|
+
# See relevant HTTP RFCs:
|
52
|
+
# https://tools.ietf.org/html/rfc2616#section-4.2
|
53
|
+
# https://tools.ietf.org/html/rfc7230#section-3.2
|
54
|
+
#
|
55
|
+
# https://tools.ietf.org/html/rfc7231#section-5.3.2
|
56
|
+
# optional white space before and
|
57
|
+
# https://tools.ietf.org/html/rfc2616#section-3.4
|
58
|
+
# utf-8 is case insensitive
|
59
|
+
#
|
60
|
+
headers ||= {}
|
61
|
+
additional_headers ||= {}
|
62
|
+
|
63
|
+
fhir_headers = {user_agent: 'Ruby FHIR Client'}
|
64
|
+
|
65
|
+
fhir_headers[:accept_charset] = DEFAULT_CHARSET if use_accept_charset
|
66
|
+
|
67
|
+
# https://www.hl7.org/fhir/DSTU2/http.html#mime-type
|
68
|
+
# could add option for ;charset=#{DEFAULT_CHARSET} in accept header
|
69
|
+
fhir_headers[:accept] = "#{format}" if use_accept_header
|
70
|
+
|
71
|
+
# maybe in a future update normalize everything to symbols
|
72
|
+
# Headers should be case insensitive anyways...
|
73
|
+
#headers = normalize_headers(headers) unless headers.empty?
|
74
|
+
#
|
75
|
+
fhir_headers = convert_symbol_headers(fhir_headers)
|
76
|
+
headers = convert_symbol_headers(headers)
|
77
|
+
|
78
|
+
# supplied headers will always be used, e.g. if @use_accept_header is false
|
79
|
+
# ,but an accept header is explicitly supplied then it will be used (or override the existing)
|
80
|
+
fhir_headers.merge!(headers) unless headers.empty?
|
81
|
+
fhir_headers.merge!(additional_headers)
|
45
82
|
fhir_headers
|
46
83
|
end
|
47
84
|
|
48
|
-
def resource_url(options, use_format_param = false)
|
85
|
+
def self.resource_url(options, use_format_param = false)
|
49
86
|
options = DEFAULTS.merge(options)
|
50
87
|
|
51
88
|
params = {}
|
@@ -4,11 +4,47 @@ module FHIR
|
|
4
4
|
#
|
5
5
|
# Read the current state of a resource.
|
6
6
|
#
|
7
|
-
def read(klass, id, format =
|
8
|
-
options = { resource: klass, id: id, format: format }.merge(options)
|
7
|
+
def read(klass, id, format = nil, summary = nil, options = {})
|
8
|
+
options = { resource: klass, id: id, format: format || @default_format}.merge(options)
|
9
9
|
options[:summary] = summary if summary
|
10
|
-
|
11
|
-
reply
|
10
|
+
headers = { accept: "#{format}" } if format
|
11
|
+
reply = get resource_url(options), fhir_headers(headers)
|
12
|
+
reply.resource = parse_reply(klass, format || @default_format, reply)
|
13
|
+
reply.resource_class = klass
|
14
|
+
reply
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Conditionally Read the resource if it has been modified since the supplied date
|
19
|
+
#
|
20
|
+
# See If-Modified-Since RRC 7232 https://tools.ietf.org/html/rfc7232#section-3.3 and
|
21
|
+
# See HTTP-date https://tools.ietf.org/html/rfc7231#section-7.1.1.1
|
22
|
+
#
|
23
|
+
# @param klass the FHIR Resource class
|
24
|
+
# @param id the resource id
|
25
|
+
# @param since_date the date
|
26
|
+
def conditional_read_since(klass, id, since_date, options = {})
|
27
|
+
|
28
|
+
options = { resource: klass, id: id}.merge(options)
|
29
|
+
headers = { if_modified_since: since_date }
|
30
|
+
reply = get resource_url(options), fhir_headers(headers)
|
31
|
+
reply.resource = parse_reply(klass, @default_format, reply)
|
32
|
+
reply.resource_class = klass
|
33
|
+
reply
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Conditionally Read the resource based on the provided ETag
|
38
|
+
#
|
39
|
+
# @param klass the FHIR Resource class
|
40
|
+
# @param id the resource id
|
41
|
+
# @param version_id the version_id used for the ETag
|
42
|
+
def conditional_read_version(klass, id, version_id, options = {})
|
43
|
+
|
44
|
+
options = { resource: klass, id: id}.merge(options)
|
45
|
+
headers = { if_none_match: "W/#{version_id}" }
|
46
|
+
reply = get resource_url(options), fhir_headers(headers)
|
47
|
+
reply.resource = parse_reply(klass, @default_format, reply)
|
12
48
|
reply.resource_class = klass
|
13
49
|
reply
|
14
50
|
end
|
@@ -16,9 +52,12 @@ module FHIR
|
|
16
52
|
#
|
17
53
|
# Read a resource bundle (an XML ATOM feed)
|
18
54
|
#
|
19
|
-
|
20
|
-
|
21
|
-
|
55
|
+
#
|
56
|
+
def read_feed(klass, format = nil)
|
57
|
+
headers = { accept: "#{format}" } if format
|
58
|
+
format ||= @default_format
|
59
|
+
options = { resource: klass, format: format}
|
60
|
+
reply = get resource_url(options), fhir_headers(headers)
|
22
61
|
reply.resource = parse_reply(klass, format, reply)
|
23
62
|
reply.resource_class = klass
|
24
63
|
reply
|
@@ -27,33 +66,36 @@ module FHIR
|
|
27
66
|
#
|
28
67
|
# Read the state of a specific version of the resource
|
29
68
|
#
|
30
|
-
def vread(klass, id, version_id, format =
|
69
|
+
def vread(klass, id, version_id, format = nil)
|
70
|
+
headers = { accept: "#{format}" } if format
|
71
|
+
format ||= @default_format
|
31
72
|
options = { resource: klass, id: id, format: format, history: { id: version_id } }
|
32
|
-
reply = get resource_url(options), fhir_headers(
|
73
|
+
reply = get resource_url(options), fhir_headers(headers)
|
33
74
|
reply.resource = parse_reply(klass, format, reply)
|
34
75
|
reply.resource_class = klass
|
35
76
|
reply
|
36
77
|
end
|
37
78
|
|
38
79
|
def raw_read(options)
|
39
|
-
get resource_url(options), fhir_headers
|
80
|
+
get resource_url(options), fhir_headers
|
40
81
|
end
|
41
82
|
|
42
|
-
def raw_read_url(url, format =
|
43
|
-
|
83
|
+
def raw_read_url(url, format = nil)
|
84
|
+
headers = { accept: "#{format}" } if format
|
85
|
+
get url, fhir_headers(headers)
|
44
86
|
end
|
45
87
|
|
46
88
|
#
|
47
89
|
# Update an existing resource by its id or create it if it is a new resource, not present on the server
|
48
90
|
#
|
49
|
-
def update(resource, id, format =
|
91
|
+
def update(resource, id, format = nil)
|
50
92
|
base_update(resource, id, nil, format)
|
51
93
|
end
|
52
94
|
|
53
95
|
#
|
54
96
|
# Update an existing resource by its id or create it if it is a new resource, not present on the server
|
55
97
|
#
|
56
|
-
def conditional_update(resource, id, search_params, format =
|
98
|
+
def conditional_update(resource, id, search_params, format = nil)
|
57
99
|
options = {
|
58
100
|
search: {
|
59
101
|
flag: false,
|
@@ -67,15 +109,29 @@ module FHIR
|
|
67
109
|
base_update(resource, id, options, format)
|
68
110
|
end
|
69
111
|
|
112
|
+
#
|
113
|
+
# Version Aware Update using version_id
|
114
|
+
# prevents Lost Update Problem https://www.w3.org/1999/04/Editing/
|
115
|
+
# @param resource the FHIR resource object
|
116
|
+
# @param id the resource id
|
117
|
+
def version_aware_update(resource, id, version_id, format = nil, options = {})
|
118
|
+
base_update(resource, id, options, format, {if_match: "W/#{version_id}"})
|
119
|
+
end
|
120
|
+
|
70
121
|
#
|
71
122
|
# Update an existing resource by its id or create it if it is a new resource, not present on the server
|
72
123
|
#
|
73
|
-
def base_update(resource, id, options, format)
|
124
|
+
def base_update(resource, id, options, format = nil, headers = nil)
|
125
|
+
headers ||= {}
|
126
|
+
headers[:accept] = "#{format}" if format
|
127
|
+
format ||= @default_format
|
128
|
+
headers[:content_type] = "#{format}"
|
129
|
+
headers[:prefer] = @return_preference if @use_return_preference
|
74
130
|
options = {} if options.nil?
|
75
131
|
options[:resource] = resource.class
|
76
132
|
options[:format] = format
|
77
133
|
options[:id] = id
|
78
|
-
reply = put resource_url(options), resource, fhir_headers(
|
134
|
+
reply = put resource_url(options), resource, fhir_headers(headers)
|
79
135
|
reply.resource = parse_reply(resource.class, format, reply) if reply.body.present?
|
80
136
|
reply.resource_class = resource.class
|
81
137
|
reply
|
@@ -84,18 +140,20 @@ module FHIR
|
|
84
140
|
#
|
85
141
|
# Partial update using a patchset (PATCH)
|
86
142
|
#
|
87
|
-
def partial_update(klass, id, patchset, options = {}, format =
|
88
|
-
|
89
|
-
|
143
|
+
def partial_update(klass, id, patchset, options = {}, format = nil)
|
144
|
+
headers = {}
|
145
|
+
headers[:accept] = "#{format}" if format
|
146
|
+
format ||= @default_format
|
147
|
+
options = { resource: klass, id: id, format: format}.merge options
|
90
148
|
if [FHIR::Formats::ResourceFormat::RESOURCE_XML, FHIR::Formats::ResourceFormat::RESOURCE_XML_DSTU2].include?(format)
|
91
149
|
options[:format] = FHIR::Formats::PatchFormat::PATCH_XML
|
92
|
-
|
150
|
+
headers[:content_type] = "#{FHIR::Formats::PatchFormat::PATCH_XML}"
|
93
151
|
elsif [FHIR::Formats::ResourceFormat::RESOURCE_JSON, FHIR::Formats::ResourceFormat::RESOURCE_JSON_DSTU2].include?(format)
|
94
152
|
options[:format] = FHIR::Formats::PatchFormat::PATCH_JSON
|
95
|
-
|
153
|
+
headers[:content_type] = "#{FHIR::Formats::PatchFormat::PATCH_JSON}"
|
96
154
|
end
|
97
|
-
|
98
|
-
reply = patch resource_url(options), patchset, fhir_headers(
|
155
|
+
headers[:prefer] = @return_preference if @use_return_preference
|
156
|
+
reply = patch resource_url(options), patchset, fhir_headers(headers)
|
99
157
|
reply.resource = parse_reply(klass, format, reply)
|
100
158
|
reply.resource_class = klass
|
101
159
|
reply
|
@@ -106,9 +164,7 @@ module FHIR
|
|
106
164
|
#
|
107
165
|
def destroy(klass, id = nil, options = {})
|
108
166
|
options = { resource: klass, id: id, format: @default_format }.merge options
|
109
|
-
|
110
|
-
headers.delete('Content-Type')
|
111
|
-
reply = delete resource_url(options), headers
|
167
|
+
reply = delete resource_url(options), fhir_headers
|
112
168
|
reply.resource_class = klass
|
113
169
|
reply
|
114
170
|
end
|
@@ -117,33 +173,38 @@ module FHIR
|
|
117
173
|
# Create a new resource with a server assigned id. Return the newly created
|
118
174
|
# resource with the id the server assigned.
|
119
175
|
#
|
120
|
-
def create(resource, options = {}, format =
|
176
|
+
def create(resource, options = {}, format = nil)
|
121
177
|
base_create(resource, options, format)
|
122
178
|
end
|
123
179
|
|
124
180
|
#
|
125
181
|
# Conditionally create a new resource with a server assigned id.
|
126
182
|
#
|
127
|
-
def conditional_create(resource, if_none_exist_parameters, format =
|
183
|
+
def conditional_create(resource, if_none_exist_parameters, format = nil)
|
128
184
|
query = ''
|
129
185
|
if_none_exist_parameters.each do |key, value|
|
130
186
|
query += "#{key}=#{value}&"
|
131
187
|
end
|
132
188
|
query = query[0..-2] # strip off the trailing ampersand
|
133
|
-
|
134
|
-
options
|
135
|
-
base_create(resource, options, format)
|
189
|
+
header = {if_none_exist: query}
|
190
|
+
base_create(resource, options, format, header)
|
136
191
|
end
|
137
192
|
|
138
193
|
#
|
139
194
|
# Create a new resource with a server assigned id. Return the newly created
|
140
195
|
# resource with the id the server assigned.
|
141
196
|
#
|
142
|
-
def base_create(resource, options, format)
|
197
|
+
def base_create(resource, options, format = nil, additional_header = {})
|
198
|
+
headers = {}
|
199
|
+
headers[:accept] = "#{format}" if format
|
200
|
+
format ||= @default_format
|
201
|
+
headers = {content_type: "#{format}"}
|
202
|
+
headers[:prefer] = @return_preference if @use_return_preference
|
203
|
+
headers.merge!(additional_header)
|
143
204
|
options = {} if options.nil?
|
144
205
|
options[:resource] = resource.class
|
145
|
-
options[:format] = format
|
146
|
-
reply = post resource_url(options), resource, fhir_headers(
|
206
|
+
options[:format] = format || @default_format
|
207
|
+
reply = post resource_url(options), resource, fhir_headers(headers)
|
147
208
|
if [200, 201].include? reply.code
|
148
209
|
type = reply.response[:headers].detect{|x, _y| x.downcase=='content-type'}.try(:last)
|
149
210
|
if !type.nil?
|
@@ -10,7 +10,7 @@ module FHIR
|
|
10
10
|
bundle = current.resource
|
11
11
|
link = bundle.method(page).call
|
12
12
|
return nil unless link
|
13
|
-
reply = get strip_base(link.url), fhir_headers
|
13
|
+
reply = get strip_base(link.url), fhir_headers
|
14
14
|
reply.resource = parse_reply(current.resource_class, @default_format, reply)
|
15
15
|
reply.resource_class = current.resource_class
|
16
16
|
reply
|
@@ -25,7 +25,7 @@ module FHIR
|
|
25
25
|
|
26
26
|
def history(options)
|
27
27
|
options = {format: @default_format}.merge(options)
|
28
|
-
reply = get resource_url(options), fhir_headers
|
28
|
+
reply = get resource_url(options), fhir_headers
|
29
29
|
reply.resource = parse_reply(options[:resource], options[:format], reply)
|
30
30
|
reply.resource_class = options[:resource]
|
31
31
|
reply
|
@@ -10,15 +10,19 @@ module FHIR
|
|
10
10
|
# Fetch Patient Record [base]/Patient/$everything | [base]/Patient/[id]/$everything
|
11
11
|
# http://hl7.org/implement/standards/FHIR-Develop/patient-operations.html#everything
|
12
12
|
# Fetches resources for a given patient record, scoped by a start and end time, and returns a Bundle of results
|
13
|
-
def fetch_patient_record(id = nil, startTime = nil, endTime = nil, method = 'GET', format =
|
13
|
+
def fetch_patient_record(id = nil, startTime = nil, endTime = nil, method = 'GET', format = nil)
|
14
14
|
fetch_record(id, [startTime, endTime], method, versioned_resource_class('Patient'), format)
|
15
15
|
end
|
16
16
|
|
17
|
-
def fetch_encounter_record(id = nil, method = 'GET', format =
|
17
|
+
def fetch_encounter_record(id = nil, method = 'GET', format = nil)
|
18
18
|
fetch_record(id, [nil, nil], method, versioned_resource_class('Encounter'), format)
|
19
19
|
end
|
20
20
|
|
21
|
-
def fetch_record(id = nil, time = [nil, nil], method = 'GET', klass = versioned_resource_class('Patient'), format =
|
21
|
+
def fetch_record(id = nil, time = [nil, nil], method = 'GET', klass = versioned_resource_class('Patient'), format = nil)
|
22
|
+
headers = {}
|
23
|
+
headers[:accept] = "#{format}" if format
|
24
|
+
format ||= @default_format
|
25
|
+
headers[:content_type] = format
|
22
26
|
options = { resource: klass, format: format, operation: { name: :fetch_patient_record, method: method } }
|
23
27
|
options.deep_merge!(id: id) unless id.nil?
|
24
28
|
options[:operation][:parameters] = {} if options[:operation][:parameters].nil?
|
@@ -26,7 +30,7 @@ module FHIR
|
|
26
30
|
options[:operation][:parameters][:end] = { type: 'Date', value: time.last } unless time.last.nil?
|
27
31
|
|
28
32
|
if options[:operation][:method] == 'GET'
|
29
|
-
reply = get resource_url(options), fhir_headers
|
33
|
+
reply = get resource_url(options), fhir_headers
|
30
34
|
else
|
31
35
|
# create Parameters body
|
32
36
|
if options[:operation] && options[:operation][:parameters]
|
@@ -37,7 +41,7 @@ module FHIR
|
|
37
41
|
p.parameter << parameter
|
38
42
|
end
|
39
43
|
end
|
40
|
-
reply = post resource_url(options), p, fhir_headers(
|
44
|
+
reply = post resource_url(options), p, fhir_headers(headers)
|
41
45
|
end
|
42
46
|
|
43
47
|
reply.resource = parse_reply(versioned_resource_class('Bundle'), format, reply)
|
@@ -95,11 +99,14 @@ module FHIR
|
|
95
99
|
|
96
100
|
def terminology_operation(params = {}, format = @default_format)
|
97
101
|
options = { format: format }
|
102
|
+
headers = {}
|
103
|
+
headers[:accept] = "#{format}" if format
|
104
|
+
format ||= @default_format
|
98
105
|
# params = [id, code, system, version, display, coding, codeableConcept, date, abstract]
|
99
106
|
options.deep_merge!(params)
|
100
107
|
|
101
108
|
if options[:operation][:method] == 'GET'
|
102
|
-
reply = get resource_url(options), fhir_headers(
|
109
|
+
reply = get resource_url(options), fhir_headers(headers)
|
103
110
|
else
|
104
111
|
# create Parameters body
|
105
112
|
if options[:operation] && options[:operation][:parameters]
|
@@ -110,7 +117,8 @@ module FHIR
|
|
110
117
|
p.parameter << parameter
|
111
118
|
end
|
112
119
|
end
|
113
|
-
|
120
|
+
headers[:content_type] = "#{format}"
|
121
|
+
reply = post resource_url(options), p, fhir_headers(headers)
|
114
122
|
end
|
115
123
|
|
116
124
|
reply.resource = parse_reply(options[:resource], format, reply)
|
@@ -124,7 +132,8 @@ module FHIR
|
|
124
132
|
add_resource_parameter(params, 'resource', resource)
|
125
133
|
add_parameter(params, 'onlyCertainMatches', 'Boolean', options[:onlyCertainMatches]) unless options[:onlyCertainMatches].nil?
|
126
134
|
add_parameter(params, 'count', 'Integer', options[:matchCount]) if options[:matchCount].is_a?(Integer)
|
127
|
-
post resource_url(options), params, fhir_headers(
|
135
|
+
post resource_url(options), params, fhir_headers({content_type: "#{format || @default_format}",
|
136
|
+
accept: "#{format || @default_format}"})
|
128
137
|
end
|
129
138
|
|
130
139
|
#
|
@@ -139,18 +148,24 @@ module FHIR
|
|
139
148
|
|
140
149
|
def validate(resource, options = {}, format = @default_format)
|
141
150
|
options.merge!(resource: resource.class, validate: true, format: format)
|
151
|
+
headers = {}
|
152
|
+
headers[:accept] = "#{format}" if format
|
153
|
+
headers[:content_type] = "#{format}"
|
142
154
|
params = versioned_resource_class('Parameters').new
|
143
155
|
add_resource_parameter(params, 'resource', resource)
|
144
156
|
add_parameter(params, 'profile', 'Uri', options[:profile_uri]) unless options[:profile_uri].nil?
|
145
|
-
post resource_url(options), params, fhir_headers(
|
157
|
+
post resource_url(options), params, fhir_headers(headers)
|
146
158
|
end
|
147
159
|
|
148
160
|
def validate_existing(resource, id, options = {}, format = @default_format)
|
149
161
|
options.merge!(resource: resource.class, id: id, validate: true, format: format)
|
162
|
+
headers = {}
|
163
|
+
headers[:accept] = "#{format}" if format
|
164
|
+
headers[:content_type] = "#{format}"
|
150
165
|
params = versioned_resource_class('Parameters').new
|
151
166
|
add_resource_parameter(params, 'resource', resource)
|
152
167
|
add_parameter(params, 'profile', 'Uri', options[:profile_uri]) unless options[:profile_uri].nil?
|
153
|
-
post resource_url(options), params, fhir_headers(
|
168
|
+
post resource_url(options), params, fhir_headers(headers)
|
154
169
|
end
|
155
170
|
|
156
171
|
private
|
@@ -13,9 +13,9 @@ module FHIR
|
|
13
13
|
options[:format] = format
|
14
14
|
|
15
15
|
reply = if options[:search] && options[:search][:flag]
|
16
|
-
post resource_url(options), nil, fhir_headers(
|
16
|
+
post resource_url(options), nil, fhir_headers({content_type: 'application/x-www-form-urlencoded'})
|
17
17
|
else
|
18
|
-
get resource_url(options), fhir_headers
|
18
|
+
get resource_url(options), fhir_headers
|
19
19
|
end
|
20
20
|
# reply = get resource_url(options), fhir_headers(options)
|
21
21
|
reply.resource = parse_reply(klass, format, reply)
|
@@ -27,9 +27,9 @@ module FHIR
|
|
27
27
|
options.merge!(resource: klass, id: id, format: format)
|
28
28
|
# if options[:search][:flag]
|
29
29
|
reply = if options[:search] && options[:search][:flag]
|
30
|
-
post resource_url(options), nil, fhir_headers(
|
30
|
+
post resource_url(options), nil, fhir_headers({content_type: 'application/x-www-form-urlencoded'})
|
31
31
|
else
|
32
|
-
get resource_url(options), fhir_headers
|
32
|
+
get resource_url(options), fhir_headers
|
33
33
|
end
|
34
34
|
reply.resource = parse_reply(klass, format, reply)
|
35
35
|
reply.resource_class = klass
|
@@ -39,9 +39,9 @@ module FHIR
|
|
39
39
|
def search_all(options = {}, format = @default_format)
|
40
40
|
options[:format] = format
|
41
41
|
reply = if options[:search] && options[:search][:flag]
|
42
|
-
post resource_url(options), nil, fhir_headers(
|
42
|
+
post resource_url(options), nil, fhir_headers({content_type: 'application/x-www-form-urlencoded'})
|
43
43
|
else
|
44
|
-
get resource_url(options), fhir_headers
|
44
|
+
get resource_url(options), fhir_headers
|
45
45
|
end
|
46
46
|
reply.resource = parse_reply(nil, format, reply)
|
47
47
|
reply.resource_class = nil
|
@@ -61,8 +61,10 @@ module FHIR
|
|
61
61
|
# @return FHIR::ClientReply
|
62
62
|
#
|
63
63
|
def end_batch(format = @default_format)
|
64
|
-
|
65
|
-
|
64
|
+
headers = {prefer: FHIR::Formats::ReturnPreferences::REPRESENTATION}
|
65
|
+
headers[:content_type] = "#{format}"
|
66
|
+
options = { format: format}
|
67
|
+
reply = post resource_url(options), @transaction_bundle, fhir_headers(headers)
|
66
68
|
begin
|
67
69
|
reply.resource = if format.downcase.include?('xml')
|
68
70
|
versioned_resource_class('Xml').from_xml(reply.body)
|
data/lib/fhir_client/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fhir_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andre Quina
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2019-02-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -144,14 +144,14 @@ dependencies:
|
|
144
144
|
requirements:
|
145
145
|
- - "~>"
|
146
146
|
- !ruby/object:Gem::Version
|
147
|
-
version: '
|
147
|
+
version: '2.0'
|
148
148
|
type: :development
|
149
149
|
prerelease: false
|
150
150
|
version_requirements: !ruby/object:Gem::Requirement
|
151
151
|
requirements:
|
152
152
|
- - "~>"
|
153
153
|
- !ruby/object:Gem::Version
|
154
|
-
version: '
|
154
|
+
version: '2.0'
|
155
155
|
- !ruby/object:Gem::Dependency
|
156
156
|
name: rake
|
157
157
|
requirement: !ruby/object:Gem::Requirement
|
@@ -256,6 +256,7 @@ files:
|
|
256
256
|
- lib/fhir_client/patch_format.rb
|
257
257
|
- lib/fhir_client/resource_address.rb
|
258
258
|
- lib/fhir_client/resource_format.rb
|
259
|
+
- lib/fhir_client/return_preferences.rb
|
259
260
|
- lib/fhir_client/sections/crud.rb
|
260
261
|
- lib/fhir_client/sections/feed.rb
|
261
262
|
- lib/fhir_client/sections/history.rb
|