graphql 1.1.0 → 1.2.0
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/graphql/analysis/analyze_query.rb +4 -2
- data/lib/graphql/analysis/field_usage.rb +4 -4
- data/lib/graphql/analysis/query_complexity.rb +16 -21
- data/lib/graphql/argument.rb +13 -6
- data/lib/graphql/base_type.rb +2 -1
- data/lib/graphql/compatibility/execution_specification.rb +76 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +16 -2
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -5
- data/lib/graphql/compatibility/schema_parser_specification.rb +6 -0
- data/lib/graphql/define/assign_argument.rb +8 -2
- data/lib/graphql/define/instance_definable.rb +12 -15
- data/lib/graphql/directive.rb +2 -1
- data/lib/graphql/enum_type.rb +5 -7
- data/lib/graphql/field.rb +6 -11
- data/lib/graphql/field/resolve.rb +1 -0
- data/lib/graphql/input_object_type.rb +9 -9
- data/lib/graphql/interface_type.rb +2 -1
- data/lib/graphql/internal_representation.rb +1 -0
- data/lib/graphql/internal_representation/node.rb +31 -9
- data/lib/graphql/internal_representation/rewrite.rb +26 -26
- data/lib/graphql/internal_representation/selections.rb +41 -0
- data/lib/graphql/introspection/input_value_type.rb +6 -2
- data/lib/graphql/language/generation.rb +2 -0
- data/lib/graphql/language/lexer.rl +4 -0
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +525 -509
- data/lib/graphql/language/parser.y +2 -0
- data/lib/graphql/object_type.rb +2 -2
- data/lib/graphql/query.rb +21 -0
- data/lib/graphql/query/context.rb +52 -4
- data/lib/graphql/query/serial_execution.rb +3 -4
- data/lib/graphql/query/serial_execution/field_resolution.rb +35 -36
- data/lib/graphql/query/serial_execution/operation_resolution.rb +9 -15
- data/lib/graphql/query/serial_execution/selection_resolution.rb +14 -11
- data/lib/graphql/query/serial_execution/value_resolution.rb +18 -17
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/relay/mutation.rb +5 -8
- data/lib/graphql/scalar_type.rb +1 -2
- data/lib/graphql/schema.rb +2 -13
- data/lib/graphql/schema/build_from_definition.rb +28 -13
- data/lib/graphql/schema/loader.rb +4 -1
- data/lib/graphql/schema/printer.rb +10 -3
- data/lib/graphql/schema/timeout_middleware.rb +18 -2
- data/lib/graphql/schema/unique_within_type.rb +6 -3
- data/lib/graphql/static_validation/literal_validator.rb +3 -1
- data/lib/graphql/union_type.rb +1 -2
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -0
- data/spec/graphql/analysis/analyze_query_spec.rb +6 -8
- data/spec/graphql/argument_spec.rb +18 -0
- data/spec/graphql/define/assign_argument_spec.rb +48 -0
- data/spec/graphql/define/instance_definable_spec.rb +4 -2
- data/spec/graphql/execution_error_spec.rb +66 -0
- data/spec/graphql/input_object_type_spec.rb +81 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +104 -21
- data/spec/graphql/introspection/input_value_type_spec.rb +43 -6
- data/spec/graphql/introspection/schema_type_spec.rb +1 -0
- data/spec/graphql/introspection/type_type_spec.rb +2 -0
- data/spec/graphql/language/generation_spec.rb +3 -2
- data/spec/graphql/query/arguments_spec.rb +17 -4
- data/spec/graphql/query/context_spec.rb +23 -0
- data/spec/graphql/query/variables_spec.rb +15 -1
- data/spec/graphql/relay/mutation_spec.rb +42 -2
- data/spec/graphql/schema/build_from_definition_spec.rb +4 -2
- data/spec/graphql/schema/loader_spec.rb +59 -1
- data/spec/graphql/schema/printer_spec.rb +2 -0
- data/spec/graphql/schema/reduce_types_spec.rb +1 -1
- data/spec/graphql/schema/timeout_middleware_spec.rb +2 -2
- data/spec/graphql/schema/unique_within_type_spec.rb +9 -0
- data/spec/graphql/schema/validation_spec.rb +15 -3
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +122 -0
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +78 -0
- data/spec/support/dairy_app.rb +9 -0
- data/spec/support/minimum_input_object.rb +4 -0
- data/spec/support/star_wars_schema.rb +1 -1
- metadata +5 -5
- data/lib/graphql/query/serial_execution/execution_context.rb +0 -37
- data/spec/graphql/query/serial_execution/execution_context_spec.rb +0 -54
@@ -172,11 +172,13 @@ rule
|
|
172
172
|
| STRING { return val[0].to_s }
|
173
173
|
| TRUE { return true }
|
174
174
|
| FALSE { return false }
|
175
|
+
| null_value
|
175
176
|
| variable
|
176
177
|
| list_value
|
177
178
|
| object_value
|
178
179
|
| enum_value
|
179
180
|
|
181
|
+
null_value: NULL { return make_node(:NullValue, name: val[0], position_source: val[0]) }
|
180
182
|
variable: VAR_SIGN name { return make_node(:VariableIdentifier, name: val[1], position_source: val[0]) }
|
181
183
|
|
182
184
|
list_value:
|
data/lib/graphql/object_type.rb
CHANGED
@@ -23,7 +23,8 @@ module GraphQL
|
|
23
23
|
class ObjectType < GraphQL::BaseType
|
24
24
|
accepts_definitions :interfaces, :fields, :mutation, field: GraphQL::Define::AssignObjectField
|
25
25
|
|
26
|
-
|
26
|
+
attr_accessor :fields, :mutation
|
27
|
+
ensure_defined(:fields, :mutation, :interfaces)
|
27
28
|
|
28
29
|
# @!attribute fields
|
29
30
|
# @return [Hash<String => GraphQL::Field>] Map String fieldnames to their {GraphQL::Field} implementations
|
@@ -45,7 +46,6 @@ module GraphQL
|
|
45
46
|
|
46
47
|
def interfaces
|
47
48
|
@clean_interfaces ||= begin
|
48
|
-
ensure_defined
|
49
49
|
if @dirty_interfaces.respond_to?(:map)
|
50
50
|
@dirty_interfaces.map { |i_type| GraphQL::BaseType.resolve_related_type(i_type) }
|
51
51
|
else
|
data/lib/graphql/query.rb
CHANGED
@@ -167,6 +167,27 @@ module GraphQL
|
|
167
167
|
@valid
|
168
168
|
end
|
169
169
|
|
170
|
+
def selections(nodes, type)
|
171
|
+
@selections ||= Hash.new { |h, k| h[k] = GraphQL::InternalRepresentation::Selections.build(self, k) }
|
172
|
+
@selections[nodes][type]
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_type(type_name)
|
176
|
+
@warden.get_type(type_name)
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_field(type, name)
|
180
|
+
@warden.get_field(type, name)
|
181
|
+
end
|
182
|
+
|
183
|
+
def possible_types(type)
|
184
|
+
@warden.possible_types(type)
|
185
|
+
end
|
186
|
+
|
187
|
+
def resolve_type(type)
|
188
|
+
@schema.resolve_type(type, @context)
|
189
|
+
end
|
190
|
+
|
170
191
|
private
|
171
192
|
|
172
193
|
# Assert that the passed-in query string is internally consistent
|
@@ -3,16 +3,23 @@ module GraphQL
|
|
3
3
|
# Expose some query-specific info to field resolve functions.
|
4
4
|
# It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`.
|
5
5
|
class Context
|
6
|
-
|
6
|
+
attr_reader :execution_strategy
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def execution_strategy=(new_strategy)
|
9
|
+
# GraphQL::Batch re-assigns this value but it was previously not used
|
10
|
+
# (ExecutionContext#strategy was used instead)
|
11
|
+
# now it _is_ used, but it breaks GraphQL::Batch tests
|
12
|
+
@execution_strategy ||= new_strategy
|
11
13
|
end
|
12
14
|
|
13
15
|
# @return [GraphQL::InternalRepresentation::Node] The internal representation for this query node
|
14
16
|
attr_accessor :irep_node
|
15
17
|
|
18
|
+
# @return [GraphQL::Language::Nodes::Field] The AST node for the currently-executing field
|
19
|
+
def ast_node
|
20
|
+
@irep_node.ast_node
|
21
|
+
end
|
22
|
+
|
16
23
|
# @return [Array<GraphQL::ExecutionError>] errors returned during execution
|
17
24
|
attr_reader :errors
|
18
25
|
|
@@ -25,6 +32,9 @@ module GraphQL
|
|
25
32
|
# @return [GraphQL::Schema::Mask::Warden]
|
26
33
|
attr_reader :warden
|
27
34
|
|
35
|
+
# @return [Array<String, Integer>] The current position in the result
|
36
|
+
attr_reader :path
|
37
|
+
|
28
38
|
# Make a new context which delegates key lookup to `values`
|
29
39
|
# @param query [GraphQL::Query] the query who owns this context
|
30
40
|
# @param values [Hash] A hash of arbitrary values which will be accessible at query-time
|
@@ -34,6 +44,7 @@ module GraphQL
|
|
34
44
|
@values = values || {}
|
35
45
|
@errors = []
|
36
46
|
@warden = query.warden
|
47
|
+
@path = []
|
37
48
|
end
|
38
49
|
|
39
50
|
# Lookup `key` from the hash passed to {Schema#execute} as `context:`
|
@@ -45,6 +56,43 @@ module GraphQL
|
|
45
56
|
def []=(key, value)
|
46
57
|
@values[key] = value
|
47
58
|
end
|
59
|
+
|
60
|
+
def spawn(path:, irep_node:)
|
61
|
+
FieldResolutionContext.new(context: self, path: path, irep_node: irep_node)
|
62
|
+
end
|
63
|
+
|
64
|
+
class FieldResolutionContext
|
65
|
+
extend Forwardable
|
66
|
+
|
67
|
+
attr_reader :path, :irep_node
|
68
|
+
|
69
|
+
def initialize(context:, path:, irep_node:)
|
70
|
+
@context = context
|
71
|
+
@path = path
|
72
|
+
@irep_node = irep_node
|
73
|
+
end
|
74
|
+
|
75
|
+
def_delegators :@context, :[], :[]=, :spawn, :query, :schema, :warden, :errors, :execution_strategy
|
76
|
+
|
77
|
+
# @return [GraphQL::Language::Nodes::Field] The AST node for the currently-executing field
|
78
|
+
def ast_node
|
79
|
+
@irep_node.ast_node
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add error to current field resolution.
|
83
|
+
# @param error [GraphQL::ExecutionError] an execution error
|
84
|
+
# @return [void]
|
85
|
+
def add_error(error)
|
86
|
+
unless error.is_a?(ExecutionError)
|
87
|
+
raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
|
88
|
+
end
|
89
|
+
|
90
|
+
error.ast_node ||= irep_node.ast_node
|
91
|
+
error.path ||= path
|
92
|
+
errors << error
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
48
96
|
end
|
49
97
|
end
|
50
98
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require "graphql/query/serial_execution/execution_context"
|
2
1
|
require "graphql/query/serial_execution/value_resolution"
|
3
2
|
require "graphql/query/serial_execution/field_resolution"
|
4
3
|
require "graphql/query/serial_execution/operation_resolution"
|
@@ -18,11 +17,11 @@ module GraphQL
|
|
18
17
|
def execute(ast_operation, root_type, query_object)
|
19
18
|
irep_root = query_object.internal_representation[ast_operation.name]
|
20
19
|
|
21
|
-
operation_resolution.
|
20
|
+
operation_resolution.resolve(
|
22
21
|
irep_root,
|
23
22
|
root_type,
|
24
|
-
|
25
|
-
)
|
23
|
+
query_object
|
24
|
+
)
|
26
25
|
end
|
27
26
|
|
28
27
|
def field_resolution
|
@@ -2,15 +2,17 @@ module GraphQL
|
|
2
2
|
class Query
|
3
3
|
class SerialExecution
|
4
4
|
class FieldResolution
|
5
|
-
attr_reader :irep_node, :parent_type, :target, :
|
5
|
+
attr_reader :irep_node, :parent_type, :target, :field, :arguments, :query
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@irep_node =
|
7
|
+
def initialize(irep_nodes, parent_type, target, query_ctx)
|
8
|
+
@irep_node = irep_nodes.first
|
9
|
+
@irep_nodes = irep_nodes
|
9
10
|
@parent_type = parent_type
|
10
11
|
@target = target
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
12
|
+
@field_ctx = query_ctx.spawn(path: query_ctx.path + [irep_node.name], irep_node: irep_node)
|
13
|
+
@query = query_ctx.query
|
14
|
+
@field = @query.get_field(parent_type, irep_node.definition_name)
|
15
|
+
@arguments = @query.arguments_for(irep_node, @field)
|
14
16
|
end
|
15
17
|
|
16
18
|
def result
|
@@ -19,6 +21,11 @@ module GraphQL
|
|
19
21
|
{ result_name => get_finished_value(raw_value) }
|
20
22
|
end
|
21
23
|
|
24
|
+
# GraphQL::Batch depends on this
|
25
|
+
def execution_context
|
26
|
+
@field_ctx
|
27
|
+
end
|
28
|
+
|
22
29
|
private
|
23
30
|
|
24
31
|
# After getting the value from the field's resolve method,
|
@@ -27,15 +34,15 @@ module GraphQL
|
|
27
34
|
case raw_value
|
28
35
|
when GraphQL::ExecutionError
|
29
36
|
raw_value.ast_node = irep_node.ast_node
|
30
|
-
raw_value.path =
|
31
|
-
|
37
|
+
raw_value.path = @field_ctx.path
|
38
|
+
@query.context.errors.push(raw_value)
|
32
39
|
when Array
|
33
40
|
list_errors = raw_value.each_with_index.select { |value, _| value.is_a?(GraphQL::ExecutionError) }
|
34
41
|
if list_errors.any?
|
35
42
|
list_errors.each do |error, index|
|
36
43
|
error.ast_node = irep_node.ast_node
|
37
|
-
error.path =
|
38
|
-
|
44
|
+
error.path = @field_ctx.path + [index]
|
45
|
+
@query.context.errors.push(error)
|
39
46
|
end
|
40
47
|
end
|
41
48
|
end
|
@@ -46,14 +53,14 @@ module GraphQL
|
|
46
53
|
field,
|
47
54
|
field.type,
|
48
55
|
raw_value,
|
49
|
-
|
50
|
-
|
56
|
+
@irep_nodes,
|
57
|
+
@field_ctx,
|
51
58
|
)
|
52
59
|
rescue GraphQL::InvalidNullError => err
|
53
60
|
if field.type.kind.non_null?
|
54
61
|
raise(err)
|
55
62
|
else
|
56
|
-
err.parent_error? ||
|
63
|
+
err.parent_error? || @query.context.errors.push(err)
|
57
64
|
nil
|
58
65
|
end
|
59
66
|
end
|
@@ -65,34 +72,26 @@ module GraphQL
|
|
65
72
|
# If the middleware chain returns a GraphQL::ExecutionError, its message
|
66
73
|
# is added to the "errors" key.
|
67
74
|
def get_raw_value
|
68
|
-
middlewares =
|
69
|
-
query_context = execution_context.query.context
|
70
|
-
# setup
|
71
|
-
query_context.irep_node = @irep_node
|
75
|
+
middlewares = @query.schema.middleware
|
72
76
|
|
73
|
-
resolve_arguments = [parent_type, target, field, arguments,
|
77
|
+
resolve_arguments = [parent_type, target, field, arguments, @field_ctx]
|
74
78
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
86
|
-
rescue GraphQL::ExecutionError => err
|
87
|
-
err
|
79
|
+
begin
|
80
|
+
# only run a middleware chain if there are any middleware
|
81
|
+
if middlewares.any?
|
82
|
+
chain = GraphQL::Schema::MiddlewareChain.new(
|
83
|
+
steps: middlewares + [FieldResolveStep],
|
84
|
+
arguments: resolve_arguments
|
85
|
+
)
|
86
|
+
chain.call
|
87
|
+
else
|
88
|
+
FieldResolveStep.call(*resolve_arguments)
|
88
89
|
end
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
resolve_value
|
90
|
+
rescue GraphQL::ExecutionError => err
|
91
|
+
err
|
92
|
+
end
|
93
93
|
end
|
94
94
|
|
95
|
-
|
96
95
|
# A `.call`-able suitable to be the last step in a middleware chain
|
97
96
|
module FieldResolveStep
|
98
97
|
# Execute the field's resolve method
|
@@ -1,24 +1,18 @@
|
|
1
1
|
module GraphQL
|
2
2
|
class Query
|
3
3
|
class SerialExecution
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@target = target
|
9
|
-
@irep_node = irep_node
|
10
|
-
@execution_context = execution_context
|
11
|
-
end
|
12
|
-
|
13
|
-
def result
|
14
|
-
execution_context.strategy.selection_resolution.resolve(
|
15
|
-
execution_context.query.root_value,
|
4
|
+
module OperationResolution
|
5
|
+
def self.resolve(irep_node, target, query)
|
6
|
+
result = query.context.execution_strategy.selection_resolution.resolve(
|
7
|
+
query.root_value,
|
16
8
|
target,
|
17
|
-
irep_node,
|
18
|
-
|
9
|
+
[irep_node],
|
10
|
+
query.context,
|
19
11
|
)
|
12
|
+
|
13
|
+
result
|
20
14
|
rescue GraphQL::InvalidNullError => err
|
21
|
-
err.parent_error? ||
|
15
|
+
err.parent_error? || query.context.errors.push(err)
|
22
16
|
nil
|
23
17
|
end
|
24
18
|
end
|
@@ -2,18 +2,21 @@ module GraphQL
|
|
2
2
|
class Query
|
3
3
|
class SerialExecution
|
4
4
|
module SelectionResolution
|
5
|
-
def self.resolve(target, current_type,
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
5
|
+
def self.resolve(target, current_type, irep_nodes, query_ctx)
|
6
|
+
own_selections = query_ctx.query.selections(irep_nodes, current_type)
|
7
|
+
|
8
|
+
selection_result = {}
|
9
|
+
|
10
|
+
own_selections.each do |name, child_irep_nodes|
|
11
|
+
selection_result.merge!(query_ctx.execution_strategy.field_resolution.new(
|
12
|
+
child_irep_nodes,
|
13
|
+
current_type,
|
14
|
+
target,
|
15
|
+
query_ctx
|
16
|
+
).result)
|
16
17
|
end
|
18
|
+
|
19
|
+
selection_result
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
@@ -2,7 +2,7 @@ module GraphQL
|
|
2
2
|
class Query
|
3
3
|
class SerialExecution
|
4
4
|
module ValueResolution
|
5
|
-
def self.resolve(parent_type, field_defn, field_type, value,
|
5
|
+
def self.resolve(parent_type, field_defn, field_type, value, irep_nodes, query_ctx)
|
6
6
|
if value.nil? || value.is_a?(GraphQL::ExecutionError)
|
7
7
|
if field_type.kind.non_null?
|
8
8
|
raise GraphQL::InvalidNullError.new(parent_type.name, field_defn.name, value)
|
@@ -14,21 +14,21 @@ module GraphQL
|
|
14
14
|
when GraphQL::TypeKinds::SCALAR
|
15
15
|
field_type.coerce_result(value)
|
16
16
|
when GraphQL::TypeKinds::ENUM
|
17
|
-
field_type.coerce_result(value,
|
17
|
+
field_type.coerce_result(value, query_ctx.query.warden)
|
18
18
|
when GraphQL::TypeKinds::LIST
|
19
19
|
wrapped_type = field_type.of_type
|
20
20
|
result = value.each_with_index.map do |inner_value, index|
|
21
|
-
|
22
|
-
resolve(
|
21
|
+
inner_ctx = query_ctx.spawn(path: query_ctx.path + [index], irep_node: query_ctx.irep_node)
|
22
|
+
inner_result = resolve(
|
23
23
|
parent_type,
|
24
24
|
field_defn,
|
25
25
|
wrapped_type,
|
26
26
|
inner_value,
|
27
|
-
|
28
|
-
|
27
|
+
irep_nodes,
|
28
|
+
inner_ctx,
|
29
29
|
)
|
30
|
+
inner_result
|
30
31
|
end
|
31
|
-
irep_node.index = nil
|
32
32
|
result
|
33
33
|
when GraphQL::TypeKinds::NON_NULL
|
34
34
|
wrapped_type = field_type.of_type
|
@@ -37,30 +37,31 @@ module GraphQL
|
|
37
37
|
field_defn,
|
38
38
|
wrapped_type,
|
39
39
|
value,
|
40
|
-
|
41
|
-
|
40
|
+
irep_nodes,
|
41
|
+
query_ctx,
|
42
42
|
)
|
43
43
|
when GraphQL::TypeKinds::OBJECT
|
44
|
-
|
44
|
+
query_ctx.execution_strategy.selection_resolution.resolve(
|
45
45
|
value,
|
46
46
|
field_type,
|
47
|
-
|
48
|
-
|
47
|
+
irep_nodes,
|
48
|
+
query_ctx
|
49
49
|
)
|
50
50
|
when GraphQL::TypeKinds::UNION, GraphQL::TypeKinds::INTERFACE
|
51
|
-
|
52
|
-
|
51
|
+
query = query_ctx.query
|
52
|
+
resolved_type = query.resolve_type(value)
|
53
|
+
possible_types = query.possible_types(field_type)
|
53
54
|
|
54
55
|
if !possible_types.include?(resolved_type)
|
55
|
-
raise GraphQL::UnresolvedTypeError.new(
|
56
|
+
raise GraphQL::UnresolvedTypeError.new(irep_nodes.first.definition_name, field_type, parent_type, resolved_type, possible_types)
|
56
57
|
else
|
57
58
|
resolve(
|
58
59
|
parent_type,
|
59
60
|
field_defn,
|
60
61
|
resolved_type,
|
61
62
|
value,
|
62
|
-
|
63
|
-
|
63
|
+
irep_nodes,
|
64
|
+
query_ctx,
|
64
65
|
)
|
65
66
|
end
|
66
67
|
else
|
@@ -39,7 +39,7 @@ module GraphQL
|
|
39
39
|
validation_result = variable_type.validate_input(provided_value, @warden)
|
40
40
|
if !validation_result.valid?
|
41
41
|
@errors << GraphQL::Query::VariableValidationError.new(ast_variable, variable_type, provided_value, validation_result)
|
42
|
-
elsif provided_value.nil?
|
42
|
+
elsif !@provided_variables.key?(variable_name) && provided_value.nil?
|
43
43
|
GraphQL::Query::LiteralInput.coerce(variable_type, default_value, {})
|
44
44
|
else
|
45
45
|
variable_type.coerce_input(provided_value)
|