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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +34 -0
  3. data/.csslintrc +2 -0
  4. data/.eslintignore +1 -0
  5. data/.eslintrc +213 -0
  6. data/.rubocop.yml +1159 -0
  7. data/.travis.yml +8 -0
  8. data/Gemfile +5 -5
  9. data/Rakefile +14 -13
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/fhir_client.gemspec +34 -21
  13. data/lib/fhir_client.rb +13 -18
  14. data/lib/fhir_client/client.rb +533 -0
  15. data/lib/{client_exception.rb → fhir_client/client_exception.rb} +1 -3
  16. data/lib/{ext → fhir_client/ext}/bundle.rb +5 -7
  17. data/lib/{ext → fhir_client/ext}/model.rb +10 -2
  18. data/lib/fhir_client/ext/reference.rb +28 -0
  19. data/lib/{fhir_api_validation.json → fhir_client/fhir_api_validation.json} +0 -0
  20. data/lib/{model → fhir_client/model}/client_reply.rb +47 -51
  21. data/lib/{model → fhir_client/model}/tag.rb +6 -7
  22. data/lib/fhir_client/patch_format.rb +8 -0
  23. data/lib/{resource_address.rb → fhir_client/resource_address.rb} +135 -139
  24. data/lib/fhir_client/resource_format.rb +11 -0
  25. data/lib/{sections → fhir_client/sections}/crud.rb +28 -30
  26. data/lib/{sections → fhir_client/sections}/feed.rb +1 -4
  27. data/lib/{sections → fhir_client/sections}/history.rb +14 -15
  28. data/lib/{sections → fhir_client/sections}/operations.rb +22 -28
  29. data/lib/fhir_client/sections/search.rb +52 -0
  30. data/lib/{sections → fhir_client/sections}/tags.rb +12 -12
  31. data/lib/{sections → fhir_client/sections}/transactions.rb +17 -20
  32. data/lib/{sections → fhir_client/sections}/validate.rb +12 -15
  33. data/lib/{tasks → fhir_client/tasks}/tasks.rake +19 -18
  34. data/lib/fhir_client/version.rb +5 -0
  35. metadata +127 -83
  36. data/lib/client_interface.rb +0 -533
  37. data/lib/ext/reference.rb +0 -11
  38. data/lib/patch_format.rb +0 -10
  39. data/lib/resource_format.rb +0 -13
  40. data/lib/sections/search.rb +0 -53
  41. data/test/fixtures/bundle-example.xml +0 -79
  42. data/test/fixtures/parameters-example.json +0 -18
  43. data/test/fixtures/parameters-example.xml +0 -17
  44. data/test/test_helper.rb +0 -8
  45. data/test/unit/basic_test.rb +0 -17
  46. data/test/unit/bundle_test.rb +0 -21
  47. 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 "http://rubygems.org"
1
+ source 'https://rubygems.org'
2
+ ruby RUBY_VERSION
2
3
  gemspec
3
4
 
4
5
  group :test do
5
- gem 'simplecov', :require => false
6
- gem 'minitest', "~> 4.0"
7
- gem 'turn', :require => false
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 'rake'
1
+ require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
- require 'fileutils'
4
- require 'pry'
3
+ require 'rubocop/rake_task'
5
4
 
6
- require_relative 'lib/fhir_client'
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
- desc "Run basic tests"
14
- Rake::TestTask.new(:test_unit) do |t|
15
- t.libs << "test"
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
- task :test => [:test_unit] do
22
- system("open coverage/index.html")
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 :default => [:test]
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
@@ -0,0 +1,8 @@
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
data/fhir_client.gemspec CHANGED
@@ -1,25 +1,38 @@
1
- # -*- encoding: utf-8 -*-
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 |s|
4
- s.name = "fhir_client"
5
- s.summary = "A Gem for handling FHIR client requests in ruby"
6
- s.description = "A Gem for handling FHIR client requests in ruby"
7
- s.email = "aquina@mitre.org"
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
- s.files = s.files = `git ls-files`.split("\n")
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
- s.add_dependency 'fhir_models', '>= 1.6.1'
15
- s.add_dependency 'tilt', '>= 1.1'
16
- s.add_dependency 'rest-client', '~> 1.8'
17
- s.add_dependency 'oauth2', '~> 1.1'
18
- s.add_dependency 'activesupport', '>= 3'
19
- s.add_dependency 'addressable', '>= 2.3'
20
- s.add_dependency 'rack', '~> 1.5'
21
- s.add_dependency 'nokogiri'
22
- s.add_development_dependency 'pry'
23
- s.add_development_dependency 'minitest'
24
- s.add_development_dependency 'test-unit'
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 '..', File.dirname(File.absolute_path(__FILE__))
13
- Dir.glob(File.join(root, 'lib','sections','**','*.rb')).each do |file|
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, 'lib','ext','**','*.rb')).each do |file|
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 File.join('.','client_interface.rb')
21
- require_relative File.join('.','resource_address.rb')
22
- require_relative File.join('.','resource_format.rb')
23
- require_relative File.join('.','patch_format.rb')
24
- require_relative File.join('.','model','client_reply.rb')
25
- require_relative File.join('.','model','tag.rb')
26
- require_relative File.join('.','client_exception.rb')
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