fhir_client 3.0.2 → 3.0.3

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.
data/Gemfile CHANGED
@@ -8,6 +8,6 @@ gemspec
8
8
 
9
9
  group :test do
10
10
  gem 'codeclimate-test-reporter', require: nil
11
- gem 'rubocop', '~> 0.43.0', require: false
11
+ gem 'rubocop', '~> 0.49.0', require: false
12
12
  gem 'awesome_print', require: 'ap'
13
13
  end
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Ruby FHIR client.
4
4
 
5
5
  Supports:
6
- * FHIR STU3
6
+ * FHIR STU3 and DSTU2
7
7
  * XML and JSON
8
8
  * All CRUD, including version read and history
9
9
  * Transactions and Batches
@@ -40,11 +40,14 @@ patient = FHIR::Patient.read('example')
40
40
 
41
41
  # update a patient
42
42
  patient.gender = 'female'
43
- patient.update # saves the patient
43
+ patient.update # saves the patient
44
44
 
45
45
  # create a patient
46
46
  patient = FHIR::Patient.create(name: {given: 'John', family: 'Doe'})
47
47
 
48
+ #create a patient with specific headers
49
+ patient = FHIR::Patient.new(name: {given: 'John', family: 'Doe'}).create({Prefer: "return=representation"})
50
+
48
51
  # search patients
49
52
  results = FHIR::Patient.search(given: 'John', family: 'Doe')
50
53
  results.count # results in an enumeration
@@ -55,6 +58,32 @@ patient.destroy
55
58
 
56
59
  ## Advanced Usage
57
60
 
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.
63
+
64
+ ```ruby
65
+ # autodetect the FHIR version
66
+ client = FHIR::Client.new(url)
67
+ version = client.detect_version
68
+ if version == :stu3
69
+ puts 'FHIR Client using STU3'
70
+ elsif version == :dstu2
71
+ puts 'FHIR Client using DSTU2'
72
+ end
73
+
74
+ # tell the client to use STU3 (default)
75
+ client.use_stu3
76
+ # now use the client normally
77
+ patient = FHIR::Patient.read('example')
78
+ patient = client.read(FHIR::Patient, 'example').resource
79
+
80
+ # tell the client to use DSTU2
81
+ client.use_dstu2
82
+ # now use the client with the DSTU2 models
83
+ patient = FHIR::DSTU2::Patient.read('example')
84
+ patient = client.read(FHIR::DSTU2::Patient, 'example').resource
85
+ ```
86
+
58
87
  ### CRUD Examples
59
88
  ```ruby
60
89
  # read an existing patient with id "example"
@@ -22,7 +22,8 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_dependency 'activesupport', '>= 3'
24
24
  spec.add_dependency 'addressable', '>= 2.3'
25
- spec.add_dependency 'fhir_models', '>= 3.0.0'
25
+ spec.add_dependency 'fhir_models', '>= 3.0.2'
26
+ spec.add_dependency 'fhir_dstu2_models', '>= 1.0.3'
26
27
  spec.add_dependency 'nokogiri'
27
28
  spec.add_dependency 'oauth2', '~> 1.1'
28
29
  spec.add_dependency 'rack', '>= 1.5'
@@ -1,4 +1,5 @@
1
1
  require 'fhir_models'
2
+ require 'fhir_dstu2_models'
2
3
  require 'active_support/all'
3
4
 
4
5
  root = File.expand_path '.', File.dirname(File.absolute_path(__FILE__))
@@ -21,7 +21,7 @@ module FHIR
21
21
  attr_accessor :client
22
22
 
23
23
  attr_accessor :default_format
24
-
24
+ attr_accessor :fhir_version
25
25
  attr_accessor :cached_capability_statement
26
26
 
27
27
  # Call method to initialize FHIR client. This method must be invoked
@@ -36,15 +36,63 @@ module FHIR
36
36
  FHIR.logger.info "Initializing client with #{@base_service_url}"
37
37
  @use_format_param = false
38
38
  @default_format = default_format
39
+ @fhir_version = :stu3
39
40
  set_no_auth
40
41
  end
41
42
 
42
43
  def default_json
43
- @default_format = FHIR::Formats::ResourceFormat::RESOURCE_JSON
44
+ @default_format = if @fhir_version == :stu3
45
+ FHIR::Formats::ResourceFormat::RESOURCE_JSON
46
+ else
47
+ FHIR::Formats::ResourceFormat::RESOURCE_JSON_DSTU2
48
+ end
44
49
  end
45
50
 
46
51
  def default_xml
47
- @default_format = FHIR::Formats::ResourceFormat::RESOURCE_XML
52
+ @default_format = if @fhir_version == :stu3
53
+ FHIR::Formats::ResourceFormat::RESOURCE_XML
54
+ else
55
+ FHIR::Formats::ResourceFormat::RESOURCE_XML_DSTU2
56
+ end
57
+ end
58
+
59
+ def use_stu3
60
+ @fhir_version = :stu3
61
+ @default_format = if @default_format.include?('xml')
62
+ FHIR::Formats::ResourceFormat::RESOURCE_XML
63
+ else
64
+ FHIR::Formats::ResourceFormat::RESOURCE_JSON
65
+ end
66
+ end
67
+
68
+ def use_dstu2
69
+ @fhir_version = :dstu2
70
+ @default_format = if @default_format.include?('xml')
71
+ FHIR::Formats::ResourceFormat::RESOURCE_XML_DSTU2
72
+ else
73
+ FHIR::Formats::ResourceFormat::RESOURCE_JSON_DSTU2
74
+ end
75
+ end
76
+
77
+ def versioned_resource_class(klass)
78
+ if @fhir_version == :stu3
79
+ FHIR.const_get(klass)
80
+ else
81
+ FHIR::DSTU2.const_get(klass)
82
+ end
83
+ end
84
+
85
+ def detect_version
86
+ cap = capability_statement
87
+ if cap.is_a?(FHIR::CapabilityStatement)
88
+ @fhir_version = :stu3
89
+ elsif cap.is_a?(FHIR::DSTU2::Conformance)
90
+ @fhir_version = :dstu2
91
+ else
92
+ @fhir_version = :stu3
93
+ end
94
+ FHIR.logger.info("Detecting server FHIR version as #{@fhir_version} via metadata")
95
+ @fhir_version
48
96
  end
49
97
 
50
98
  # Set the client to use no authentication mechanisms
@@ -189,9 +237,22 @@ module FHIR
189
237
  formats.each do |frmt|
190
238
  reply = get 'metadata', fhir_headers(format: frmt)
191
239
  next unless reply.code == 200
192
- @cached_capability_statement = parse_reply(FHIR::CapabilityStatement, frmt, reply)
193
- @default_format = frmt
194
- break
240
+ begin
241
+ @cached_capability_statement = parse_reply(FHIR::CapabilityStatement, frmt, reply)
242
+ rescue
243
+ @cached_capability_statement = nil
244
+ end
245
+ unless @cached_capability_statement
246
+ begin
247
+ @cached_capability_statement = parse_reply(FHIR::DSTU2::Conformance, frmt, reply)
248
+ rescue
249
+ @cached_capability_statement = nil
250
+ end
251
+ end
252
+ if @cached_capability_statement
253
+ @default_format = frmt
254
+ break
255
+ end
195
256
  end
196
257
  @default_format = default_format if @default_format.nil?
197
258
  @default_format
@@ -210,16 +271,28 @@ module FHIR
210
271
  end
211
272
 
212
273
  def parse_reply(klass, format, response)
213
- FHIR.logger.info "Parsing response with {klass: #{klass}, format: #{format}, code: #{response.code}}."
274
+ FHIR.logger.debug "Parsing response with {klass: #{klass}, format: #{format}, code: #{response.code}}."
214
275
  return nil unless [200, 201].include? response.code
215
276
  res = nil
216
277
  begin
217
- res = FHIR.from_contents(response.body)
278
+ res = if(@fhir_version == :dstu2 || klass.ancestors.include?(FHIR::DSTU2::Model))
279
+ if(format.include?('xml'))
280
+ FHIR::DSTU2::Xml.from_xml(response.body)
281
+ else
282
+ FHIR::DSTU2::Json.from_json(response.body)
283
+ end
284
+ else
285
+ if(format.include?('xml'))
286
+ FHIR::Xml.from_xml(response.body)
287
+ else
288
+ FHIR::Json.from_json(response.body)
289
+ end
290
+ end
218
291
  res.client = self unless res.nil?
219
292
  FHIR.logger.warn "Expected #{klass} but got #{res.class}" if res.class != klass
220
293
  rescue => e
221
- FHIR.logger.error "Failed to parse #{format} as resource #{klass}: #{e.message} %n #{e.backtrace.join("\n")} #{response}"
222
- nil
294
+ FHIR.logger.error "Failed to parse #{format} as resource #{klass}: #{e.message}"
295
+ res = nil
223
296
  end
224
297
  res
225
298
  end
@@ -232,7 +305,13 @@ module FHIR
232
305
  if [:get, :delete, :head].include?(request['method'])
233
306
  method(request['method']).call(request['url'], request['headers'])
234
307
  elsif [:post, :put].include?(request['method'])
235
- resource = FHIR.from_contents(request['payload']) unless request['payload'].nil?
308
+ unless request['payload'].nil?
309
+ resource = if @fhir_version == :stu3
310
+ FHIR.from_contents(request['payload'])
311
+ else
312
+ FHIR::DSTU2.from_contents(request['payload'])
313
+ end
314
+ end
236
315
  method(request['method']).call(request['url'], resource, request['headers'])
237
316
  end
238
317
  end
@@ -308,6 +387,12 @@ module FHIR
308
387
  begin
309
388
  response = @client.get(url, headers: headers)
310
389
  rescue => e
390
+ unless e.response
391
+ # Re-raise the client error if there's no response. Otherwise, logging
392
+ # and other things break below!
393
+ FHIR.logger.error "GET - Request: #{url} failed! No response from server: #{e}"
394
+ raise # Re-raise the same error we caught.
395
+ end
311
396
  response = e.response if e.response
312
397
  end
313
398
  req = {
@@ -323,11 +408,11 @@ module FHIR
323
408
  body: response.body
324
409
  }
325
410
  if url.end_with?('/metadata')
326
- FHIR.logger.info "GET - Request: #{req}, Response: [too large]"
411
+ FHIR.logger.debug "GET - Request: #{req}, Response: [too large]"
327
412
  else
328
- FHIR.logger.info "GET - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
413
+ FHIR.logger.debug "GET - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
329
414
  end
330
- @reply = FHIR::ClientReply.new(req, res)
415
+ @reply = FHIR::ClientReply.new(req, res, self)
331
416
  else
332
417
  headers.merge!(@security_headers) if @use_basic_auth
333
418
  begin
@@ -346,15 +431,21 @@ module FHIR
346
431
  headers: nil,
347
432
  body: sslerr.message
348
433
  }
349
- @reply = FHIR::ClientReply.new(req, res)
434
+ @reply = FHIR::ClientReply.new(req, res, self)
350
435
  return @reply
351
436
  rescue => e
352
- response = e.response if e.response
437
+ unless e.response
438
+ # Re-raise the client error if there's no response. Otherwise, logging
439
+ # and other things break below!
440
+ FHIR.logger.error "GET - Request: #{url} failed! No response from server: #{e}"
441
+ raise # Re-raise the same error we caught.
442
+ end
443
+ response = e.response
353
444
  end
354
445
  if url.end_with?('/metadata')
355
- FHIR.logger.info "GET - Request: #{response.request.to_json}, Response: [too large]"
446
+ FHIR.logger.debug "GET - Request: #{response.request.to_json}, Response: [too large]"
356
447
  else
357
- FHIR.logger.info "GET - Request: #{response.request.to_json}, Response: #{response.body.force_encoding('UTF-8')}"
448
+ FHIR.logger.debug "GET - Request: #{response.request.to_json}, Response: #{response.body.force_encoding('UTF-8')}"
358
449
  end
359
450
  response.request.args[:path] = response.request.args[:url].gsub(@base_service_url, '')
360
451
  headers = response.headers.each_with_object({}) { |(k, v), h| h[k.to_s.tr('_', '-')] = v.to_s; h }
@@ -364,7 +455,7 @@ module FHIR
364
455
  body: response.body
365
456
  }
366
457
 
367
- @reply = FHIR::ClientReply.new(response.request.args, res)
458
+ @reply = FHIR::ClientReply.new(response.request.args, res, self)
368
459
  end
369
460
  end
370
461
 
@@ -378,6 +469,12 @@ module FHIR
378
469
  begin
379
470
  response = @client.post(url, headers: headers, body: payload)
380
471
  rescue => e
472
+ unless e.response
473
+ # Re-raise the client error if there's no response. Otherwise, logging
474
+ # and other things break below!
475
+ FHIR.logger.error "POST - Request: #{url} failed! No response from server: #{e}"
476
+ raise # Re-raise the same error we caught.
477
+ end
381
478
  response = e.response if e.response
382
479
  end
383
480
  req = {
@@ -392,19 +489,19 @@ module FHIR
392
489
  headers: response.headers,
393
490
  body: response.body
394
491
  }
395
- FHIR.logger.info "POST - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
396
- @reply = FHIR::ClientReply.new(req, res)
492
+ FHIR.logger.debug "POST - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
493
+ @reply = FHIR::ClientReply.new(req, res, self)
397
494
  else
398
495
  headers.merge!(@security_headers) if @use_basic_auth
399
496
  @client.post(url, payload, headers) do |resp, request, result|
400
- FHIR.logger.info "POST - Request: #{request.to_json}\nResponse:\nResponse Headers: #{scrubbed_response_headers(result.each_key {})} \nResponse Body: #{resp.force_encoding('UTF-8')}"
497
+ FHIR.logger.debug "POST - Request: #{request.to_json}\nResponse:\nResponse Headers: #{scrubbed_response_headers(result.each_key {})} \nResponse Body: #{resp.force_encoding('UTF-8')}"
401
498
  request.args[:path] = url.gsub(@base_service_url, '')
402
499
  res = {
403
500
  code: result.code,
404
501
  headers: scrubbed_response_headers(result.each_key {}),
405
502
  body: resp
406
503
  }
407
- @reply = FHIR::ClientReply.new(request.args, res)
504
+ @reply = FHIR::ClientReply.new(request.args, res, self)
408
505
  end
409
506
  end
410
507
  end
@@ -419,6 +516,12 @@ module FHIR
419
516
  begin
420
517
  response = @client.put(url, headers: headers, body: payload)
421
518
  rescue => e
519
+ unless e.response
520
+ # Re-raise the client error if there's no response. Otherwise, logging
521
+ # and other things break below!
522
+ FHIR.logger.error "PUT - Request: #{url} failed! No response from server: #{e}"
523
+ raise # Re-raise the same error we caught.
524
+ end
422
525
  response = e.response if e.response
423
526
  end
424
527
  req = {
@@ -433,19 +536,19 @@ module FHIR
433
536
  headers: response.headers,
434
537
  body: response.body
435
538
  }
436
- FHIR.logger.info "PUT - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
437
- @reply = FHIR::ClientReply.new(req, res)
539
+ FHIR.logger.debug "PUT - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
540
+ @reply = FHIR::ClientReply.new(req, res, self)
438
541
  else
439
542
  headers.merge!(@security_headers) if @use_basic_auth
440
543
  @client.put(url, payload, headers) do |resp, request, result|
441
- FHIR.logger.info "PUT - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
544
+ FHIR.logger.debug "PUT - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
442
545
  request.args[:path] = url.gsub(@base_service_url, '')
443
546
  res = {
444
547
  code: result.code,
445
548
  headers: scrubbed_response_headers(result.each_key {}),
446
549
  body: resp
447
550
  }
448
- @reply = FHIR::ClientReply.new(request.args, res)
551
+ @reply = FHIR::ClientReply.new(request.args, res, self)
449
552
  end
450
553
  end
451
554
  end
@@ -460,6 +563,12 @@ module FHIR
460
563
  begin
461
564
  response = @client.patch(url, headers: headers, body: payload)
462
565
  rescue => e
566
+ unless e.response
567
+ # Re-raise the client error if there's no response. Otherwise, logging
568
+ # and other things break below!
569
+ FHIR.logger.error "PATCH - Request: #{url} failed! No response from server: #{e}"
570
+ raise # Re-raise the same error we caught.
571
+ end
463
572
  response = e.response if e.response
464
573
  end
465
574
  req = {
@@ -474,22 +583,28 @@ module FHIR
474
583
  headers: response.headers,
475
584
  body: response.body
476
585
  }
477
- FHIR.logger.info "PATCH - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
478
- @reply = FHIR::ClientReply.new(req, res)
586
+ FHIR.logger.debug "PATCH - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
587
+ @reply = FHIR::ClientReply.new(req, res, self)
479
588
  else
480
589
  headers.merge!(@security_headers) if @use_basic_auth
481
590
  begin
482
591
  @client.patch(url, payload, headers) do |resp, request, result|
483
- FHIR.logger.info "PATCH - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
592
+ FHIR.logger.debug "PATCH - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
484
593
  request.args[:path] = url.gsub(@base_service_url, '')
485
594
  res = {
486
595
  code: result.code,
487
596
  headers: scrubbed_response_headers(result.each_key {}),
488
597
  body: resp
489
598
  }
490
- @reply = FHIR::ClientReply.new(request.args, res)
599
+ @reply = FHIR::ClientReply.new(request.args, res, self)
491
600
  end
492
601
  rescue => e
602
+ unless e.response
603
+ # Re-raise the client error if there's no response. Otherwise, logging
604
+ # and other things break below!
605
+ FHIR.logger.error "PATCH - Request: #{url} failed! No response from server: #{e}"
606
+ raise # Re-raise the same error we caught.
607
+ end
493
608
  req = {
494
609
  method: :patch,
495
610
  url: url,
@@ -500,8 +615,9 @@ module FHIR
500
615
  res = {
501
616
  body: e.message
502
617
  }
618
+ FHIR.logger.debug "PATCH - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
503
619
  FHIR.logger.error "PATCH Error: #{e.message}"
504
- @reply = FHIR::ClientReply.new(req, res)
620
+ @reply = FHIR::ClientReply.new(req, res, self)
505
621
  end
506
622
  end
507
623
  end
@@ -515,6 +631,12 @@ module FHIR
515
631
  begin
516
632
  response = @client.delete(url, headers: headers)
517
633
  rescue => e
634
+ unless e.response
635
+ # Re-raise the client error if there's no response. Otherwise, logging
636
+ # and other things break below!
637
+ FHIR.logger.error "DELETE - Request: #{url} failed! No response from server: #{e}"
638
+ raise # Re-raise the same error we caught.
639
+ end
518
640
  response = e.response if e.response
519
641
  end
520
642
  req = {
@@ -529,19 +651,19 @@ module FHIR
529
651
  headers: response.headers,
530
652
  body: response.body
531
653
  }
532
- FHIR.logger.info "DELETE - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
533
- @reply = FHIR::ClientReply.new(req, res)
654
+ FHIR.logger.debug "DELETE - Request: #{req}, Response: #{response.body.force_encoding('UTF-8')}"
655
+ @reply = FHIR::ClientReply.new(req, res, self)
534
656
  else
535
657
  headers.merge!(@security_headers) if @use_basic_auth
536
658
  @client.delete(url, headers) do |resp, request, result|
537
- FHIR.logger.info "DELETE - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
659
+ FHIR.logger.debug "DELETE - Request: #{request.to_json}, Response: #{resp.force_encoding('UTF-8')}"
538
660
  request.args[:path] = url.gsub(@base_service_url, '')
539
661
  res = {
540
662
  code: result.code,
541
663
  headers: scrubbed_response_headers(result.each_key {}),
542
664
  body: resp
543
665
  }
544
- @reply = FHIR::ClientReply.new(request.args, res)
666
+ @reply = FHIR::ClientReply.new(request.args, res, self)
545
667
  end
546
668
  end
547
669
  end
@@ -551,14 +673,14 @@ module FHIR
551
673
  url = URI(build_url(path)).to_s
552
674
  FHIR.logger.info "HEADING: #{url}"
553
675
  RestClient.head(url, headers) do |response, request, result|
554
- FHIR.logger.info "HEAD - Request: #{request.to_json}, Response: #{response.force_encoding('UTF-8')}"
676
+ FHIR.logger.debug "HEAD - Request: #{request.to_json}, Response: #{response.force_encoding('UTF-8')}"
555
677
  request.args[:path] = url.gsub(@base_service_url, '')
556
678
  res = {
557
679
  code: result.code,
558
680
  headers: scrubbed_response_headers(result.each_key {}),
559
681
  body: response
560
682
  }
561
- @reply = FHIR::ClientReply.new(request.args, res)
683
+ @reply = FHIR::ClientReply.new(request.args, res, self)
562
684
  end
563
685
  end
564
686