fhir_client 1.6.3 → 1.6.7

Sign up to get free protection for your applications and to get access to all the features.
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