inferno_core 0.2.0 → 0.3.0.rc1
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.
- checksums.yaml +4 -4
- data/lib/inferno/apps/web/controllers/test_runs/create.rb +33 -14
- data/lib/inferno/apps/web/index.html.erb +16 -1
- data/lib/inferno/apps/web/serializers/result.rb +4 -0
- data/lib/inferno/apps/web/serializers/test.rb +1 -1
- data/lib/inferno/apps/web/serializers/test_group.rb +1 -1
- data/lib/inferno/apps/web/serializers/test_suite.rb +1 -0
- data/lib/inferno/dsl/configurable.rb +22 -8
- data/lib/inferno/dsl/fhir_validation.rb +3 -1
- data/lib/inferno/dsl/input_output_handling.rb +185 -0
- data/lib/inferno/dsl/runnable.rb +24 -133
- data/lib/inferno/entities/input.rb +121 -0
- data/lib/inferno/entities/test.rb +5 -5
- data/lib/inferno/ext/fhir_models.rb +59 -0
- data/lib/inferno/public/bundle.js +15 -15
- data/lib/inferno/public/bundle.js.LICENSE.txt +2 -0
- data/lib/inferno/test_runner.rb +24 -2
- data/lib/inferno/utils/preset_template_generator.rb +4 -5
- data/lib/inferno/version.rb +2 -1
- data/spec/factories/result.rb +1 -0
- metadata +11 -10
- data/lib/inferno/public/72a5cd989e6aea904540824ec865a0f8.png +0 -0
- data/lib/inferno/public/e09b16f5cb645eb05f90c8f38f3409fb.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d689ccce031c3a119965c25c083606b6f5a789c61b29dca2b50971872205d47
|
4
|
+
data.tar.gz: 1be92b3799e22400b7d37ac1780de257c3104d23e809b712ee543b5437af8ecd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 563d0d578f6cc039ef95e3e5cc81b2285da6a04779e6705462111a626e826cd47cb2c82dd26dbd00f048d6e4f7277761151351da27e30a0ba3cbb64f6086440e
|
7
|
+
data.tar.gz: cda50085e8e67e5737a06b3440b2533804251d9eea5aff2235e28c9039387ca352aaa72b45f46e00e859adb8d8bfc5d83f63a0851c58c2d71dff5844ef35d5a7
|
@@ -11,6 +11,36 @@ module Inferno
|
|
11
11
|
|
12
12
|
PARAMS = [:test_session_id, :test_suite_id, :test_group_id, :test_id].freeze
|
13
13
|
|
14
|
+
def verify_runnable(runnable, inputs)
|
15
|
+
missing_inputs = runnable&.missing_inputs(inputs)
|
16
|
+
user_runnable = runnable&.user_runnable?
|
17
|
+
raise Inferno::Exceptions::RequiredInputsNotFound, missing_inputs if missing_inputs&.any?
|
18
|
+
raise Inferno::Exceptions::NotUserRunnableException unless user_runnable
|
19
|
+
end
|
20
|
+
|
21
|
+
def persist_inputs(params, test_run)
|
22
|
+
params[:inputs]&.each do |input_params|
|
23
|
+
input =
|
24
|
+
test_run.runnable.available_inputs
|
25
|
+
.find { |_, runnable_input| runnable_input.name == input_params[:name] }
|
26
|
+
&.last
|
27
|
+
|
28
|
+
if input.nil?
|
29
|
+
Inferno::Application['logger'].warning(
|
30
|
+
"Unknown input `#{input_params[:name]}` for #{test_run.runnable.id}: #{test_run.runnable.title}"
|
31
|
+
)
|
32
|
+
next
|
33
|
+
end
|
34
|
+
|
35
|
+
session_data_repo.save(
|
36
|
+
test_session_id: test_run.test_session_id,
|
37
|
+
name: input.name,
|
38
|
+
value: input_params[:value],
|
39
|
+
type: input.type
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
14
44
|
def call(params)
|
15
45
|
test_session = test_sessions_repo.find(params[:test_session_id])
|
16
46
|
|
@@ -21,24 +51,13 @@ module Inferno
|
|
21
51
|
return
|
22
52
|
end
|
23
53
|
|
24
|
-
|
25
|
-
# and runnable are validated
|
26
|
-
test_run = repo.create(create_params(params).merge(status: 'queued'))
|
27
|
-
missing_inputs = test_run.runnable.missing_inputs(params[:inputs])
|
54
|
+
verify_runnable(repo.build_entity(create_params(params)).runnable, params[:inputs])
|
28
55
|
|
29
|
-
|
30
|
-
raise Inferno::Exceptions::NotUserRunnableException unless test_run.runnable.user_runnable?
|
56
|
+
test_run = repo.create(create_params(params).merge(status: 'queued'))
|
31
57
|
|
32
58
|
self.body = serialize(test_run)
|
33
59
|
|
34
|
-
params
|
35
|
-
session_data_repo.save(
|
36
|
-
test_session_id: test_session.id,
|
37
|
-
name: input[:name],
|
38
|
-
value: input[:value],
|
39
|
-
type: input[:type]
|
40
|
-
)
|
41
|
-
end
|
60
|
+
persist_inputs(params, test_run)
|
42
61
|
|
43
62
|
Jobs.perform(Jobs::ExecuteTestRun, test_run.id)
|
44
63
|
rescue Sequel::ValidationFailed, Sequel::ForeignKeyConstraintViolation,
|
@@ -25,11 +25,26 @@
|
|
25
25
|
work correctly both with client-side routing and a non-root public URL.
|
26
26
|
Learn how to configure a non-root public URL by running `npm run build`.
|
27
27
|
-->
|
28
|
+
<style>
|
29
|
+
.wrapper {
|
30
|
+
height: 100%;
|
31
|
+
display: flex;
|
32
|
+
flex-direction: column;
|
33
|
+
}
|
34
|
+
.app {
|
35
|
+
height: 100%;
|
36
|
+
}
|
37
|
+
</style>
|
28
38
|
<title>Inferno</title>
|
29
39
|
</head>
|
30
40
|
<body>
|
31
41
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
32
|
-
<div
|
42
|
+
<div class='wrapper'>
|
43
|
+
<% if File.exist? (File.join(Dir.pwd, 'config', 'banner.html.erb')) %>
|
44
|
+
<div class='banner'><%= ERB.new(File.read(File.join(Dir.pwd, 'config', 'banner.html.erb'))).result %></div>
|
45
|
+
<% end %>
|
46
|
+
<div class='app' id="root"></div>
|
47
|
+
</div>
|
33
48
|
<!--
|
34
49
|
This HTML file is a template.
|
35
50
|
If you open it directly in the browser, you will see an empty page.
|
@@ -18,6 +18,10 @@ module Inferno
|
|
18
18
|
field :updated_at
|
19
19
|
field :optional?, name: :optional
|
20
20
|
|
21
|
+
field :inputs do |result, _options|
|
22
|
+
result.input_json.present? ? JSON.parse(result.input_json) : []
|
23
|
+
end
|
24
|
+
|
21
25
|
field :outputs do |result, _options|
|
22
26
|
result.output_json.present? ? JSON.parse(result.output_json) : []
|
23
27
|
end
|
@@ -7,7 +7,7 @@ module Inferno
|
|
7
7
|
field :short_id
|
8
8
|
field :title
|
9
9
|
field :short_title
|
10
|
-
field :
|
10
|
+
field :available_inputs, name: :inputs, extractor: HashValueExtractor, blueprint: Input
|
11
11
|
field :output_definitions, name: :outputs, extractor: HashValueExtractor
|
12
12
|
field :description
|
13
13
|
field :short_description
|
@@ -17,7 +17,7 @@ module Inferno
|
|
17
17
|
|
18
18
|
association :groups, name: :test_groups, blueprint: TestGroup
|
19
19
|
association :tests, blueprint: Test
|
20
|
-
field :
|
20
|
+
field :available_inputs, name: :inputs, extractor: HashValueExtractor, blueprint: Input
|
21
21
|
field :output_definitions, name: :outputs, extractor: HashValueExtractor
|
22
22
|
end
|
23
23
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative '../entities/input'
|
2
|
+
|
1
3
|
module Inferno
|
2
4
|
module DSL
|
3
5
|
# This module contains the DSL for managing runnable configuration.
|
@@ -35,7 +37,11 @@ module Inferno
|
|
35
37
|
new_configuration
|
36
38
|
end
|
37
39
|
|
38
|
-
self.configuration = configuration.deep_merge(config_to_apply)
|
40
|
+
self.configuration = configuration.deep_merge(config_to_apply.reject { |key, _| key == :inputs })
|
41
|
+
|
42
|
+
config_to_apply[:inputs]&.each do |identifier, new_input|
|
43
|
+
add_input(identifier, new_input.to_hash)
|
44
|
+
end
|
39
45
|
end
|
40
46
|
|
41
47
|
def options
|
@@ -49,28 +55,36 @@ module Inferno
|
|
49
55
|
end
|
50
56
|
|
51
57
|
def add_input(identifier, new_config = {})
|
52
|
-
existing_config =
|
53
|
-
|
58
|
+
existing_config = input(identifier)
|
59
|
+
|
60
|
+
if existing_config.nil?
|
61
|
+
return inputs[identifier] = Entities::Input.new(default_input_params(identifier).merge(new_config))
|
62
|
+
end
|
63
|
+
|
64
|
+
inputs[identifier] =
|
65
|
+
Entities::Input
|
66
|
+
.new(existing_config.to_hash)
|
67
|
+
.merge(Entities::Input.new(new_config))
|
54
68
|
end
|
55
69
|
|
56
|
-
def
|
70
|
+
def default_input_params(identifier)
|
57
71
|
{ name: identifier, type: 'text' }
|
58
72
|
end
|
59
73
|
|
60
|
-
def
|
74
|
+
def input_exists?(identifier)
|
61
75
|
inputs.key? identifier
|
62
76
|
end
|
63
77
|
|
64
|
-
def
|
78
|
+
def input(identifier)
|
65
79
|
inputs[identifier]
|
66
80
|
end
|
67
81
|
|
68
82
|
def input_name(identifier)
|
69
|
-
inputs
|
83
|
+
inputs[identifier]&.name
|
70
84
|
end
|
71
85
|
|
72
86
|
def input_type(identifier)
|
73
|
-
inputs
|
87
|
+
inputs[identifier]&.type
|
74
88
|
end
|
75
89
|
|
76
90
|
### Output Configuration ###
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative '../ext/fhir_models'
|
2
|
+
|
1
3
|
module Inferno
|
2
4
|
module DSL
|
3
5
|
# This module contains the methods needed to configure a validator to
|
@@ -168,7 +170,7 @@ module Inferno
|
|
168
170
|
def validate(resource, profile_url)
|
169
171
|
RestClient.post(
|
170
172
|
"#{url}/validate",
|
171
|
-
resource.
|
173
|
+
resource.source_contents,
|
172
174
|
params: { profile: profile_url }
|
173
175
|
).body
|
174
176
|
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module Inferno
|
2
|
+
module DSL
|
3
|
+
module InputOutputHandling
|
4
|
+
# Define inputs
|
5
|
+
#
|
6
|
+
# @param identifier [Symbol] identifier for the input
|
7
|
+
# @param other_identifiers [Symbol] array of symbols if specifying multiple inputs
|
8
|
+
# @param input_params [Hash] options for input such as type, description, or title
|
9
|
+
# @option input_params [String] :title Human readable title for input
|
10
|
+
# @option input_params [String] :description Description for the input
|
11
|
+
# @option input_params [String] :type text | textarea | radio
|
12
|
+
# @option input_params [String] :default The default value for the input
|
13
|
+
# @option input_params [Boolean] :optional Set to true to not require input for test execution
|
14
|
+
# @option input_params [Hash] :options Possible input option formats based on input type
|
15
|
+
# @option options [Array] :list_options Array of options for input formats that require a list of possible values
|
16
|
+
# @return [void]
|
17
|
+
# @example
|
18
|
+
# input :patient_id, title: 'Patient ID', description: 'The ID of the patient being searched for',
|
19
|
+
# default: 'default_patient_id'
|
20
|
+
# @example
|
21
|
+
# input :textarea, title: 'Textarea Input Example', type: 'textarea', optional: true
|
22
|
+
def input(identifier, *other_identifiers, **input_params)
|
23
|
+
if other_identifiers.present?
|
24
|
+
[identifier, *other_identifiers].compact.each do |input_identifier|
|
25
|
+
inputs << input_identifier
|
26
|
+
config.add_input(input_identifier)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
inputs << identifier
|
30
|
+
config.add_input(identifier, input_params)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Define outputs
|
35
|
+
#
|
36
|
+
# @param identifier [Symbol] identifier for the output
|
37
|
+
# @param other_identifiers [Symbol] array of symbols if specifying multiple outputs
|
38
|
+
# @param output_definition [Hash] options for output
|
39
|
+
# @option output_definition [String] :type text | textarea | oauth_credentials
|
40
|
+
# @return [void]
|
41
|
+
# @example
|
42
|
+
# output :patient_id, :condition_id, :observation_id
|
43
|
+
# @example
|
44
|
+
# output :oauth_credentials, type: 'oauth_credentials'
|
45
|
+
def output(identifier, *other_identifiers, **output_definition)
|
46
|
+
if other_identifiers.present?
|
47
|
+
[identifier, *other_identifiers].compact.each do |output_identifier|
|
48
|
+
outputs << output_identifier
|
49
|
+
config.add_output(output_identifier)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
outputs << identifier
|
53
|
+
config.add_output(identifier, output_definition)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @private
|
58
|
+
def inputs
|
59
|
+
@inputs ||= []
|
60
|
+
end
|
61
|
+
|
62
|
+
# @private
|
63
|
+
def outputs
|
64
|
+
@outputs ||= []
|
65
|
+
end
|
66
|
+
|
67
|
+
# @private
|
68
|
+
def output_definitions
|
69
|
+
config.outputs.slice(*outputs)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @private
|
73
|
+
def required_inputs
|
74
|
+
available_inputs
|
75
|
+
.reject { |_, input| input.optional }
|
76
|
+
.map { |_, input| input.name }
|
77
|
+
end
|
78
|
+
|
79
|
+
# @private
|
80
|
+
def missing_inputs(submitted_inputs)
|
81
|
+
submitted_inputs = [] if submitted_inputs.nil?
|
82
|
+
|
83
|
+
required_inputs.map(&:to_s) - submitted_inputs.map { |input| input[:name] }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Define a particular order for inputs to be presented in the API/UI
|
87
|
+
# @example
|
88
|
+
# group do
|
89
|
+
# input :input1, :input2, :input3
|
90
|
+
# input_order :input3, :input2, :input1
|
91
|
+
# end
|
92
|
+
# @param new_input_order [Array<String,Symbol>]
|
93
|
+
# @return [Array<String, Symbol>]
|
94
|
+
def input_order(*new_input_order)
|
95
|
+
return @input_order = new_input_order if new_input_order.present?
|
96
|
+
|
97
|
+
@input_order ||= []
|
98
|
+
end
|
99
|
+
|
100
|
+
# @private
|
101
|
+
def order_available_inputs(original_inputs)
|
102
|
+
input_names = original_inputs.map { |_, input| input.name }.join(', ')
|
103
|
+
|
104
|
+
ordered_inputs =
|
105
|
+
input_order.each_with_object({}) do |input_name, inputs|
|
106
|
+
key, input = original_inputs.find { |_, input| input.name == input_name.to_s }
|
107
|
+
if input.nil?
|
108
|
+
Inferno::Application[:logger].error <<~ERROR
|
109
|
+
Error trying to order inputs in #{id}: #{title}:
|
110
|
+
- Unable to find input #{input_name} in available inputs: #{input_names}
|
111
|
+
ERROR
|
112
|
+
next
|
113
|
+
end
|
114
|
+
inputs[key] = original_inputs.delete(key)
|
115
|
+
end
|
116
|
+
|
117
|
+
original_inputs.each do |key, input|
|
118
|
+
ordered_inputs[key] = input
|
119
|
+
end
|
120
|
+
|
121
|
+
ordered_inputs
|
122
|
+
end
|
123
|
+
|
124
|
+
# @private
|
125
|
+
def all_outputs
|
126
|
+
outputs
|
127
|
+
.map { |output_identifier| config.output_name(output_identifier) }
|
128
|
+
.concat(children.flat_map(&:all_outputs))
|
129
|
+
.uniq
|
130
|
+
end
|
131
|
+
|
132
|
+
# @private
|
133
|
+
# Inputs available for this runnable's children. A running list of outputs
|
134
|
+
# created by the children is used to exclude any inputs which are provided
|
135
|
+
# by an earlier child's output.
|
136
|
+
def children_available_inputs
|
137
|
+
@children_available_inputs ||=
|
138
|
+
begin
|
139
|
+
child_outputs = []
|
140
|
+
children.each_with_object({}) do |child, definitions|
|
141
|
+
new_definitions = child.available_inputs.map(&:dup)
|
142
|
+
new_definitions.each do |input, new_definition|
|
143
|
+
existing_definition = definitions[input]
|
144
|
+
|
145
|
+
updated_definition =
|
146
|
+
if existing_definition.present?
|
147
|
+
existing_definition.merge_with_child(new_definition)
|
148
|
+
else
|
149
|
+
new_definition
|
150
|
+
end
|
151
|
+
|
152
|
+
next if child_outputs.include?(updated_definition.name.to_sym)
|
153
|
+
|
154
|
+
definitions[updated_definition.name.to_sym] = updated_definition
|
155
|
+
end
|
156
|
+
|
157
|
+
child_outputs.concat(child.all_outputs).uniq!
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# @private
|
163
|
+
# Inputs available for the user for this runnable and all its children.
|
164
|
+
def available_inputs
|
165
|
+
@available_inputs ||=
|
166
|
+
begin
|
167
|
+
available_inputs =
|
168
|
+
config.inputs
|
169
|
+
.slice(*inputs)
|
170
|
+
.each_with_object({}) do |(_, input), inputs|
|
171
|
+
inputs[input.name.to_sym] = input
|
172
|
+
end
|
173
|
+
|
174
|
+
available_inputs.each do |input, current_definition|
|
175
|
+
child_definition = children_available_inputs[input]
|
176
|
+
current_definition.merge_with_child(child_definition)
|
177
|
+
end
|
178
|
+
|
179
|
+
available_inputs = children_available_inputs.merge(available_inputs)
|
180
|
+
order_available_inputs(available_inputs)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
data/lib/inferno/dsl/runnable.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'configurable'
|
2
|
+
require_relative 'input_output_handling'
|
2
3
|
require_relative 'resume_test_route'
|
3
4
|
require_relative '../utils/markdown_formatter'
|
4
5
|
|
@@ -20,6 +21,7 @@ module Inferno
|
|
20
21
|
def self.extended(extending_class)
|
21
22
|
super
|
22
23
|
extending_class.extend Configurable
|
24
|
+
extending_class.extend InputOutputHandling
|
23
25
|
|
24
26
|
extending_class.define_singleton_method(:inherited) do |subclass|
|
25
27
|
copy_instance_variables(subclass)
|
@@ -39,28 +41,35 @@ module Inferno
|
|
39
41
|
|
40
42
|
# Class instance variables are used to hold the metadata for Runnable
|
41
43
|
# classes. When inheriting from a Runnable class, these class instance
|
42
|
-
# variables need to be copied.
|
43
|
-
# need to be
|
44
|
+
# variables need to be copied. Some instance variables should not be
|
45
|
+
# copied, and will need to be repopulated from scratch on the new class.
|
46
|
+
# Any child Runnable classes will themselves need to be subclassed so that
|
47
|
+
# their parent can be updated.
|
48
|
+
VARIABLES_NOT_TO_COPY = [
|
49
|
+
:@id, # New runnable will have a different id
|
50
|
+
:@parent, # New runnable unlikely to have the same parent
|
51
|
+
:@children, # New subclasses have to be made for each child
|
52
|
+
:@test_count, # Needs to be recalculated
|
53
|
+
:@config, # Needs to be set by calling .config, which does extra work
|
54
|
+
:@available_inputs, # Needs to be recalculated
|
55
|
+
:@children_available_inputs # Needs to be recalculated
|
56
|
+
].freeze
|
57
|
+
|
44
58
|
# @private
|
45
59
|
def copy_instance_variables(subclass)
|
46
|
-
instance_variables
|
47
|
-
|
48
|
-
|
49
|
-
subclass.instance_variable_set(variable, instance_variable_get(variable).dup)
|
50
|
-
end
|
60
|
+
instance_variables
|
61
|
+
.reject { |variable| VARIABLES_NOT_TO_COPY.include? variable }
|
62
|
+
.each { |variable| subclass.instance_variable_set(variable, instance_variable_get(variable).dup) }
|
51
63
|
|
52
64
|
subclass.config(config)
|
53
65
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
subclass_child.parent = subclass
|
58
|
-
end
|
66
|
+
new_children = children.map do |child|
|
67
|
+
Class.new(child).tap do |subclass_child|
|
68
|
+
subclass_child.parent = subclass
|
59
69
|
end
|
60
|
-
|
61
|
-
subclass.instance_variable_set(:"@#{child_type}", new_children)
|
62
|
-
subclass.children.concat(new_children)
|
63
70
|
end
|
71
|
+
|
72
|
+
subclass.instance_variable_set(:@children, new_children)
|
64
73
|
end
|
65
74
|
|
66
75
|
# @private
|
@@ -253,36 +262,6 @@ module Inferno
|
|
253
262
|
@input_instructions = format_markdown(new_input_instructions)
|
254
263
|
end
|
255
264
|
|
256
|
-
# Define inputs
|
257
|
-
#
|
258
|
-
# @param identifier [Symbol] identifier for the input
|
259
|
-
# @param other_identifiers [Symbol] array of symbols if specifying multiple inputs
|
260
|
-
# @param input_definition [Hash] options for input such as type, description, or title
|
261
|
-
# @option input_definition [String] :title Human readable title for input
|
262
|
-
# @option input_definition [String] :description Description for the input
|
263
|
-
# @option input_definition [String] :type text | textarea | radio
|
264
|
-
# @option input_definition [String] :default The default value for the input
|
265
|
-
# @option input_definition [Boolean] :optional Set to true to not require input for test execution
|
266
|
-
# @option input_definition [Hash] :options Possible input option formats based on input type
|
267
|
-
# @option options [Array] :list_options Array of options for input formats that require a list of possible values
|
268
|
-
# @return [void]
|
269
|
-
# @example
|
270
|
-
# input :patient_id, title: 'Patient ID', description: 'The ID of the patient being searched for',
|
271
|
-
# default: 'default_patient_id'
|
272
|
-
# @example
|
273
|
-
# input :textarea, title: 'Textarea Input Example', type: 'textarea', optional: true
|
274
|
-
def input(identifier, *other_identifiers, **input_definition)
|
275
|
-
if other_identifiers.present?
|
276
|
-
[identifier, *other_identifiers].compact.each do |input_identifier|
|
277
|
-
inputs << input_identifier
|
278
|
-
config.add_input(input_identifier)
|
279
|
-
end
|
280
|
-
else
|
281
|
-
inputs << identifier
|
282
|
-
config.add_input(identifier, input_definition)
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
265
|
# Mark as optional. Tests are required by default.
|
287
266
|
#
|
288
267
|
# @param optional [Boolean]
|
@@ -317,62 +296,11 @@ module Inferno
|
|
317
296
|
!optional?
|
318
297
|
end
|
319
298
|
|
320
|
-
# Define outputs
|
321
|
-
#
|
322
|
-
# @param identifier [Symbol] identifier for the output
|
323
|
-
# @param other_identifiers [Symbol] array of symbols if specifying multiple outputs
|
324
|
-
# @param output_definition [Hash] options for output
|
325
|
-
# @option output_definition [String] :type text | textarea | oauth_credentials
|
326
|
-
# @return [void]
|
327
|
-
# @example
|
328
|
-
# output :patient_id, :condition_id, :observation_id
|
329
|
-
# @example
|
330
|
-
# output :oauth_credentials, type: 'oauth_credentials'
|
331
|
-
def output(identifier, *other_identifiers, **output_definition)
|
332
|
-
if other_identifiers.present?
|
333
|
-
[identifier, *other_identifiers].compact.each do |output_identifier|
|
334
|
-
outputs << output_identifier
|
335
|
-
config.add_output(output_identifier)
|
336
|
-
end
|
337
|
-
else
|
338
|
-
outputs << identifier
|
339
|
-
config.add_output(identifier, output_definition)
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
299
|
# @private
|
344
300
|
def default_id
|
345
301
|
to_s
|
346
302
|
end
|
347
303
|
|
348
|
-
# @private
|
349
|
-
def inputs
|
350
|
-
@inputs ||= []
|
351
|
-
end
|
352
|
-
|
353
|
-
# @private
|
354
|
-
def input_definitions
|
355
|
-
config.inputs.slice(*inputs)
|
356
|
-
end
|
357
|
-
|
358
|
-
# @private
|
359
|
-
def output_definitions
|
360
|
-
config.outputs.slice(*outputs)
|
361
|
-
end
|
362
|
-
|
363
|
-
# @private
|
364
|
-
def outputs
|
365
|
-
@outputs ||= []
|
366
|
-
end
|
367
|
-
|
368
|
-
# @private
|
369
|
-
def child_types
|
370
|
-
return [] if ancestors.include? Inferno::Entities::Test
|
371
|
-
return [:groups] if ancestors.include? Inferno::Entities::TestSuite
|
372
|
-
|
373
|
-
[:groups, :tests]
|
374
|
-
end
|
375
|
-
|
376
304
|
# @private
|
377
305
|
def children
|
378
306
|
@children ||= []
|
@@ -454,49 +382,12 @@ module Inferno
|
|
454
382
|
@test_count ||= children&.reduce(0) { |sum, child| sum + child.test_count } || 0
|
455
383
|
end
|
456
384
|
|
457
|
-
# @private
|
458
|
-
def required_inputs(prior_outputs = [])
|
459
|
-
required_inputs =
|
460
|
-
inputs
|
461
|
-
.reject { |input| input_definitions[input][:optional] }
|
462
|
-
.map { |input| config.input_name(input) }
|
463
|
-
.reject { |input| prior_outputs.include?(input) }
|
464
|
-
children_required_inputs = children.flat_map { |child| child.required_inputs(prior_outputs) }
|
465
|
-
prior_outputs.concat(outputs.map { |output| config.output_name(output) })
|
466
|
-
(required_inputs + children_required_inputs).flatten.uniq
|
467
|
-
end
|
468
|
-
|
469
|
-
# @private
|
470
|
-
def missing_inputs(submitted_inputs)
|
471
|
-
submitted_inputs = [] if submitted_inputs.nil?
|
472
|
-
|
473
|
-
required_inputs.map(&:to_s) - submitted_inputs.map { |input| input[:name] }
|
474
|
-
end
|
475
|
-
|
476
385
|
# @private
|
477
386
|
def user_runnable?
|
478
387
|
@user_runnable ||= parent.nil? ||
|
479
388
|
!parent.respond_to?(:run_as_group?) ||
|
480
389
|
(parent.user_runnable? && !parent.run_as_group?)
|
481
390
|
end
|
482
|
-
|
483
|
-
# @private
|
484
|
-
def available_input_definitions(prior_outputs = [])
|
485
|
-
available_input_definitions =
|
486
|
-
inputs
|
487
|
-
.each_with_object({}) do |input, definitions|
|
488
|
-
definitions[config.input_name(input)] =
|
489
|
-
config.input_config(input)
|
490
|
-
end
|
491
|
-
available_input_definitions.reject! { |input, _| prior_outputs.include? input }
|
492
|
-
|
493
|
-
children_available_input_definitions =
|
494
|
-
children.each_with_object({}) do |child, definitions|
|
495
|
-
definitions.merge!(child.available_input_definitions(prior_outputs))
|
496
|
-
end
|
497
|
-
prior_outputs.concat(outputs.map { |output| config.output_name(output) })
|
498
|
-
children_available_input_definitions.merge(available_input_definitions)
|
499
|
-
end
|
500
391
|
end
|
501
392
|
end
|
502
393
|
end
|