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
@@ -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