fhir_client 1.6.3 → 1.6.7
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/.codeclimate.yml +34 -0
- data/.csslintrc +2 -0
- data/.eslintignore +1 -0
- data/.eslintrc +213 -0
- data/.rubocop.yml +1159 -0
- data/.travis.yml +8 -0
- data/Gemfile +5 -5
- data/Rakefile +14 -13
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fhir_client.gemspec +34 -21
- data/lib/fhir_client.rb +13 -18
- data/lib/fhir_client/client.rb +533 -0
- data/lib/{client_exception.rb → fhir_client/client_exception.rb} +1 -3
- data/lib/{ext → fhir_client/ext}/bundle.rb +5 -7
- data/lib/{ext → fhir_client/ext}/model.rb +10 -2
- data/lib/fhir_client/ext/reference.rb +28 -0
- data/lib/{fhir_api_validation.json → fhir_client/fhir_api_validation.json} +0 -0
- data/lib/{model → fhir_client/model}/client_reply.rb +47 -51
- data/lib/{model → fhir_client/model}/tag.rb +6 -7
- data/lib/fhir_client/patch_format.rb +8 -0
- data/lib/{resource_address.rb → fhir_client/resource_address.rb} +135 -139
- data/lib/fhir_client/resource_format.rb +11 -0
- data/lib/{sections → fhir_client/sections}/crud.rb +28 -30
- data/lib/{sections → fhir_client/sections}/feed.rb +1 -4
- data/lib/{sections → fhir_client/sections}/history.rb +14 -15
- data/lib/{sections → fhir_client/sections}/operations.rb +22 -28
- data/lib/fhir_client/sections/search.rb +52 -0
- data/lib/{sections → fhir_client/sections}/tags.rb +12 -12
- data/lib/{sections → fhir_client/sections}/transactions.rb +17 -20
- data/lib/{sections → fhir_client/sections}/validate.rb +12 -15
- data/lib/{tasks → fhir_client/tasks}/tasks.rake +19 -18
- data/lib/fhir_client/version.rb +5 -0
- metadata +127 -83
- data/lib/client_interface.rb +0 -533
- data/lib/ext/reference.rb +0 -11
- data/lib/patch_format.rb +0 -10
- data/lib/resource_format.rb +0 -13
- data/lib/sections/search.rb +0 -53
- data/test/fixtures/bundle-example.xml +0 -79
- data/test/fixtures/parameters-example.json +0 -18
- data/test/fixtures/parameters-example.xml +0 -17
- data/test/test_helper.rb +0 -8
- data/test/unit/basic_test.rb +0 -17
- data/test/unit/bundle_test.rb +0 -21
- data/test/unit/parameters_test.rb +0 -44
data/.travis.yml
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- "2.0.0"
|
4
|
+
- "2.1.10"
|
5
|
+
- "2.2.5"
|
6
|
+
- "2.3.1"
|
7
|
+
before_install:
|
8
|
+
- gem install bundler
|
4
9
|
services: mongodb
|
5
10
|
script: bundle exec rake test
|
6
11
|
notifications:
|
@@ -8,3 +13,6 @@ notifications:
|
|
8
13
|
recipients:
|
9
14
|
- healthcare-ci@googlegroups.com
|
10
15
|
on_failure: change
|
16
|
+
addons:
|
17
|
+
code_climate:
|
18
|
+
repo_token: ac1640edaefa720557e56bfaba4e09808559d7d61812f96d58ea4aa5f4669738
|
data/Gemfile
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
ruby RUBY_VERSION
|
2
3
|
gemspec
|
3
4
|
|
4
5
|
group :test do
|
5
|
-
gem '
|
6
|
-
gem '
|
7
|
-
gem '
|
8
|
-
gem 'awesome_print', :require => 'ap'
|
6
|
+
gem 'codeclimate-test-reporter', require: nil
|
7
|
+
gem 'rubocop', '~> 0.43.0', require: false
|
8
|
+
gem 'awesome_print', require: 'ap'
|
9
9
|
end
|
data/Rakefile
CHANGED
@@ -1,25 +1,26 @@
|
|
1
|
-
require '
|
1
|
+
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/testtask'
|
3
|
-
require '
|
4
|
-
require 'pry'
|
3
|
+
require 'rubocop/rake_task'
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
# Pull in any rake task defined in lib/tasks
|
9
|
-
Dir['lib/tasks/**/*.rake'].sort.each do |ext|
|
5
|
+
Dir['lib/fhir_client/tasks/**/*.rake'].sort.each do |ext|
|
10
6
|
load ext
|
11
7
|
end
|
12
8
|
|
13
|
-
|
14
|
-
|
15
|
-
t.libs <<
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'test'
|
11
|
+
t.libs << 'lib'
|
16
12
|
t.test_files = FileList['test/**/*_test.rb']
|
17
13
|
t.verbose = true
|
18
14
|
t.warning = false
|
19
15
|
end
|
20
16
|
|
21
|
-
|
22
|
-
|
17
|
+
desc 'Run rubocop'
|
18
|
+
task :rubocop do
|
19
|
+
RuboCop::RakeTask.new
|
20
|
+
end
|
21
|
+
|
22
|
+
task test: [:rubocop] do
|
23
|
+
system('open coverage/index.html')
|
23
24
|
end
|
24
25
|
|
25
|
-
task :
|
26
|
+
task default: [:test]
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'fhir_client'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require 'pry'
|
11
|
+
Pry.start
|
12
|
+
|
13
|
+
# require 'irb'
|
14
|
+
# IRB.start
|
data/bin/setup
ADDED
data/fhir_client.gemspec
CHANGED
@@ -1,25 +1,38 @@
|
|
1
|
-
#
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fhir_client/version'
|
2
5
|
|
3
|
-
Gem::Specification.new do |
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
s.homepage = "https://github.com/hl7-fhir/fhir-svn"
|
9
|
-
s.authors = ["Andre Quina", "Jason Walonoski", "Janoo Fernandes"]
|
10
|
-
s.version = '1.6.3'
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'fhir_client'
|
8
|
+
spec.version = FHIR::Client::VERSION
|
9
|
+
spec.authors = ['Andre Quina', 'Jason Walonoski', 'Janoo Fernandes']
|
10
|
+
spec.email = ['aquina@mitre.org']
|
11
11
|
|
12
|
-
|
12
|
+
spec.summary = %q{A Gem for handling FHIR client requests in ruby}
|
13
|
+
spec.description = %q{A Gem for handling FHIR client requests in ruby}
|
14
|
+
spec.homepage = 'https://github.com/fhir-crucible/fhir_client'
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = 'exe'
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_dependency 'activesupport', '>= 3'
|
24
|
+
spec.add_dependency 'addressable', '>= 2.3'
|
25
|
+
spec.add_dependency 'fhir_models', '>= 1.6.6'
|
26
|
+
spec.add_dependency 'nokogiri'
|
27
|
+
spec.add_dependency 'oauth2', '~> 1.1'
|
28
|
+
spec.add_dependency 'rack', '>= 1.5'
|
29
|
+
spec.add_dependency 'rest-client', '~> 1.8'
|
30
|
+
spec.add_dependency 'tilt', '>= 1.1'
|
31
|
+
|
32
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
33
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
34
|
+
spec.add_development_dependency 'pry'
|
35
|
+
spec.add_development_dependency 'webmock'
|
36
|
+
spec.add_development_dependency 'test-unit'
|
37
|
+
spec.add_development_dependency 'simplecov'
|
25
38
|
end
|
data/lib/fhir_client.rb
CHANGED
@@ -1,26 +1,21 @@
|
|
1
|
-
# Top level include file that brings in all the necessary code
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'yaml'
|
4
|
-
require 'nokogiri'
|
5
1
|
require 'fhir_models'
|
6
|
-
require 'rest_client'
|
7
|
-
require 'addressable/uri'
|
8
|
-
require 'oauth2'
|
9
|
-
require 'active_support'
|
10
2
|
require 'active_support/all'
|
11
3
|
|
12
|
-
root = File.expand_path '
|
13
|
-
Dir.glob(File.join(root, '
|
4
|
+
root = File.expand_path '.', File.dirname(File.absolute_path(__FILE__))
|
5
|
+
Dir.glob(File.join(root, 'fhir_client', 'sections', '**', '*.rb')).each do |file|
|
14
6
|
require file
|
15
7
|
end
|
16
|
-
Dir.glob(File.join(root, '
|
8
|
+
Dir.glob(File.join(root, 'fhir_client', 'ext', '**', '*.rb')).each do |file|
|
17
9
|
require file
|
18
10
|
end
|
19
11
|
|
20
|
-
require_relative
|
21
|
-
require_relative
|
22
|
-
require_relative
|
23
|
-
require_relative
|
24
|
-
require_relative
|
25
|
-
require_relative
|
26
|
-
|
12
|
+
require_relative 'fhir_client/client'
|
13
|
+
require_relative 'fhir_client/resource_address'
|
14
|
+
require_relative 'fhir_client/resource_format'
|
15
|
+
require_relative 'fhir_client/patch_format'
|
16
|
+
require_relative 'fhir_client/client_exception'
|
17
|
+
require_relative 'fhir_client/version'
|
18
|
+
|
19
|
+
Dir.glob(File.join(root, 'fhir_client', 'model', '**', '*.rb')).each do |file|
|
20
|
+
require file
|
21
|
+
end
|
@@ -0,0 +1,533 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'addressable/uri'
|
4
|
+
require 'oauth2'
|
5
|
+
|
6
|
+
module FHIR
|
7
|
+
class Client
|
8
|
+
include FHIR::Sections::History
|
9
|
+
include FHIR::Sections::Crud
|
10
|
+
include FHIR::Sections::Validate
|
11
|
+
include FHIR::Sections::Tags
|
12
|
+
include FHIR::Sections::Feed
|
13
|
+
include FHIR::Sections::Search
|
14
|
+
include FHIR::Sections::Operations
|
15
|
+
include FHIR::Sections::Transactions
|
16
|
+
|
17
|
+
attr_accessor :reply
|
18
|
+
attr_accessor :use_format_param
|
19
|
+
attr_accessor :use_basic_auth
|
20
|
+
attr_accessor :use_oauth2_auth
|
21
|
+
attr_accessor :security_headers
|
22
|
+
attr_accessor :client
|
23
|
+
|
24
|
+
attr_accessor :default_format
|
25
|
+
|
26
|
+
attr_accessor :cached_conformance
|
27
|
+
|
28
|
+
# Call method to initialize FHIR client. This method must be invoked
|
29
|
+
# with a valid base server URL prior to using the client.
|
30
|
+
#
|
31
|
+
# @param base_service_url Base service URL for FHIR Service.
|
32
|
+
# @param default_format Default Format Mime type
|
33
|
+
# @return
|
34
|
+
#
|
35
|
+
def initialize(base_service_url, default_format: FHIR::Formats::ResourceFormat::RESOURCE_XML)
|
36
|
+
@base_service_url = base_service_url
|
37
|
+
FHIR.logger.info "Initializing client with #{@base_service_url}"
|
38
|
+
@use_format_param = false
|
39
|
+
@default_format = default_format
|
40
|
+
set_no_auth
|
41
|
+
end
|
42
|
+
|
43
|
+
def default_json
|
44
|
+
@default_format = FHIR::Formats::ResourceFormat::RESOURCE_JSON
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_xml
|
48
|
+
@default_format = FHIR::Formats::ResourceFormat::RESOURCE_XML
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set the client to use no authentication mechanisms
|
52
|
+
def set_no_auth
|
53
|
+
FHIR.logger.info 'Configuring the client to use no authentication.'
|
54
|
+
@use_oauth2_auth = false
|
55
|
+
@use_basic_auth = false
|
56
|
+
@security_headers = {}
|
57
|
+
@client = RestClient
|
58
|
+
end
|
59
|
+
|
60
|
+
# Set the client to use HTTP Basic Authentication
|
61
|
+
def set_basic_auth(client, secret)
|
62
|
+
FHIR.logger.info 'Configuring the client to use HTTP Basic authentication.'
|
63
|
+
token = Base64.encode64("#{client}:#{secret}")
|
64
|
+
value = "Basic #{token}"
|
65
|
+
@security_headers = { 'Authorization' => value }
|
66
|
+
@use_oauth2_auth = false
|
67
|
+
@use_basic_auth = true
|
68
|
+
@client = RestClient
|
69
|
+
end
|
70
|
+
|
71
|
+
# Set the client to use Bearer Token Authentication
|
72
|
+
def set_bearer_token(token)
|
73
|
+
FHIR.logger.info 'Configuring the client to use Bearer Token authentication.'
|
74
|
+
value = "Bearer #{token}"
|
75
|
+
@security_headers = { 'Authorization' => value }
|
76
|
+
@use_oauth2_auth = false
|
77
|
+
@use_basic_auth = true
|
78
|
+
@client = RestClient
|
79
|
+
end
|
80
|
+
|
81
|
+
# Set the client to use OpenID Connect OAuth2 Authentication
|
82
|
+
# client -- client id
|
83
|
+
# secret -- client secret
|
84
|
+
# authorize_path -- absolute path of authorization endpoint
|
85
|
+
# token_path -- absolute path of token endpoint
|
86
|
+
def set_oauth2_auth(client, secret, authorize_path, token_path)
|
87
|
+
FHIR.logger.info 'Configuring the client to use OpenID Connect OAuth2 authentication.'
|
88
|
+
@use_oauth2_auth = true
|
89
|
+
@use_basic_auth = false
|
90
|
+
@security_headers = {}
|
91
|
+
options = {
|
92
|
+
site: @base_service_url,
|
93
|
+
authorize_url: authorize_path,
|
94
|
+
token_url: token_path,
|
95
|
+
raise_errors: true
|
96
|
+
}
|
97
|
+
client = OAuth2::Client.new(client, secret, options)
|
98
|
+
@client = client.client_credentials.get_token
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get the OAuth2 server and endpoints from the conformance statement
|
102
|
+
# (the server should not require OAuth2 or other special security to access
|
103
|
+
# the conformance statement).
|
104
|
+
# <rest>
|
105
|
+
# <mode value="server"/>
|
106
|
+
# <documentation value="All the functionality defined in FHIR"/>
|
107
|
+
# <security>
|
108
|
+
# <extension url="http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris">
|
109
|
+
# <extension url="register">
|
110
|
+
# <valueUri value="https://authorize-dstu2.smarthealthit.org/register"/>
|
111
|
+
# </extension>
|
112
|
+
# <extension url="authorize">
|
113
|
+
# <valueUri value="https://authorize-dstu2.smarthealthit.org/authorize"/>
|
114
|
+
# </extension>
|
115
|
+
# <extension url="token">
|
116
|
+
# <valueUri value="https://authorize-dstu2.smarthealthit.org/token"/>
|
117
|
+
# </extension>
|
118
|
+
# </extension>
|
119
|
+
# <service>
|
120
|
+
# <coding>
|
121
|
+
# <system value="http://hl7.org/fhir/vs/restful-security-service"/>
|
122
|
+
# <code value="OAuth2"/>
|
123
|
+
# </coding>
|
124
|
+
# <text value="OAuth version 2 (see oauth.net)."/>
|
125
|
+
# </service>
|
126
|
+
# <description value="SMART on FHIR uses OAuth2 for authorization"/>
|
127
|
+
# </security>
|
128
|
+
def get_oauth2_metadata_from_conformance
|
129
|
+
options = {
|
130
|
+
authorize_url: nil,
|
131
|
+
token_url: nil
|
132
|
+
}
|
133
|
+
oauth_extension = 'http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris'
|
134
|
+
authorize_extension = 'authorize'
|
135
|
+
token_extension = 'token'
|
136
|
+
begin
|
137
|
+
conformance = conformance_statement
|
138
|
+
conformance.rest.each do |rest|
|
139
|
+
rest.security.service.each do |service|
|
140
|
+
service.coding.each do |coding|
|
141
|
+
next unless coding.code == 'SMART-on-FHIR'
|
142
|
+
rest.security.extension.where(url: oauth_extension).first.extension.each do |ext|
|
143
|
+
case ext.url
|
144
|
+
when authorize_extension
|
145
|
+
options[:authorize_url] = ext.value.value
|
146
|
+
when "#{oauth_extension}\##{authorize_extension}"
|
147
|
+
options[:authorize_url] = ext.value.value
|
148
|
+
when token_extension
|
149
|
+
options[:token_url] = ext.value.value
|
150
|
+
when "#{oauth_extension}\##{token_extension}"
|
151
|
+
options[:token_url] = ext.value.value
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
rescue
|
158
|
+
FHIR.logger.error 'Failed to locate SMART-on-FHIR OAuth2 Security Extensions.'
|
159
|
+
end
|
160
|
+
options.delete_if { |_k, v| v.nil? }
|
161
|
+
options.clear if options.keys.size != 2
|
162
|
+
options
|
163
|
+
end
|
164
|
+
|
165
|
+
# Method returns a conformance statement for the system queried.
|
166
|
+
# @return
|
167
|
+
def conformance_statement(format = FHIR::Formats::ResourceFormat::RESOURCE_XML)
|
168
|
+
if @cached_conformance.nil? || format != @default_format
|
169
|
+
try_conformance_formats(format)
|
170
|
+
end
|
171
|
+
@cached_conformance
|
172
|
+
end
|
173
|
+
|
174
|
+
def try_conformance_formats(default_format)
|
175
|
+
formats = [FHIR::Formats::ResourceFormat::RESOURCE_XML,
|
176
|
+
FHIR::Formats::ResourceFormat::RESOURCE_JSON,
|
177
|
+
FHIR::Formats::ResourceFormat::RESOURCE_XML_DSTU2,
|
178
|
+
FHIR::Formats::ResourceFormat::RESOURCE_JSON_DSTU2,
|
179
|
+
'application/xml',
|
180
|
+
'application/json']
|
181
|
+
formats.insert(0, default_format)
|
182
|
+
|
183
|
+
@cached_conformance = nil
|
184
|
+
@default_format = nil
|
185
|
+
|
186
|
+
formats.each do |frmt|
|
187
|
+
reply = get 'metadata', fhir_headers(format: frmt)
|
188
|
+
next unless reply.code == 200
|
189
|
+
@cached_conformance = parse_reply(FHIR::Conformance, frmt, reply)
|
190
|
+
@default_format = frmt
|
191
|
+
break
|
192
|
+
end
|
193
|
+
@default_format = default_format if @default_format.nil?
|
194
|
+
@default_format
|
195
|
+
end
|
196
|
+
|
197
|
+
def resource_url(options)
|
198
|
+
FHIR::ResourceAddress.new.resource_url(options, @use_format_param)
|
199
|
+
end
|
200
|
+
|
201
|
+
def full_resource_url(options)
|
202
|
+
@base_service_url + resource_url(options)
|
203
|
+
end
|
204
|
+
|
205
|
+
def fhir_headers(options = {})
|
206
|
+
FHIR::ResourceAddress.new.fhir_headers(options, @use_format_param)
|
207
|
+
end
|
208
|
+
|
209
|
+
def parse_reply(klass, format, response)
|
210
|
+
FHIR.logger.info "Parsing response with {klass: #{klass}, format: #{format}, code: #{response.code}}."
|
211
|
+
return nil unless [200, 201].include? response.code
|
212
|
+
res = nil
|
213
|
+
begin
|
214
|
+
res = FHIR.from_contents(response.body)
|
215
|
+
res.client = self unless res.nil?
|
216
|
+
FHIR.logger.warn "Expected #{klass} but got #{res.class}" if res.class != klass
|
217
|
+
rescue => e
|
218
|
+
FHIR.logger.error "Failed to parse #{format} as resource #{klass}: #{e.message} %n #{e.backtrace.join("\n")} #{response}"
|
219
|
+
nil
|
220
|
+
end
|
221
|
+
res
|
222
|
+
end
|
223
|
+
|
224
|
+
def strip_base(path)
|
225
|
+
path.gsub(@base_service_url, '')
|
226
|
+
end
|
227
|
+
|
228
|
+
def reissue_request(request)
|
229
|
+
if [:get, :delete, :head].include?(request['method'])
|
230
|
+
method(request['method']).call(request['url'], request['headers'])
|
231
|
+
elsif [:post, :put].include?(request['method'])
|
232
|
+
resource = FHIR.from_contents(request['payload']) unless request['payload'].nil?
|
233
|
+
method(request['method']).call(request['url'], resource, request['headers'])
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
def base_path(path)
|
240
|
+
if path.start_with?('/')
|
241
|
+
if @base_service_url.end_with?('/')
|
242
|
+
@base_service_url.chop
|
243
|
+
else
|
244
|
+
@base_service_url
|
245
|
+
end
|
246
|
+
else
|
247
|
+
@base_service_url + '/'
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Extract the request payload in the specified format, defaults to XML
|
252
|
+
def request_payload(resource, headers)
|
253
|
+
if headers
|
254
|
+
format_specified = headers[:format] || headers['format']
|
255
|
+
if format_specified.downcase.include?('xml')
|
256
|
+
resource.to_xml
|
257
|
+
elsif format_specified.downcase.include?('json')
|
258
|
+
resource.to_json
|
259
|
+
else
|
260
|
+
resource.to_xml
|
261
|
+
end
|
262
|
+
else
|
263
|
+
resource.to_xml
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def request_patch_payload(patchset, format)
|
268
|
+
if format == FHIR::Formats::PatchFormat::PATCH_JSON
|
269
|
+
patchset.each do |patch|
|
270
|
+
# remove the resource name from the patch path, since the JSON representation doesn't have that
|
271
|
+
patch[:path] = patch[:path].slice(patch[:path].index('/')..-1)
|
272
|
+
end
|
273
|
+
patchset.to_json
|
274
|
+
elsif format == FHIR::Formats::PatchFormat::PATCH_XML
|
275
|
+
builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
|
276
|
+
patchset.each do |patch|
|
277
|
+
xml.diff do
|
278
|
+
# TODO: support other kinds besides just replace
|
279
|
+
xml.replace(patch[:value], sel: patch[:path] + '/@value') if patch[:op] == 'replace'
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
builder.to_xml
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def clean_headers(headers)
|
288
|
+
headers.delete_if { |k, v| (k.nil? || v.nil?) }
|
289
|
+
headers.each_with_object({}) { |(k, v), h| h[k.to_s] = v.to_s; h }
|
290
|
+
end
|
291
|
+
|
292
|
+
def scrubbed_response_headers(result)
|
293
|
+
result.each_key do |k|
|
294
|
+
v = result[k]
|
295
|
+
result[k] = v[0] if v.is_a? Array
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def get(path, headers)
|
300
|
+
url = Addressable::URI.parse(build_url(path)).to_s
|
301
|
+
FHIR.logger.info "GETTING: #{url}"
|
302
|
+
headers = clean_headers(headers)
|
303
|
+
if @use_oauth2_auth
|
304
|
+
# @client.refresh!
|
305
|
+
begin
|
306
|
+
response = @client.get(url, headers: headers)
|
307
|
+
rescue => e
|
308
|
+
response = e.response if e.response
|
309
|
+
end
|
310
|
+
req = {
|
311
|
+
method: :get,
|
312
|
+
url: url,
|
313
|
+
path: url.gsub(@base_service_url, ''),
|
314
|
+
headers: headers,
|
315
|
+
payload: nil
|
316
|
+
}
|
317
|
+
res = {
|
318
|
+
code: response.status.to_s,
|
319
|
+
headers: response.headers,
|
320
|
+
body: response.body
|
321
|
+
}
|
322
|
+
FHIR.logger.info "GET - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
|
323
|
+
@reply = FHIR::ClientReply.new(req, res)
|
324
|
+
else
|
325
|
+
headers.merge!(@security_headers) if @use_basic_auth
|
326
|
+
begin
|
327
|
+
response = @client.get(url, headers)
|
328
|
+
rescue => e
|
329
|
+
response = e.response if e.response
|
330
|
+
end
|
331
|
+
|
332
|
+
FHIR.logger.info "GET - Request: #{response.request.to_json}, Response: #{response.body.force_encoding('UTF-8')}"
|
333
|
+
response.request.args[:path] = response.request.args[:url].gsub(@base_service_url, '')
|
334
|
+
headers = response.headers.each_with_object({}) { |(k, v), h| h[k.to_s.tr('_', '-')] = v.to_s; h }
|
335
|
+
res = {
|
336
|
+
code: response.code,
|
337
|
+
headers: scrubbed_response_headers(headers),
|
338
|
+
body: response.body
|
339
|
+
}
|
340
|
+
|
341
|
+
@reply = FHIR::ClientReply.new(response.request.args, res)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def post(path, resource, headers)
|
346
|
+
url = URI(build_url(path)).to_s
|
347
|
+
FHIR.logger.info "POSTING: #{url}"
|
348
|
+
headers = clean_headers(headers)
|
349
|
+
payload = request_payload(resource, headers) if resource
|
350
|
+
if @use_oauth2_auth
|
351
|
+
# @client.refresh!
|
352
|
+
begin
|
353
|
+
response = @client.post(url, headers: headers, body: payload)
|
354
|
+
rescue => e
|
355
|
+
response = e.response if e.response
|
356
|
+
end
|
357
|
+
req = {
|
358
|
+
method: :post,
|
359
|
+
url: url,
|
360
|
+
path: url.gsub(@base_service_url, ''),
|
361
|
+
headers: headers,
|
362
|
+
payload: payload
|
363
|
+
}
|
364
|
+
res = {
|
365
|
+
code: response.status.to_s,
|
366
|
+
headers: response.headers,
|
367
|
+
body: response.body
|
368
|
+
}
|
369
|
+
FHIR.logger.info "POST - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
|
370
|
+
@reply = FHIR::ClientReply.new(req, res)
|
371
|
+
else
|
372
|
+
headers.merge!(@security_headers) if @use_basic_auth
|
373
|
+
@client.post(url, payload, headers) do |resp, request, result|
|
374
|
+
FHIR.logger.info "POST - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
|
375
|
+
request.args[:path] = url.gsub(@base_service_url, '')
|
376
|
+
res = {
|
377
|
+
code: result.code,
|
378
|
+
headers: scrubbed_response_headers(result.each_key {}),
|
379
|
+
body: resp
|
380
|
+
}
|
381
|
+
@reply = FHIR::ClientReply.new(request.args, res)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def put(path, resource, headers)
|
387
|
+
url = URI(build_url(path)).to_s
|
388
|
+
FHIR.logger.info "PUTTING: #{url}"
|
389
|
+
headers = clean_headers(headers)
|
390
|
+
payload = request_payload(resource, headers) if resource
|
391
|
+
if @use_oauth2_auth
|
392
|
+
# @client.refresh!
|
393
|
+
begin
|
394
|
+
response = @client.put(url, headers: headers, body: payload)
|
395
|
+
rescue => e
|
396
|
+
response = e.response if e.response
|
397
|
+
end
|
398
|
+
req = {
|
399
|
+
method: :put,
|
400
|
+
url: url,
|
401
|
+
path: url.gsub(@base_service_url, ''),
|
402
|
+
headers: headers,
|
403
|
+
payload: payload
|
404
|
+
}
|
405
|
+
res = {
|
406
|
+
code: response.status.to_s,
|
407
|
+
headers: response.headers,
|
408
|
+
body: response.body
|
409
|
+
}
|
410
|
+
FHIR.logger.info "PUT - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
|
411
|
+
@reply = FHIR::ClientReply.new(req, res)
|
412
|
+
else
|
413
|
+
headers.merge!(@security_headers) if @use_basic_auth
|
414
|
+
@client.put(url, payload, headers) do |resp, request, result|
|
415
|
+
FHIR.logger.info "PUT - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
|
416
|
+
request.args[:path] = url.gsub(@base_service_url, '')
|
417
|
+
res = {
|
418
|
+
code: result.code,
|
419
|
+
headers: scrubbed_response_headers(result.each_key {}),
|
420
|
+
body: resp
|
421
|
+
}
|
422
|
+
@reply = FHIR::ClientReply.new(request.args, res)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
def patch(path, patchset, headers)
|
428
|
+
url = URI(build_url(path)).to_s
|
429
|
+
FHIR.logger.info "PATCHING: #{url}"
|
430
|
+
headers = clean_headers(headers)
|
431
|
+
payload = request_patch_payload(patchset, headers['format'])
|
432
|
+
if @use_oauth2_auth
|
433
|
+
# @client.refresh!
|
434
|
+
begin
|
435
|
+
response = @client.patch(url, headers: headers, body: payload)
|
436
|
+
rescue => e
|
437
|
+
response = e.response if e.response
|
438
|
+
end
|
439
|
+
req = {
|
440
|
+
method: :patch,
|
441
|
+
url: url,
|
442
|
+
path: url.gsub(@base_service_url, ''),
|
443
|
+
headers: headers,
|
444
|
+
payload: payload
|
445
|
+
}
|
446
|
+
res = {
|
447
|
+
code: response.status.to_s,
|
448
|
+
headers: response.headers,
|
449
|
+
body: response.body
|
450
|
+
}
|
451
|
+
FHIR.logger.info "PATCH - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
|
452
|
+
@reply = FHIR::ClientReply.new(req, res)
|
453
|
+
else
|
454
|
+
headers.merge!(@security_headers) if @use_basic_auth
|
455
|
+
# url = 'http://requestb.in/o8juy3o8'
|
456
|
+
@client.patch(url, payload, headers) do |resp, request, result|
|
457
|
+
FHIR.logger.info "PATCH - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
|
458
|
+
request.args[:path] = url.gsub(@base_service_url, '')
|
459
|
+
res = {
|
460
|
+
code: result.code,
|
461
|
+
headers: scrubbed_response_headers(result.each_key {}),
|
462
|
+
body: resp
|
463
|
+
}
|
464
|
+
@reply = FHIR::ClientReply.new(request.args, res)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
def delete(path, headers)
|
470
|
+
url = URI(build_url(path)).to_s
|
471
|
+
FHIR.logger.info "DELETING: #{url}"
|
472
|
+
headers = clean_headers(headers)
|
473
|
+
if @use_oauth2_auth
|
474
|
+
# @client.refresh!
|
475
|
+
begin
|
476
|
+
response = @client.delete(url, headers: headers)
|
477
|
+
rescue => e
|
478
|
+
response = e.response if e.response
|
479
|
+
end
|
480
|
+
req = {
|
481
|
+
method: :delete,
|
482
|
+
url: url,
|
483
|
+
path: url.gsub(@base_service_url, ''),
|
484
|
+
headers: headers,
|
485
|
+
payload: nil
|
486
|
+
}
|
487
|
+
res = {
|
488
|
+
code: response.status.to_s,
|
489
|
+
headers: response.headers,
|
490
|
+
body: response.body
|
491
|
+
}
|
492
|
+
FHIR.logger.info "DELETE - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
|
493
|
+
@reply = FHIR::ClientReply.new(req, res)
|
494
|
+
else
|
495
|
+
headers.merge!(@security_headers) if @use_basic_auth
|
496
|
+
@client.delete(url, headers) do |resp, request, result|
|
497
|
+
FHIR.logger.info "DELETE - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
|
498
|
+
request.args[:path] = url.gsub(@base_service_url, '')
|
499
|
+
res = {
|
500
|
+
code: result.code,
|
501
|
+
headers: scrubbed_response_headers(result.each_key {}),
|
502
|
+
body: resp
|
503
|
+
}
|
504
|
+
@reply = FHIR::ClientReply.new(request.args, res)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
def head(path, headers)
|
510
|
+
headers.merge!(@security_headers) unless @security_headers.blank?
|
511
|
+
url = URI(build_url(path)).to_s
|
512
|
+
FHIR.logger.info "HEADING: #{url}"
|
513
|
+
RestClient.head(url, headers) do |response, request, result|
|
514
|
+
FHIR.logger.info "HEAD - Request: #{request.to_json}, Response: #{response.force_encoding('UTF-8')}"
|
515
|
+
request.args[:path] = url.gsub(@base_service_url, '')
|
516
|
+
res = {
|
517
|
+
code: result.code,
|
518
|
+
headers: scrubbed_response_headers(result.each_key {}),
|
519
|
+
body: response
|
520
|
+
}
|
521
|
+
@reply = FHIR::ClientReply.new(request.args, res)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
def build_url(path)
|
526
|
+
if path =~ /^\w+:\/\//
|
527
|
+
path
|
528
|
+
else
|
529
|
+
"#{base_path(path)}#{path}"
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|