inferno_core 0.0.8 → 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,119 @@
1
+ require_relative '../entities/attributes'
2
+
3
+ module Inferno
4
+ module DSL
5
+ class OAuthCredentials
6
+ ATTRIBUTES = [
7
+ :access_token,
8
+ :refresh_token,
9
+ :token_url,
10
+ :client_id,
11
+ :client_secret,
12
+ :token_retrieval_time,
13
+ :expires_in,
14
+ :name
15
+ ].freeze
16
+
17
+ include Entities::Attributes
18
+
19
+ attr_accessor :client
20
+
21
+ def initialize(raw_attributes_hash)
22
+ attributes_hash = raw_attributes_hash.symbolize_keys
23
+
24
+ invalid_keys = attributes_hash.keys - ATTRIBUTES
25
+
26
+ raise Exceptions::UnknownAttributeException.new(invalid_keys, self.class) if invalid_keys.present?
27
+
28
+ attributes_hash.each do |name, value|
29
+ value = DateTime.parse(value) if name == :token_retrieval_time && value.is_a?(String)
30
+
31
+ instance_variable_set(:"@#{name}", value)
32
+ end
33
+
34
+ self.token_retrieval_time = DateTime.now if token_retrieval_time.blank?
35
+ end
36
+
37
+ # @api private
38
+ def to_hash
39
+ self.class::ATTRIBUTES.each_with_object({}) do |attribute, hash|
40
+ value = send(attribute)
41
+ next if value.nil?
42
+
43
+ value = token_retrieval_time.iso8601 if attribute == :token_retrieval_time
44
+
45
+ hash[attribute] = value
46
+ end
47
+ end
48
+
49
+ # @api private
50
+ def to_s
51
+ JSON.generate(to_hash)
52
+ end
53
+
54
+ # @api private
55
+ def add_to_client(client)
56
+ client.oauth_credentials = self
57
+ self.client = client
58
+
59
+ return unless access_token.present?
60
+
61
+ client.set_bearer_token(access_token)
62
+ end
63
+
64
+ # @api private
65
+ def need_to_refresh?
66
+ return false if access_token.blank? || refresh_token.blank?
67
+
68
+ return true if expires_in.blank?
69
+
70
+ token_retrieval_time.to_i + expires_in - DateTime.now.to_i < 60
71
+ end
72
+
73
+ # @api private
74
+ def able_to_refresh?
75
+ refresh_token.present? && token_url.present?
76
+ end
77
+
78
+ # @api private
79
+ def confidential_client?
80
+ client_id.present? && client_secret.present?
81
+ end
82
+
83
+ # @api private
84
+ def oauth2_refresh_params
85
+ {
86
+ 'grant_type' => 'refresh_token',
87
+ 'refresh_token' => refresh_token
88
+ }
89
+ end
90
+
91
+ # @api private
92
+ def oauth2_refresh_headers
93
+ base_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
94
+
95
+ return base_headers unless confidential_client?
96
+
97
+ credentials = "#{client_id}:#{client_secret}"
98
+
99
+ base_headers.merge(
100
+ 'Authorization' => "Basic #{Base64.strict_encode64(credentials)}"
101
+ )
102
+ end
103
+
104
+ def update_from_response_body(request)
105
+ token_response_body = JSON.parse(request.response_body)
106
+
107
+ expires_in = token_response_body['expires_in'].is_a?(Numeric) ? token_response_body['expires_in'] : nil
108
+
109
+ self.access_token = token_response_body['access_token']
110
+ self.refresh_token = token_response_body['refresh_token']
111
+ self.expires_in = expires_in
112
+ self.token_retrieval_time = DateTime.now
113
+
114
+ add_to_client(client)
115
+ self
116
+ end
117
+ end
118
+ end
119
+ end
@@ -229,12 +229,14 @@ module Inferno
229
229
  # @param input_definition [Hash] options for input such as type, description, or title
230
230
  # @option input_definition [String] :title Human readable title for input
231
231
  # @option input_definition [String] :description Description for the input
232
- # @option input_definition [String] :type text | textarea
232
+ # @option input_definition [String] :type text | textarea | radio
233
233
  # @option input_definition [String] :default The default value for the input
234
234
  # @option input_definition [Boolean] :optional Set to true to not require input for test execution
235
+ # @option input_definition [Hash] :options Possible input option formats based on input type
236
+ # @option options [Array] :list_options Array of options for input formats that require a list of possible values
235
237
  # @return [void]
236
238
  # @example
237
- # input :patientid, title: 'Patient ID', description: 'The ID of the patient being searched for',
239
+ # input :patient_id, title: 'Patient ID', description: 'The ID of the patient being searched for',
238
240
  # default: 'default_patient_id'
239
241
  # @example
240
242
  # input :textarea, title: 'Textarea Input Example', type: 'textarea', optional: true
@@ -252,14 +254,24 @@ module Inferno
252
254
 
253
255
  # Define outputs
254
256
  #
255
- # @param output_list [Symbol]
257
+ # @param identifier [Symbol] identifier for the output
258
+ # @param other_identifiers [Symbol] array of symbols if specifying multiple outputs
259
+ # @param output_definition [Hash] options for output
260
+ # @option output_definition [String] :type text | textarea | oauth_credentials
256
261
  # @return [void]
257
262
  # @example
258
- # output :patient_id, :bearer_token
259
- def output(*output_list)
260
- output_list.each do |output_identifier|
261
- outputs << output_identifier
262
- config.add_output(output_identifier)
263
+ # output :patient_id, :condition_id, :observation_id
264
+ # @example
265
+ # output :oauth_credentials, type: 'oauth_credentials'
266
+ def output(identifier, *other_identifiers, **output_definition)
267
+ if other_identifiers.present?
268
+ [identifier, *other_identifiers].compact.each do |output_identifier|
269
+ outputs << output_identifier
270
+ config.add_output(output_identifier)
271
+ end
272
+ else
273
+ outputs << identifier
274
+ config.add_output(identifier, output_definition)
263
275
  end
264
276
  end
265
277
 
@@ -186,6 +186,12 @@ module Inferno
186
186
  .map { |header_name, value| Header.new(name: header_name.downcase, value: value, type: 'request') }
187
187
  response_headers = response[:headers]
188
188
  .map { |header_name, value| Header.new(name: header_name.downcase, value: value, type: 'response') }
189
+ request_body =
190
+ if request.dig(:headers, 'Content-Type')&.include?('application/x-www-form-urlencoded')
191
+ URI.encode_www_form(request[:payload])
192
+ else
193
+ request[:payload]
194
+ end
189
195
 
190
196
  new(
191
197
  verb: request[:method],
@@ -193,7 +199,7 @@ module Inferno
193
199
  direction: direction,
194
200
  name: name,
195
201
  status: response[:code].to_i,
196
- request_body: request[:payload],
202
+ request_body: request_body,
197
203
  response_body: response[:body],
198
204
  test_session_id: test_session_id,
199
205
  headers: request_headers + response_headers
@@ -62,5 +62,24 @@ module Inferno
62
62
  super('The chosen runnable must be run as part of a group')
63
63
  end
64
64
  end
65
+
66
+ class UnknownAttributeException < RuntimeError
67
+ def initialize(attributes, klass)
68
+ attributes_string = attributes.map { |attribute| "'#{attribute}'" }.join(', ')
69
+ super("Unknown attributes for #{klass.name}: #{attributes_string}")
70
+ end
71
+ end
72
+
73
+ class UnknownSessionDataType < RuntimeError
74
+ def initialize(output)
75
+ super("Unknown type '#{output[:type]}' for '#{output[:name]}'.")
76
+ end
77
+ end
78
+
79
+ class BadSessionDataType < RuntimeError
80
+ def initialize(name, expected_class, actual_class)
81
+ super("Expected '#{name}' to be a #{expected_class.name}, but found a #{actual_class.name}.")
82
+ end
83
+ end
65
84
  end
66
85
  end
@@ -0,0 +1,13 @@
1
+ module FHIR
2
+ class Client
3
+ attr_accessor :oauth_credentials
4
+
5
+ def need_to_refresh?
6
+ oauth_credentials&.need_to_refresh?
7
+ end
8
+
9
+ def able_to_refresh?
10
+ oauth_credentials&.able_to_refresh?
11
+ end
12
+ end
13
+ end