fhir_client 3.0.2 → 3.0.3

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