Module: CarinForBlueButtonTestKit::MockServer

Includes:
URLs
Included in:
C4BBV200ClientSuite, NextPageEndpoint, ResourceAPIEndpoint, ResourceIDEndpoint
Defined in:
lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb

Overview

Serve responses to CARIN requests

Note that there are numerous expected validation issues that can safely be ignored. See here for full list: hl7.org/fhir/us/carin-bb/STU2/qa.html#suppressed

Constant Summary collapse

SUPPORTED_SCOPES =
['launch', 'patient/*.rs', 'user/*.rs', 'offline_access'].freeze

Instance Method Summary collapse

Methods included from URLs

#authorization_url, #base_url, #client_fhir_base_url, #fhir_base_url, #jwks_url, #metadata_url, #patient_url, #resource_api_url, #resource_id_url, #resume_claims_data_url, #resume_fail_url, #resume_pass_url, #smart_configuration_url, #token_url

Instance Method Details

#carin_resource_id_response(request) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 73

def carin_resource_id_response(request)
  endpoint = resource_id_endpoint(request.url)
  if endpoint
    server_response = server_proxy.get(endpoint)
    response.status = server_response.status
    response_resource = FHIR.from_contents(server_response.body)
    response.headers.merge!(server_response.headers)
    remove_transfer_encoding_and_content_length_header(response.headers)
  else
    response_resource = error_response_resource(request)
  end
  response.body = response_resource.to_json
end

#carin_resource_response(request, test = nil, test_result = nil) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 49

def carin_resource_response(request, test = nil, test_result = nil)
  endpoint = resource_endpoint(request.url)
  if endpoint
    request_parameters = get_params(request.query_string)
    params = match_request_to_expectation(endpoint, request_parameters)
    if params.present?
      server_response = server_proxy.get(endpoint, params)
      response.status = server_response.status
      response_resource = if response.status == 200
                            replace_bundle_urls(FHIR.from_contents(server_response.body))
                          else
                            FHIR.from_contents(server_response.body)
                          end
      response.headers.merge!(server_response.headers)
      remove_transfer_encoding_and_content_length_header(response.headers)
    else
      response_resource = error_response_resource(request)
    end
  else
    response_resource = error_response_resource(request)
  end
  response.body = response_resource.to_json
end

#error_response_resource(request) ⇒ Object



29
30
31
32
33
34
35
36
37
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 29

def error_response_resource(request)
  server_response = server_proxy.get('Patient', { _id: 888 })
  response_resource = FHIR.from_contents(server_response.body)
  response_resource.entry = [{ fullUrl: 'urn:uuid:2866af9c-137d-4458-a8a9-eeeec0ce5583',
                               resource: mock_operation_outcome_resource, search: { mode: 'outcome' } }]
  response_resource.link.first.url = request.url # specific case for Operation Outcome handling
  response.status = 403
  response_resource
end

#extract_bearer_token(request) ⇒ Object

Header expected to be a bearer token of the form “Bearer: <token>”



139
140
141
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 139

def extract_bearer_token(request)
  request.headers['authorization']&.delete_prefix('Bearer ')
end

#extract_client_id(request) ⇒ Object



134
135
136
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 134

def extract_client_id(request)
  URI.decode_www_form(request.body.read).to_h['client_id']
end

#extract_test_run_identifier_from_query_params(request) ⇒ Object



147
148
149
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 147

def extract_test_run_identifier_from_query_params(request)
  request.query_parameters['test_run_identifier']
end

#extract_token_from_query_params(request) ⇒ Object



143
144
145
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 143

def extract_token_from_query_params(request)
  request.query_parameters['token']
end

#find_matching_entry(ref, entries, root_url = '') ⇒ Object



195
196
197
198
199
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 195

def find_matching_entry(ref, entries, root_url = '')
  ref = "#{root_url}/#{ref}" if relative_reference?(ref) && root_url&.present?

  entries&.find { |entry| entry&.fullUrl == ref }
end

#get_metadataObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 94

def 
  erb_template = ERB.new(
    File.read(
      File.join(__dir__,
                'metadata/mock_capability_statement.json.erb')
    )
  )
  capability_statement = JSON.parse(erb_template.result).to_json

  proc {
    [200, { 'Content-Type' => 'application/fhir+json;charset=utf-8' },
     [capability_statement]]
  }
end

#get_params(query_string) ⇒ Object



39
40
41
42
43
44
45
46
47
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 39

def get_params(query_string)
  params = {}
  query_string.split('&').each do |param|
    split_param = param.split('=')
    params[split_param.first] = [] if params[split_param.first].nil?
    params[split_param.first].append(split_param.last)
  end
  params
end

#match_request_to_expectation(resource_type, params) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 121

def match_request_to_expectation(resource_type, params)
  matched_search = supported_searches[resource_type.to_sym].select do |expectation|
    (params.keys.map(&:to_s) & expectation).sort == expectation
  end.map(&:first)

  if matched_search.present?
    return params.select do |key, value|
      matched_search.include?(key.to_s) || key == '_revInclude' || key == '_include'
    end
  end
  nil
end

#mock_operation_outcome_resourceObject



168
169
170
171
172
173
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 168

def mock_operation_outcome_resource
  FHIR.from_contents(File.read(
                       File.join(__dir__,
                                 'metadata/mock_operation_outcome_resource.json')
                     ))
end


186
187
188
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 186

def new_link
  "#{Inferno::Application['base_url']}/custom/#{suite_id}/fhir"
end

#read_next_page(request, test = nil, test_result = nil) ⇒ Object



87
88
89
90
91
92
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 87

def read_next_page(request, test = nil, test_result = nil)
  server_response = server_proxy.get('', JSON.parse(request.params.to_json))
  response.status = server_response.status
  response.response_headers = remove_transfer_encoding_header(server_response.headers)
  response.response_body = replace_bundle_urls(FHIR.from_contents(server_response.body)).to_json
end

#relative_reference?(ref) ⇒ Boolean

Returns:

  • (Boolean)


202
203
204
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 202

def relative_reference?(ref)
  ref&.count('/') == 1
end

#remove_transfer_encoding_and_content_length_header(headers) ⇒ Object



109
110
111
112
113
114
115
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 109

def remove_transfer_encoding_and_content_length_header(headers)
  headers.delete('transfer-encoding') if headers['transfer-encoding'].present?

  return unless headers['Content-Length'].present?

  headers.delete('Content-Length')
end

#replace_bundle_urls(bundle) ⇒ Object



175
176
177
178
179
180
181
182
183
184
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 175

def replace_bundle_urls(bundle)
  reference_server_base = ENV.fetch('FHIR_REFERENCE_SERVER')
  bundle&.link&.map! { |link| { relation: link.relation, url: link.url.gsub(reference_server_base, new_link) } }
  bundle&.entry&.map! do |bundled_resource|
    { fullUrl: bundled_resource.fullUrl.gsub(reference_server_base, new_link),
      resource: bundled_resource.resource,
      search: bundled_resource.search }
  end
  bundle
end

#resource_endpoint(url) ⇒ Object

Pull resource type from url e.g. example.org/fhir/Patient?_id=123 -> Patient



154
155
156
157
158
159
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 154

def resource_endpoint(url)
  return unless url.start_with?('http://', 'https://')

  match = %r{custom/#{suite_id}/fhir/(.*)\?}.match(url)
  match[1] if match.present?
end

#resource_id_endpoint(url) ⇒ Object



161
162
163
164
165
166
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 161

def resource_id_endpoint(url)
  return unless url.start_with?('http://', 'https://')

  match = %r{custom/#{suite_id}/fhir/(.*)}.match(url)
  match[1] if match.present?
end

#server_proxyObject



17
18
19
20
21
22
23
24
25
26
27
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 17

def server_proxy
  @server_proxy ||= Faraday.new(
    url: ENV.fetch('FHIR_REFERENCE_SERVER'),
    params: {},
    headers: { 'Content-Type' => 'application/json', 'Authorization' => 'Bearer SAMPLE_TOKEN',
               'Host' => ENV.fetch('HOST_HEADER') }
  ) do |proxy|
    proxy.use FaradayMiddleware::Gzip
    proxy.options.params_encoder = Faraday::FlatParamsEncoder
  end
end

#suite_idObject



190
191
192
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 190

def suite_id
  C4BBV200ClientSuite.id
end

#supported_searchesObject



117
118
119
# File 'lib/carin_for_blue_button_test_kit/client/v2.0.0/mock_server.rb', line 117

def supported_searches
  @supported_searches ||= SEARCHES_BY_PRIORITY
end