Module: Inferno::DSL::FHIRClient

Defined in:
lib/inferno/dsl/fhir_client.rb

Overview

This module contains the FHIR DSL available to test writers.

Examples:

class MyTestGroup < Inferno::TestGroup
  # create a "default" client for a group
  fhir_client do
    url 'https://example.com/fhir'
  end

  # create a named client for a group
  fhir_client :with_custom_header do
    url 'https://example.com/fhir'
    headers 'X-my-custom-header': 'ABC123'
  end

  test :some_test do
    run do
      # uses the default client
      fhir_read('Patient', 5)

      # uses a named client
      fhir_read('Patient', 5, client: :with_custom_header)

      request  # the most recent request
      response # the most recent response
      resource # the resource from the most recent response
      requests # all of the requests which have been made in this test
    end
  end
end

See Also:

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(klass) ⇒ Object



41
42
43
44
45
46
# File 'lib/inferno/dsl/fhir_client.rb', line 41

def self.included(klass)
  klass.extend ClassMethods
  klass.extend Forwardable
  klass.include RequestStorage
  klass.include TCPExceptionHandler
end

Instance Method Details

#body_to_path(body) ⇒ Object

Converts a list of FHIR Parameters into a query string for GET requests

Parameters:

  • body (FHIR::Parameters)

    Must all be primitive if making GET request



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/inferno/dsl/fhir_client.rb', line 76

def body_to_path(body)
  query_hashes = body.parameter.map do |param|
    if primitive_parameter?(param)
      { param.name => param.to_hash.except('name').values[0] }
    else
      Inferno::Application[:logger].error "Cannot use GET request with non-primitive datatype #{param.name}"
      raise ArgumentError, "Cannot use GET request with non-primitive datatype #{param.name}"
    end
  end
  query_hashes.map(&:to_query).join('&')
end

#fhir_class_from_resource_type(resource_type) ⇒ Object

TODO:

Make this a FHIR class method? Something like FHIR.class_for(resource_type)



333
334
335
# File 'lib/inferno/dsl/fhir_client.rb', line 333

def fhir_class_from_resource_type(resource_type)
  FHIR.const_get(resource_type.to_s.camelize)
end

#fhir_client(client = :default) ⇒ FHIR::Client

Return a previously defined FHIR client

Parameters:

  • client (Symbol) (defaults to: :default)

    the name of the client

Returns:

See Also:



53
54
55
56
# File 'lib/inferno/dsl/fhir_client.rb', line 53

def fhir_client(client = :default)
  fhir_clients[client] ||=
    FHIRClientBuilder.new.build(self, self.class.fhir_client_definitions[client])
end

#fhir_clientsObject



59
60
61
# File 'lib/inferno/dsl/fhir_client.rb', line 59

def fhir_clients
  @fhir_clients ||= {}
end

#fhir_create(resource, client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR create interaction.

Parameters:

  • resource (FHIR::Model)
  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



158
159
160
161
162
163
164
# File 'lib/inferno/dsl/fhir_client.rb', line 158

def fhir_create(resource, client: :default, name: nil, tags: [])
  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      fhir_client(client).create(resource)
    end
  end
end

#fhir_delete(resource_type, id, client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR delete interaction.

Parameters:

  • resource_type (String, Symbol, Class)
  • id (String)
  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



305
306
307
308
309
310
311
# File 'lib/inferno/dsl/fhir_client.rb', line 305

def fhir_delete(resource_type, id, client: :default, name: nil, tags: [])
  store_request('outgoing', name:, tags:) do
    tcp_exception_handler do
      fhir_client(client).destroy(fhir_class_from_resource_type(resource_type), id)
    end
  end
end

#fhir_get_capability_statement(client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Fetch the capability statement.

Parameters:

  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



141
142
143
144
145
146
147
148
# File 'lib/inferno/dsl/fhir_client.rb', line 141

def fhir_get_capability_statement(client: :default, name: nil, tags: [])
  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      fhir_client(client).conformance_statement
      fhir_client(client).reply
    end
  end
end

#fhir_history(resource_type = nil, id = nil, client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR history interaction.

Parameters:

  • resource_type (String, Symbol, Class) (defaults to: nil)
  • id (String) (defaults to: nil)
  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/inferno/dsl/fhir_client.rb', line 245

def fhir_history(resource_type = nil, id = nil, client: :default, name: nil, tags: [])
  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      if id
        fhir_client(client).resource_instance_history(fhir_class_from_resource_type(resource_type), id)
      elsif resource_type
        fhir_client(client).resource_history(fhir_class_from_resource_type(resource_type))
      else
        fhir_client(client).all_history
      end
    end
  end
end

#fhir_operation(path, body: nil, client: :default, name: nil, headers: {}, operation_method: :post, tags: []) ⇒ Inferno::Entities::Request

Note:

This is a placeholder method until the FHIR::Client supports general operations. Note that while both POST and GET methods are allowed, GET is only allowed when the operation does not affect the server’s state. See build.fhir.org/operationdefinition-definitions.html#OperationDefinition.affectsState

Note:

Currently does not allow for repeated parameters if using GET

Perform a FHIR operation

Parameters:

  • path (String)
  • body (FHIR::Parameters) (defaults to: nil)

    Must all be primitive if making GET request

  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • headers (Hash) (defaults to: {})

    custom headers for this operation

  • operation_method (Symbol) (defaults to: :post)

    indicates which request type to use for the operation

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/inferno/dsl/fhir_client.rb', line 106

def fhir_operation(
  path,
  body: nil,
  client: :default,
  name: nil,
  headers: {},
  operation_method: :post,
  tags: []
)
  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      operation_headers = fhir_client(client).fhir_headers
      operation_headers.merge!('Content-Type' => 'application/fhir+json') if body.present?
      operation_headers.merge!(headers) if headers.present?
      case operation_method
      when :post
        fhir_client(client).send(:post, path, body, operation_headers)
      when :get
        path = "#{path}?#{body_to_path(body)}" if body.present?
        fhir_client(client).send(:get, path, operation_headers)
      else
        Inferno::Application[:logger].error "Cannot perform #{operation_method} requests, use GET or POST"
        raise ArgumentError, "Cannot perform #{operation_method} requests, use GET or POST"
      end
    end
  end
end

#fhir_patch(resource_type, id, patchset, client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR patch interaction.

Parameters:

  • resource_type (String, Symbol, Class)
  • id (String)
  • patchset (Array)
  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



228
229
230
231
232
233
234
# File 'lib/inferno/dsl/fhir_client.rb', line 228

def fhir_patch(resource_type, id, patchset, client: :default, name: nil, tags: [])
  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      fhir_client(client).partial_update(fhir_class_from_resource_type(resource_type), id, patchset)
    end
  end
end

#fhir_read(resource_type, id, client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR read interaction.

Parameters:

  • resource_type (String, Symbol, Class)
  • id (String)
  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



175
176
177
178
179
180
181
# File 'lib/inferno/dsl/fhir_client.rb', line 175

def fhir_read(resource_type, id, client: :default, name: nil, tags: [])
  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      fhir_client(client).read(fhir_class_from_resource_type(resource_type), id)
    end
  end
end

#fhir_search(resource_type = nil, client: :default, params: {}, name: nil, search_method: :get, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR search interaction.

Parameters:

  • resource_type (String, Symbol, Class) (defaults to: nil)
  • client (Symbol) (defaults to: :default)
  • params (Hash) (defaults to: {})

    the search params

  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • search_method (Symbol) (defaults to: :get)

    Use ‘:post` to search via POST

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/inferno/dsl/fhir_client.rb', line 269

def fhir_search(
  resource_type = nil,
  client: :default,
  params: {},
  name: nil,
  search_method: :get,
  tags: []
)
  search =
    if search_method == :post
      { body: params }
    else
      { parameters: params }
    end

  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      if resource_type
        fhir_client(client)
          .search(fhir_class_from_resource_type(resource_type), { search: })
      else
        fhir_client(client).search_all({ search: })
      end
    end
  end
end

#fhir_transaction(bundle = nil, client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR batch/transaction interaction.

Parameters:

  • bundle (FHIR::Bundle) (defaults to: nil)

    the FHIR batch/transaction Bundle

  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



321
322
323
324
325
326
327
328
# File 'lib/inferno/dsl/fhir_client.rb', line 321

def fhir_transaction(bundle = nil, client: :default, name: nil, tags: [])
  store_request('outgoing', name:, tags:) do
    tcp_exception_handler do
      fhir_client(client).transaction_bundle = bundle if bundle.present?
      fhir_client(client).end_transaction
    end
  end
end

#fhir_update(resource, id, client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR update interaction.

Parameters:

  • resource (FHIR::Model)
  • id (String)
  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



210
211
212
213
214
215
216
# File 'lib/inferno/dsl/fhir_client.rb', line 210

def fhir_update(resource, id, client: :default, name: nil, tags: [])
  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      fhir_client(client).update(resource, id)
    end
  end
end

#fhir_vread(resource_type, id, version_id, client: :default, name: nil, tags: []) ⇒ Inferno::Entities::Request

Perform a FHIR vread interaction.

Parameters:

  • resource_type (String, Symbol, Class)
  • id (String)
  • version_id (String)
  • client (Symbol) (defaults to: :default)
  • name (Symbol) (defaults to: nil)

    Name for this request to allow it to be used by other tests

  • tags (Array<String>) (defaults to: [])

    a list of tags to assign to the request

Returns:



193
194
195
196
197
198
199
# File 'lib/inferno/dsl/fhir_client.rb', line 193

def fhir_vread(resource_type, id, version_id, client: :default, name: nil, tags: [])
  store_request_and_refresh_token(fhir_client(client), name, tags) do
    tcp_exception_handler do
      fhir_client(client).vread(fhir_class_from_resource_type(resource_type), id, version_id)
    end
  end
end

#perform_refresh(client) ⇒ Object



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/inferno/dsl/fhir_client.rb', line 349

def perform_refresh(client)
  credentials = client.auth_info || client.oauth_credentials

  post(
    credentials.token_url,
    body: credentials.oauth2_refresh_params,
    headers: credentials.oauth2_refresh_headers
  )

  return if request.status != 200

  credentials.update_from_response_body(request)

  if credentials.name.present?
    Inferno::Repositories::SessionData.new.save(
      name: credentials.name,
      value: credentials,
      type: credentials.is_a?(Inferno::DSL::AuthInfo) ? 'auth_info' : 'oauth_credentials',
      test_session_id:
    )
  end
rescue StandardError => e
  Inferno::Application[:logger].error "Unable to refresh token: #{e.message}"
end

#primitive_parameter?(param) ⇒ Boolean

Wrapper for checking if parameter contents are primitive

Parameters:

  • param (FHIR::Parameters::Parameter)

    Parameter to be checked

Returns:

  • (Boolean)


67
68
69
70
# File 'lib/inferno/dsl/fhir_client.rb', line 67

def primitive_parameter?(param)
  param_val = param.to_hash.except('name')
  param_val.any? { |datatype, param_value| FHIR.primitive?(datatype: datatype[5..], value: param_value) }
end

#store_request_and_refresh_token(client, name, tags, &block) ⇒ Object

This method wraps a request to automatically refresh its access token if expired. It’s combined with ‘store_request` so that all of the fhir request methods don’t have to be wrapped twice.



341
342
343
344
345
346
# File 'lib/inferno/dsl/fhir_client.rb', line 341

def store_request_and_refresh_token(client, name, tags, &block)
  store_request('outgoing', name:, tags:) do
    perform_refresh(client) if client.need_to_refresh? && client.able_to_refresh?
    block.call
  end
end