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
@@ -1,10 +1,8 @@
1
- class ClientException < Exception
2
-
1
+ class ClientException < RuntimeError
3
2
  attr_accessor :reply
4
3
 
5
4
  def initialize(message, reply = nil)
6
5
  super(message)
7
6
  @reply = reply
8
7
  end
9
-
10
8
  end
@@ -1,24 +1,23 @@
1
1
  module FHIR
2
2
  class Bundle
3
-
4
3
  def self_link
5
- link.select {|n| n.relation == 'self'}.first
4
+ link.select { |n| n.relation == 'self' }.first
6
5
  end
7
6
 
8
7
  def first_link
9
- link.select {|n| n.relation == 'first'}.first
8
+ link.select { |n| n.relation == 'first' }.first
10
9
  end
11
10
 
12
11
  def last_link
13
- link.select {|n| n.relation == 'last'}.first
12
+ link.select { |n| n.relation == 'last' }.first
14
13
  end
15
14
 
16
15
  def next_link
17
- link.select {|n| n.relation == 'next'}.first
16
+ link.select { |n| n.relation == 'next' }.first
18
17
  end
19
18
 
20
19
  def previous_link
21
- link.select {|n| n.relation == 'previous' || n.relation == 'prev'}.first
20
+ link.select { |n| n.relation == 'previous' || n.relation == 'prev' }.first
22
21
  end
23
22
 
24
23
  def get_by_id(id)
@@ -41,4 +40,3 @@ module FHIR
41
40
  end
42
41
  end
43
42
  end
44
-
@@ -84,7 +84,7 @@ module FHIR
84
84
  end
85
85
 
86
86
  def conditional_update(params)
87
- handle_response client.conditional_update(self, self.id, params)
87
+ handle_response client.conditional_update(self, id, params)
88
88
  end
89
89
 
90
90
  def destroy
@@ -92,10 +92,18 @@ module FHIR
92
92
  nil
93
93
  end
94
94
 
95
+ def resolve(reference)
96
+ if reference.contained?
97
+ contained.detect { |c| c.id == reference.id }
98
+ else
99
+ reference.read
100
+ end
101
+ end
102
+
95
103
  private
96
104
 
97
105
  def self.handle_response(response)
98
- raise ClientException.new "Server returned #{response.code}.", response if response.code.between?(400,599)
106
+ raise ClientException.new "Server returned #{response.code}.", response if response.code.between?(400, 599)
99
107
  response.resource
100
108
  end
101
109
 
@@ -0,0 +1,28 @@
1
+ module FHIR
2
+ class Reference
3
+ def contained?
4
+ reference.to_s.start_with?('#')
5
+ end
6
+
7
+ def id
8
+ if contained?
9
+ reference.to_s[1..-1]
10
+ else
11
+ reference.to_s.split('/').last
12
+ end
13
+ end
14
+
15
+ def type
16
+ reference.to_s.split('/').first unless contained?
17
+ end
18
+
19
+ def resource_class
20
+ "FHIR::#{type}".constantize unless contained?
21
+ end
22
+
23
+ def read
24
+ return if contained? || type.blank? || id.blank?
25
+ resource_class.read(id, client)
26
+ end
27
+ end
28
+ end
@@ -1,7 +1,6 @@
1
1
  module FHIR
2
2
  class ClientReply
3
-
4
- @@validation_rules = JSON.parse( File.open(File.join(File.expand_path('..',File.dirname(File.absolute_path(__FILE__))),'fhir_api_validation.json'),'r:UTF-8',&:read) )
3
+ @@validation_rules = JSON.parse(File.open(File.join(File.expand_path('..', File.dirname(File.absolute_path(__FILE__))), 'fhir_api_validation.json'), 'r:UTF-8', &:read))
5
4
  @@path_regexes = {
6
5
  '[type]' => "(#{FHIR::RESOURCES.join('|')})",
7
6
  '[id]' => FHIR::PRIMITIVES['id']['regex'],
@@ -20,9 +19,9 @@ module FHIR
20
19
  UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Z])/ix
21
20
 
22
21
  @@header_regexes = {
23
- 'Content-Type' => Regexp.new("(#{FHIR::Formats::ResourceFormat::RESOURCE_XML.gsub('+','\\\+')}|#{FHIR::Formats::ResourceFormat::RESOURCE_JSON.gsub('+','\\\+')})(([ ;]+)(charset)([ =]+)(UTF-8|utf-8))?"),
24
- 'Accept' => Regexp.new("(#{FHIR::Formats::ResourceFormat::RESOURCE_XML.gsub('+','\\\+')}|#{FHIR::Formats::ResourceFormat::RESOURCE_JSON.gsub('+','\\\+')})"),
25
- 'Prefer' => Regexp.new("(return=minimal|return=representation)"),
22
+ 'Content-Type' => Regexp.new("(#{FHIR::Formats::ResourceFormat::RESOURCE_XML.gsub('+', '\\\+')}|#{FHIR::Formats::ResourceFormat::RESOURCE_JSON.gsub('+', '\\\+')})(([ ;]+)(charset)([ =]+)(UTF-8|utf-8))?"),
23
+ 'Accept' => Regexp.new("(#{FHIR::Formats::ResourceFormat::RESOURCE_XML.gsub('+', '\\\+')}|#{FHIR::Formats::ResourceFormat::RESOURCE_JSON.gsub('+', '\\\+')})"),
24
+ 'Prefer' => Regexp.new('(return=minimal|return=representation)'),
26
25
  'ETag' => Regexp.new('(W\/)?"[\dA-Za-z]+"'),
27
26
  'If-Modified-Since' => @@rfs1123,
28
27
  'If-Match' => Regexp.new('(W\/)?"[\dA-Za-z]+"'),
@@ -39,13 +38,13 @@ module FHIR
39
38
  # :headers => {},
40
39
  # :payload => nil # body of request goes here in POST
41
40
  # }
42
- attr_accessor :request
41
+ attr_accessor :request
43
42
  # {
44
43
  # :code => '200',
45
44
  # :headers => {},
46
45
  # :body => '{xml or json here}'
47
46
  # }
48
- attr_accessor :response
47
+ attr_accessor :response
49
48
  attr_accessor :resource # a FHIR resource
50
49
  attr_accessor :resource_class # class of the :resource
51
50
 
@@ -61,7 +60,7 @@ module FHIR
61
60
  def id
62
61
  return nil if @resource_class.nil?
63
62
  (self_link || @request[:url]) =~ %r{(?<=#{@resource_class.name.demodulize}\/)([^\/]+)}
64
- $1
63
+ Regexp.last_match(1)
65
64
  end
66
65
 
67
66
  def version
@@ -71,7 +70,7 @@ module FHIR
71
70
  begin
72
71
  tokens = link.split('_history/')
73
72
  version = tokens.last.split('/').first
74
- rescue Exception => e
73
+ rescue
75
74
  version = nil
76
75
  end
77
76
  end
@@ -100,49 +99,46 @@ module FHIR
100
99
  def validate
101
100
  errors = []
102
101
  @@validation_rules.each do |rule|
103
- if rule['verb']==@request[:method].to_s.upcase
104
- rule_match = false
105
- rule['path'].each do |path|
106
- rule_regex = path.gsub('/','(\/)').gsub('?','\?')
107
- @@path_regexes.each do |token,regex|
108
- rule_regex.gsub!(token,regex)
109
- end
110
- rule_match = true if(Regexp.new(rule_regex) =~ @request[:path])
111
- end
112
- if rule_match
113
- # check the request headers
114
- errors << validate_headers("#{rule['interaction'].upcase} REQUEST",@request[:headers],rule['request']['headers'])
115
- # check the request body
116
- errors << validate_body("#{rule['interaction'].upcase} REQUEST",@request[:payload],rule['request']['body'])
117
- # check the response codes
118
- if !rule['response']['status'].include?(@response[:code].to_i)
119
- errors << "#{rule['interaction'].upcase} RESPONSE: Invalid response code: #{@response[:code]}"
120
- end
121
- if @response[:code].to_i < 400
122
- # check the response headers
123
- errors << validate_headers("#{rule['interaction'].upcase} RESPONSE",@response[:headers],rule['response']['headers'])
124
- # check the response body
125
- errors << validate_body("#{rule['interaction'].upcase} RESPONSE",@response[:body],rule['response']['body'])
126
- end
102
+ next unless rule['verb'] == @request[:method].to_s.upcase
103
+ rule_match = false
104
+ rule['path'].each do |path|
105
+ rule_regex = path.gsub('/', '(\/)').gsub('?', '\?')
106
+ @@path_regexes.each do |token, regex|
107
+ rule_regex.gsub!(token, regex)
127
108
  end
109
+ rule_match = true if Regexp.new(rule_regex) =~ @request[:path]
110
+ end
111
+ next unless rule_match
112
+ # check the request headers
113
+ errors << validate_headers("#{rule['interaction'].upcase} REQUEST", @request[:headers], rule['request']['headers'])
114
+ # check the request body
115
+ errors << validate_body("#{rule['interaction'].upcase} REQUEST", @request[:payload], rule['request']['body'])
116
+ # check the response codes
117
+ unless rule['response']['status'].include?(@response[:code].to_i)
118
+ errors << "#{rule['interaction'].upcase} RESPONSE: Invalid response code: #{@response[:code]}"
128
119
  end
120
+ next unless @response[:code].to_i < 400
121
+ # check the response headers
122
+ errors << validate_headers("#{rule['interaction'].upcase} RESPONSE", @response[:headers], rule['response']['headers'])
123
+ # check the response body
124
+ errors << validate_body("#{rule['interaction'].upcase} RESPONSE", @response[:body], rule['response']['body'])
129
125
  end
130
126
  errors.flatten
131
127
  end
132
128
 
133
- def validate_headers(name,headers,header_rules)
129
+ def validate_headers(name, headers, header_rules)
134
130
  errors = []
135
- header_rules.each do |header,present|
131
+ header_rules.each do |header, present|
136
132
  value = headers[header]
137
- if present==true
133
+ if present == true
138
134
  if value
139
- errors << "#{name}: Malformed value for header #{header}: #{value}" if !(@@header_regexes[header] =~ value)
135
+ errors << "#{name}: Malformed value for header #{header}: #{value}" unless @@header_regexes[header] =~ value
140
136
  else
141
137
  errors << "#{name}: Missing header: #{header}"
142
138
  end
143
- elsif (present=='optional' && value)
144
- errors << "#{name}: Malformed value for optional header #{header}: #{value}" if !(@@header_regexes[header] =~ value)
145
- # binding.pry if !(@@header_regexes[header] =~ value)
139
+ elsif present == 'optional' && value
140
+ errors << "#{name}: Malformed value for optional header #{header}: #{value}" unless @@header_regexes[header] =~ value
141
+ # binding.pry if !(@@header_regexes[header] =~ value)
146
142
  elsif !value.nil?
147
143
  errors << "#{name}: Should not have header: #{header}"
148
144
  end
@@ -150,24 +146,25 @@ module FHIR
150
146
  errors
151
147
  end
152
148
 
153
- def validate_body(name,body,body_rules)
149
+ def validate_body(name, body, body_rules)
154
150
  errors = []
155
151
  if body && body_rules
156
152
  if body_rules['types']
157
153
  body_type_match = false
158
- body_rules['types'].each do |type|
159
- begin
160
- content = FHIR.from_contents(body)
161
- body_type_match = true if content.resourceType==type
162
- body_type_match = true if type=='Resource' && FHIR::RESOURCES.include?(content.resourceType)
163
- rescue
154
+ begin
155
+ content = FHIR.from_contents(body)
156
+ body_rules['types'].each do |type|
157
+ body_type_match = true if content.resourceType == type
158
+ body_type_match = true if type == 'Resource' && FHIR::RESOURCES.include?(content.resourceType)
164
159
  end
160
+ rescue
161
+ FHIR.logger.warn "ClientReply was unable to validate response body: #{body}"
165
162
  end
166
- errors << "#{name}: Body does not match allowed types: #{body_rules['types'].join(', ')}" if !body_type_match
163
+ errors << "#{name}: Body does not match allowed types: #{body_rules['types'].join(', ')}" unless body_type_match
167
164
  end
168
165
  if body_rules['regex']
169
166
  regex = Regexp.new(body_rules['regex'])
170
- errors << "#{name}: Body does not match regular expression: #{body_rules['regex']}" if !(regex =~ body)
167
+ errors << "#{name}: Body does not match regular expression: #{body_rules['regex']}" unless regex =~ body
171
168
  end
172
169
  elsif body && !body_rules
173
170
  errors "#{name}: Body not allowed"
@@ -176,6 +173,5 @@ module FHIR
176
173
  end
177
174
 
178
175
  private :validate_headers, :validate_body
179
-
180
176
  end
181
- end
177
+ end
@@ -2,12 +2,12 @@ module FHIR
2
2
  class Tag
3
3
  # Each Tag is part of an HTTP header named "Category" with three parts: term, scheme, and label.
4
4
  # Each Tag can be in an individual "Category" header, or they can all be concatentated (with comma
5
- # separation) inside a single "Category" header.
5
+ # separation) inside a single "Category" header.
6
6
 
7
7
  # Term is a URI:
8
- # General tags:
8
+ # General tags:
9
9
  # Bundle / FHIR Documents: "http://hl7.org/fhir/tag/document"
10
- # Bundle / FHIR Messages: "http://hl7.org/fhir/tag/message"
10
+ # Bundle / FHIR Messages: "http://hl7.org/fhir/tag/message"
11
11
  # Profile tags: URL that references a profile resource.
12
12
  attr_accessor :term
13
13
 
@@ -22,7 +22,7 @@ module FHIR
22
22
 
23
23
  def to_header
24
24
  s = "#{term}; scheme=#{scheme}"
25
- s += "; label=#{label}" if !label.nil?
25
+ s += "; label=#{label}" unless label.nil?
26
26
  s
27
27
  end
28
28
 
@@ -35,10 +35,10 @@ module FHIR
35
35
  tokens.each do |token|
36
36
  if !token.strip.index('scheme').nil?
37
37
  token.strip =~ %r{(?<=scheme)(\s*)=(\s*)([\".:_\-\/\w]+)}
38
- h.scheme = $3
38
+ h.scheme = Regexp.last_match(3)
39
39
  elsif !token.strip.index('label').nil?
40
40
  token.strip =~ %r{(?<=label)(\s*)=(\s*)([\".:_\-\/\w\s]+)}
41
- h.label = $3
41
+ h.label = Regexp.last_match(3)
42
42
  end
43
43
  end
44
44
  h
@@ -52,6 +52,5 @@ module FHIR
52
52
  tokens.each { |token| tags << FHIR::Tag.parse_tag(token) }
53
53
  tags
54
54
  end
55
-
56
55
  end
57
56
  end
@@ -0,0 +1,8 @@
1
+ module FHIR
2
+ module Formats
3
+ class PatchFormat
4
+ PATCH_XML = 'application/xml-patch+xml'.freeze
5
+ PATCH_JSON = 'application/json-patch+json'.freeze
6
+ end
7
+ end
8
+ end
@@ -1,139 +1,135 @@
1
- module FHIR
2
- class ResourceAddress
3
-
4
- DEFAULTS = {
5
- id: nil,
6
- resource: nil,
7
- format: 'application/fhir+xml'
8
- }
9
-
10
- DEFAULT_CHARSET = 'UTF-8'
11
-
12
- def fhir_headers(options, use_format_param=false)
13
- options = DEFAULTS.merge(options)
14
-
15
- format = options[:format] || FHIR::Formats::ResourceFormat::RESOURCE_XML
16
- fhir_headers = {
17
- 'User-Agent' => 'Ruby FHIR Client',
18
- 'Content-Type' => format + ';charset=' + DEFAULT_CHARSET,
19
- 'Accept-Charset' => DEFAULT_CHARSET
20
- }
21
- # remove the content-type header if the format is 'xml' or 'json' because
22
- # even those are valid _format parameter options, they are not valid MimeTypes.
23
- fhir_headers.delete('Content-Type') if ['xml','json'].include?(format.downcase)
24
-
25
- if(options[:category])
26
- # options[:category] should be an Array of FHIR::Tag objects
27
- tags = {
28
- 'Category' => options[:category].collect { |h| h.to_header }.join(',')
29
- }
30
- fhir_headers.merge!(tags)
31
- options.delete(:category)
32
- end
33
-
34
- if use_format_param
35
- fhir_headers.delete('Accept')
36
- options.delete('Accept')
37
- options.delete(:accept)
38
- else
39
- fhir_headers['Accept'] = format
40
- end
41
-
42
- fhir_headers.merge!(options) unless options.empty?
43
- fhir_headers[:operation] = options[:operation][:name] if options[:operation] && options[:operation][:name]
44
- fhir_headers
45
- end
46
-
47
- def resource_url(options, use_format_param=false)
48
- options = DEFAULTS.merge(options)
49
-
50
- params = {}
51
- url = ""
52
- # handle requests for resources by class or string; useful for testing nonexistent resource types
53
- url += "/#{ options[:resource].try(:name).try(:demodulize) || options[:resource].split("::").last }" if options[:resource]
54
- url += "/#{options[:id]}" if options[:id]
55
- url += "/$validate" if options[:validate]
56
-
57
- if(options[:operation])
58
- opr = options[:operation]
59
- p = opr[:parameters]
60
- p = p.each{|k,v|p[k]=v[:value]} if p
61
- params.merge!(p) if p && opr[:method]=='GET'
62
-
63
- if (opr[:name] == :fetch_patient_record)
64
- url += "/$everything"
65
- elsif (opr[:name] == :value_set_expansion)
66
- url += "/$expand"
67
- elsif (opr && opr[:name]== :value_set_based_validation)
68
- url += "/$validate-code"
69
- elsif (opr && opr[:name]== :code_system_lookup)
70
- url += "/$lookup"
71
- elsif (opr && opr[:name]== :concept_map_translate)
72
- url += "/$translate"
73
- elsif (opr && opr[:name]== :closure_table_maintenance)
74
- url += "/$closure"
75
- end
76
- end
77
-
78
- if (options[:history])
79
- history = options[:history]
80
- url += "/_history/#{history[:id]}"
81
- params[:_count] = history[:count] if history[:count]
82
- params[:_since] = history[:since].iso8601 if history[:since]
83
- end
84
-
85
- if(options[:search])
86
- search_options = options[:search]
87
- url += '/_search' if search_options[:flag]
88
- url += "/#{search_options[:compartment]}" if search_options[:compartment]
89
-
90
- if search_options[:parameters]
91
- search_options[:parameters].each do |key,value|
92
- params[key.to_sym] = value
93
- end
94
- end
95
- end
96
-
97
- # options[:params] is simply appended at the end of a url and is used by testscripts
98
- url += options[:params] if options[:params]
99
-
100
- if(options[:summary])
101
- params[:_summary] = options[:summary]
102
- end
103
-
104
- if use_format_param && options[:format]
105
- params[:_format] = options[:format]
106
- end
107
-
108
- uri = Addressable::URI.parse(url)
109
- # params passed in options takes precidence over params calculated in this method
110
- # for use by testscript primarily
111
- uri.query_values = params unless options[:params] && options[:params].include?("?")
112
- uri.normalize.to_str
113
- end
114
-
115
- # Get the resource ID out of the URL (e.g. Bundle.entry.response.location)
116
- def self.pull_out_id(resourceType,url)
117
- id = nil
118
- if !resourceType.nil? && !url.nil?
119
- token = "#{resourceType}/"
120
- if url.index(token)
121
- start = url.index(token) + token.length
122
- t = url[start..-1]
123
- stop = (t.index("/") || 0)-1
124
- stop = -1 if stop.nil?
125
- id = t[0..stop]
126
- else
127
- id = nil
128
- end
129
- end
130
- id
131
- end
132
-
133
- def self.append_forward_slash_to_path(path)
134
- path += '/' unless path.last == '/'
135
- path
136
- end
137
-
138
- end
139
- end
1
+ module FHIR
2
+ class ResourceAddress
3
+ DEFAULTS = {
4
+ id: nil,
5
+ resource: nil,
6
+ format: 'application/fhir+xml'
7
+ }.freeze
8
+
9
+ DEFAULT_CHARSET = 'UTF-8'.freeze
10
+
11
+ def fhir_headers(options, use_format_param = false)
12
+ options = DEFAULTS.merge(options)
13
+
14
+ format = options[:format] || FHIR::Formats::ResourceFormat::RESOURCE_XML
15
+ fhir_headers = {
16
+ 'User-Agent' => 'Ruby FHIR Client',
17
+ 'Content-Type' => format + ';charset=' + DEFAULT_CHARSET,
18
+ 'Accept-Charset' => DEFAULT_CHARSET
19
+ }
20
+ # remove the content-type header if the format is 'xml' or 'json' because
21
+ # even those are valid _format parameter options, they are not valid MimeTypes.
22
+ fhir_headers.delete('Content-Type') if %w(xml json).include?(format.downcase)
23
+
24
+ if options[:category]
25
+ # options[:category] should be an Array of FHIR::Tag objects
26
+ tags = {
27
+ 'Category' => options[:category].collect(&:to_header).join(',')
28
+ }
29
+ fhir_headers.merge!(tags)
30
+ options.delete(:category)
31
+ end
32
+
33
+ if use_format_param
34
+ fhir_headers.delete('Accept')
35
+ options.delete('Accept')
36
+ options.delete(:accept)
37
+ else
38
+ fhir_headers['Accept'] = format
39
+ end
40
+
41
+ fhir_headers.merge!(options) unless options.empty?
42
+ fhir_headers[:operation] = options[:operation][:name] if options[:operation] && options[:operation][:name]
43
+ fhir_headers
44
+ end
45
+
46
+ def resource_url(options, use_format_param = false)
47
+ options = DEFAULTS.merge(options)
48
+
49
+ params = {}
50
+ url = ''
51
+ # handle requests for resources by class or string; useful for testing nonexistent resource types
52
+ url += "/#{options[:resource].try(:name).try(:demodulize) || options[:resource].split('::').last}" if options[:resource]
53
+ url += "/#{options[:id]}" if options[:id]
54
+ url += '/$validate' if options[:validate]
55
+
56
+ if options[:operation]
57
+ opr = options[:operation]
58
+ p = opr[:parameters]
59
+ p = p.each { |k, v| p[k] = v[:value] } if p
60
+ params.merge!(p) if p && opr[:method] == 'GET'
61
+
62
+ if opr[:name] == :fetch_patient_record
63
+ url += '/$everything'
64
+ elsif opr[:name] == :value_set_expansion
65
+ url += '/$expand'
66
+ elsif opr && opr[:name] == :value_set_based_validation
67
+ url += '/$validate-code'
68
+ elsif opr && opr[:name] == :code_system_lookup
69
+ url += '/$lookup'
70
+ elsif opr && opr[:name] == :concept_map_translate
71
+ url += '/$translate'
72
+ elsif opr && opr[:name] == :closure_table_maintenance
73
+ url += '/$closure'
74
+ end
75
+ end
76
+
77
+ if options[:history]
78
+ history = options[:history]
79
+ url += "/_history/#{history[:id]}"
80
+ params[:_count] = history[:count] if history[:count]
81
+ params[:_since] = history[:since].iso8601 if history[:since]
82
+ end
83
+
84
+ if options[:search]
85
+ search_options = options[:search]
86
+ url += '/_search' if search_options[:flag]
87
+ url += "/#{search_options[:compartment]}" if search_options[:compartment]
88
+
89
+ if search_options[:parameters]
90
+ search_options[:parameters].each do |key, value|
91
+ params[key.to_sym] = value
92
+ end
93
+ end
94
+ end
95
+
96
+ # options[:params] is simply appended at the end of a url and is used by testscripts
97
+ url += options[:params] if options[:params]
98
+
99
+ params[:_summary] = options[:summary] if options[:summary]
100
+
101
+ if use_format_param && options[:format]
102
+ params[:_format] = options[:format]
103
+ end
104
+
105
+ uri = Addressable::URI.parse(url)
106
+ # params passed in options takes precidence over params calculated in this method
107
+ # for use by testscript primarily
108
+ uri.query_values = params unless options[:params] && options[:params].include?('?')
109
+ uri.normalize.to_str
110
+ end
111
+
112
+ # Get the resource ID out of the URL (e.g. Bundle.entry.response.location)
113
+ def self.pull_out_id(resource_type, url)
114
+ id = nil
115
+ if !resource_type.nil? && !url.nil?
116
+ token = "#{resource_type}/"
117
+ if url.index(token)
118
+ start = url.index(token) + token.length
119
+ t = url[start..-1]
120
+ stop = (t.index('/') || 0) - 1
121
+ stop = -1 if stop.nil?
122
+ id = t[0..stop]
123
+ else
124
+ id = nil
125
+ end
126
+ end
127
+ id
128
+ end
129
+
130
+ def self.append_forward_slash_to_path(path)
131
+ path += '/' unless path.last == '/'
132
+ path
133
+ end
134
+ end
135
+ end