Skip to content

Commit 1989152

Browse files
authored
Merge pull request #114 from fhir-crucible/r4
R4 Support
2 parents 178f2b4 + e44adec commit 1989152

23 files changed

+610
-128
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ Gemfile.lock
3636
# Logs
3737
*.log
3838
*.log.*
39+
40+
# User-specific
41+
.idea

Gemfile

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
source 'https://rubygems.org'
22

3-
# gem 'fhir_models', :path => '../fhir_models'
4-
# gem 'fhir_dstu2_models', :path => '../fhir_dstu2_models'
5-
63
gemspec
74

85
group :test do

README.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
Ruby FHIR client.
44

55
Supports:
6-
* FHIR STU3 and DSTU2
7-
* XML and JSON
6+
* FHIR R4, STU3 and DSTU2
7+
* JSON and XML
88
* All CRUD, including version read and history
99
* Transactions and Batches
1010
* Search
@@ -58,8 +58,8 @@ patient.destroy
5858

5959
## Advanced Usage
6060

61-
### STU3 and DSTU2
62-
The client defaults to `STU3` but can be switched to `DSTU2` or it can also attempt to autodetect the FHIR version based on the `metadata` endpoint.
61+
### Changing FHIR Versions
62+
The client defaults to `R4` but can be switched to `DSTU2` or `STU3`. It can also attempt to autodetect the FHIR version based on the `metadata` endpoint.
6363

6464
```ruby
6565
# autodetect the FHIR version
@@ -69,19 +69,42 @@ if version == :stu3
6969
puts 'FHIR Client using STU3'
7070
elsif version == :dstu2
7171
puts 'FHIR Client using DSTU2'
72+
elsif version == :r4
73+
puts 'FHIR Client using R4'
7274
end
7375

76+
# tell the client to use R4
77+
client.use_r4
78+
# now use the client with the DSTU2 models
79+
patient = FHIR::Patient.read('example')
80+
patient = client.read(FHIR::Patient, 'example').resource
81+
7482
# tell the client to use STU3 (default)
7583
client.use_stu3
7684
# now use the client normally
77-
patient = FHIR::Patient.read('example')
78-
patient = client.read(FHIR::Patient, 'example').resource
85+
patient = FHIR::STU3::Patient.read('example')
86+
patient = client.read(FHIR::STU3::Patient, 'example').resource
7987

8088
# tell the client to use DSTU2
8189
client.use_dstu2
8290
# now use the client with the DSTU2 models
8391
patient = FHIR::DSTU2::Patient.read('example')
8492
patient = client.read(FHIR::DSTU2::Patient, 'example').resource
93+
94+
95+
```
96+
97+
### Changing FHIR Formats
98+
The client defaults to `json` representation of resources but can be switched to `xml` representations.
99+
100+
```ruby
101+
client = FHIR::Client.new(url)
102+
103+
# Tell the client to use xml
104+
client.default_xml
105+
106+
# Tell the client to use json
107+
client.default_json
85108
```
86109

87110
### Configuration
@@ -192,7 +215,7 @@ end
192215

193216
# License
194217

195-
Copyright 2014-2016 The MITRE Corporation
218+
Copyright 2014-2019 The MITRE Corporation
196219

197220
Licensed under the Apache License, Version 2.0 (the "License");
198221
you may not use this file except in compliance with the License.

fhir_client.gemspec

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ Gem::Specification.new do |spec|
2222

2323
spec.add_dependency 'activesupport', '>= 3'
2424
spec.add_dependency 'addressable', '>= 2.3'
25-
spec.add_dependency 'fhir_models', '>= 3.0.3'
26-
spec.add_dependency 'fhir_dstu2_models', '>= 1.0.4'
25+
spec.add_dependency 'fhir_models', '>= 4.0.0'
26+
spec.add_dependency 'fhir_stu3_models', '>= 3.0.0'
27+
spec.add_dependency 'fhir_dstu2_models', '>= 1.0.9'
2728
spec.add_dependency 'nokogiri', '>= 1.8.2'
2829
spec.add_dependency 'oauth2', '~> 1.1'
2930
spec.add_dependency 'rack', '>= 1.5'

lib/fhir_client.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'fhir_models'
22
require 'fhir_dstu2_models'
3+
require 'fhir_stu3_models'
34
require 'active_support/all'
45

56
root = File.expand_path '.', File.dirname(File.absolute_path(__FILE__))
@@ -10,6 +11,7 @@
1011
require file
1112
end
1213

14+
require_relative 'fhir_client/version_management'
1315
require_relative 'fhir_client/client'
1416
require_relative 'fhir_client/resource_address'
1517
require_relative 'fhir_client/resource_format'

lib/fhir_client/client.rb

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class Client
1111
include FHIR::Sections::Search
1212
include FHIR::Sections::Operations
1313
include FHIR::Sections::Transactions
14+
include FHIR::VersionManagement
1415

1516
attr_accessor :reply
1617
attr_accessor :use_format_param
@@ -37,14 +38,14 @@ class Client
3738
# @param default_format Default Format Mime type
3839
# @return
3940
#
40-
def initialize(base_service_url, default_format: FHIR::Formats::ResourceFormat::RESOURCE_XML, proxy: nil)
41+
def initialize(base_service_url, default_format: FHIR::Formats::ResourceFormat::RESOURCE_JSON, proxy: nil)
4142
@base_service_url = base_service_url
4243
FHIR.logger.info "Initializing client with #{@base_service_url}"
4344
@use_format_param = false
4445
@use_accept_header = true
4546
@use_accept_charset = true
4647
@default_format = default_format
47-
@fhir_version = :stu3
48+
@fhir_version = :r4
4849
@use_return_preference = false
4950
@return_preference = FHIR::Formats::ReturnPreferences::REPRESENTATION
5051
@exception_class = ClientException
@@ -54,37 +55,26 @@ def initialize(base_service_url, default_format: FHIR::Formats::ResourceFormat::
5455
end
5556

5657
def default_json
57-
@default_format = if @fhir_version == :stu3
58-
FHIR::Formats::ResourceFormat::RESOURCE_JSON
59-
else
60-
FHIR::Formats::ResourceFormat::RESOURCE_JSON_DSTU2
61-
end
58+
@default_format = versioned_format_class(:json)
6259
end
6360

6461
def default_xml
65-
@default_format = if @fhir_version == :stu3
66-
FHIR::Formats::ResourceFormat::RESOURCE_XML
67-
else
68-
FHIR::Formats::ResourceFormat::RESOURCE_XML_DSTU2
69-
end
62+
@default_format = versioned_format_class(:xml)
7063
end
7164

7265
def use_stu3
7366
@fhir_version = :stu3
74-
@default_format = if @default_format.include?('xml')
75-
FHIR::Formats::ResourceFormat::RESOURCE_XML
76-
else
77-
FHIR::Formats::ResourceFormat::RESOURCE_JSON
78-
end
67+
@default_format = versioned_format_class
7968
end
8069

8170
def use_dstu2
8271
@fhir_version = :dstu2
83-
@default_format = if @default_format.include?('xml')
84-
FHIR::Formats::ResourceFormat::RESOURCE_XML_DSTU2
85-
else
86-
FHIR::Formats::ResourceFormat::RESOURCE_JSON_DSTU2
87-
end
72+
@default_format = versioned_format_class
73+
end
74+
75+
def use_r4
76+
@fhir_version = :r4
77+
@default_format = versioned_format_class
8878
end
8979

9080
#
@@ -101,23 +91,19 @@ def use_representation_preference
10191
@return_preference = FHIR::Formats::ReturnPreferences::REPRESENTATION
10292
end
10393

104-
def versioned_resource_class(klass)
105-
if @fhir_version == :stu3
106-
FHIR.const_get(klass)
107-
else
108-
FHIR::DSTU2.const_get(klass)
109-
end
110-
end
111-
11294
def detect_version
11395
cap = capability_statement
11496
if cap.is_a?(FHIR::CapabilityStatement)
115-
@fhir_version = :stu3
97+
use_r4
98+
elsif cap.is_a?(FHIR::STU3::CapabilityStatement)
99+
use_stu3
116100
elsif cap.is_a?(FHIR::DSTU2::Conformance)
117-
@fhir_version = :dstu2
101+
use_dstu2
118102
else
119-
@fhir_version = :stu3
103+
use_r4
120104
end
105+
# Should update the default_format when changing fhir_version
106+
@default_format = versioned_format_class
121107
FHIR.logger.info("Detecting server FHIR version as #{@fhir_version} via metadata")
122108
@fhir_version
123109
end
@@ -279,22 +265,31 @@ def try_conformance_formats(default_format)
279265
formats.insert(0, default_format)
280266

281267
@cached_capability_statement = nil
282-
@default_format = nil
283268

284269
formats.each do |frmt|
285270
reply = get 'metadata', fhir_headers({accept: "#{frmt}"})
286271
next unless reply.code == 200
272+
use_r4
287273
begin
288274
@cached_capability_statement = parse_reply(FHIR::CapabilityStatement, frmt, reply)
289275
rescue
290276
@cached_capability_statement = nil
291277
end
292-
unless @cached_capability_statement
278+
if @cached_capability_statement.nil? || !@cached_capability_statement.fhirVersion.starts_with?('4')
279+
use_stu3
293280
begin
294-
@cached_capability_statement = parse_reply(FHIR::DSTU2::Conformance, frmt, reply)
281+
@cached_capability_statement = parse_reply(FHIR::STU3::CapabilityStatement, frmt, reply)
295282
rescue
296283
@cached_capability_statement = nil
297284
end
285+
unless @cached_capability_statement
286+
use_dstu2
287+
begin
288+
@cached_capability_statement = parse_reply(FHIR::DSTU2::Conformance, frmt, reply)
289+
rescue
290+
@cached_capability_statement = nil
291+
end
292+
end
298293
end
299294
if @cached_capability_statement
300295
@default_format = frmt
@@ -328,12 +323,18 @@ def parse_reply(klass, format, response)
328323
else
329324
FHIR::DSTU2::Json.from_json(response.body)
330325
end
331-
else
326+
elsif(@fhir_version == :r4 || klass&.ancestors&.include?(FHIR::Model))
332327
if(format.include?('xml'))
333328
FHIR::Xml.from_xml(response.body)
334329
else
335330
FHIR::Json.from_json(response.body)
336331
end
332+
else
333+
if(format.include?('xml'))
334+
FHIR::STU3::Xml.from_xml(response.body)
335+
else
336+
FHIR::STU3::Json.from_json(response.body)
337+
end
337338
end
338339
res.client = self unless res.nil?
339340
FHIR.logger.warn "Expected #{klass} but got #{res.class}" if res.class != klass
@@ -353,11 +354,7 @@ def reissue_request(request)
353354
method(request['method']).call(request['url'], request['headers'])
354355
elsif [:post, :put].include?(request['method'])
355356
unless request['payload'].nil?
356-
resource = if @fhir_version == :stu3
357-
FHIR.from_contents(request['payload'])
358-
else
359-
FHIR::DSTU2.from_contents(request['payload'])
360-
end
357+
resource = versioned_resource_class.from_contents(request['payload'])
361358
end
362359
method(request['method']).call(request['url'], resource, request['headers'])
363360
end

lib/fhir_client/ext/bundle.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,11 @@ class Bundle
6060
end
6161
end
6262
end
63+
64+
module FHIR
65+
module STU3
66+
class Bundle
67+
include FHIR::BundleExtras
68+
end
69+
end
70+
end

lib/fhir_client/ext/model.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,11 @@ class Model
139139
end
140140
end
141141
end
142+
143+
module FHIR
144+
module STU3
145+
class Model
146+
include FHIR::ModelExtras
147+
end
148+
end
149+
end

lib/fhir_client/ext/reference.rb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def reference_id
3939
end
4040
end
4141

42-
def type
42+
def resource_type
4343
return if contained?
4444
parts[:type]
4545
end
@@ -82,7 +82,7 @@ class Reference
8282
include FHIR::ReferenceExtras
8383

8484
def resource_class
85-
"FHIR::#{type}".constantize unless contained?
85+
"FHIR::#{resource_type}".constantize unless contained?
8686
end
8787
end
8888
end
@@ -93,7 +93,19 @@ class Reference
9393
include FHIR::ReferenceExtras
9494

9595
def resource_class
96-
"FHIR::DSTU2::#{type}".constantize unless contained?
96+
"FHIR::DSTU2::#{resource_type}".constantize unless contained?
97+
end
98+
end
99+
end
100+
end
101+
102+
module FHIR
103+
module STU3
104+
class Reference
105+
include FHIR::ReferenceExtras
106+
107+
def resource_class
108+
"FHIR::STU3::#{resource_type}".constantize unless contained?
97109
end
98110
end
99111
end

lib/fhir_client/model/client_reply.rb

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
module FHIR
22
class ClientReply
3+
4+
include FHIR::VersionManagement
5+
36
@@path_regexes = {
47
'[type]' => "(#{FHIR::RESOURCES.join('|')})",
58
'[id]' => FHIR::PRIMITIVES['id']['regex'],
@@ -157,11 +160,7 @@ def validate_body(name, body, body_rules)
157160
if body_rules['types']
158161
body_type_match = false
159162
begin
160-
content = if @fhir_version == :stu3
161-
FHIR.from_contents(body)
162-
else
163-
FHIR::DSTU2.from_contents(body)
164-
end
163+
content = versioned_resource_class().from_contents(body)
165164
body_rules['types'].each do |type|
166165
body_type_match = true if content.resourceType == type
167166
body_type_match = true if type == 'Resource' && validate_resource(content.resourceType)
@@ -182,12 +181,7 @@ def validate_body(name, body, body_rules)
182181
end
183182

184183
def validate_resource(resource_type)
185-
if @fhir_version == :stu3
186-
return true if FHIR::RESOURCES.include?(resource_type)
187-
else
188-
return true if FHIR::DSTU2::RESOURCES.include?(resource_type)
189-
end
190-
false
184+
versioned_resource_class(:RESOURCES).include?(resource_type)? true : false
191185
end
192186

193187
private :validate_headers, :validate_body, :validate_resource

0 commit comments

Comments
 (0)