Skip to content

Commit 968478c

Browse files
authored
Merge pull request #762 from code0-tech/756-fix-unlimited-nested-parameters
fix unlimited nested parameters
2 parents 130ef06 + 5d500cb commit 968478c

34 files changed

+358
-372
lines changed

app/graphql/mutations/namespaces/projects/flows/create.rb

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,10 @@ def resolve(project_id:, flow:, **_params)
2424
}
2525
end
2626

27-
flow_type = SagittariusSchema.object_from_id(flow.type)
28-
if flow_type.nil?
29-
return {
30-
flow: nil,
31-
errors: [create_error(:flow_type_not_found, 'Invalid flow type id')],
32-
}
33-
end
34-
3527
::Namespaces::Projects::Flows::CreateService.new(
3628
current_authentication,
3729
namespace_project: project,
38-
flow_type: flow_type,
39-
starting_node_id: flow.starting_node_id,
40-
nodes: flow.nodes,
41-
flow_settings: flow.settings || [],
42-
name: flow.name
30+
flow_input: flow
4331
).execute.to_mutation_response(success_key: :flow)
4432
end
4533
end

app/graphql/types/flow_type.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class FlowType < Types::BaseObject
2828
field :nodes, Types::NodeFunctionType.connection_type,
2929
null: false,
3030
description: 'Nodes of the flow',
31-
method: :collect_node_functions
31+
method: :node_functions
3232

3333
expose_abilities %i[
3434
delete_flow

app/graphql/types/input/node_parameter_value_input_type.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ module Input
55
class NodeParameterValueInputType < Types::BaseInputObject
66
description 'Input type for parameter value'
77

8-
argument :function_value, Types::Input::NodeFunctionInputType,
9-
required: false, description: 'The function value of the parameter'
108
argument :literal_value, GraphQL::Types::JSON,
119
required: false, description: 'The literal value of the parameter'
10+
argument :node_function_id, Types::GlobalIdType[::NodeFunction],
11+
required: false, description: 'The function value of the parameter as an id'
1212
argument :reference_value, Types::Input::ReferenceValueInputType,
1313
required: false, description: 'The reference value of the parameter'
1414

15-
require_one_of %i[function_value literal_value reference_value]
15+
require_one_of %i[node_function_id literal_value reference_value]
1616
end
1717
end
1818
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
module Types
4+
class NodeFunctionIdWrapperType < Types::BaseObject
5+
description 'Represents a Node Function id wrapper.'
6+
7+
authorize :read_flow
8+
id_field NodeFunction
9+
end
10+
end

app/graphql/types/node_parameter_value_type.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ module Types
44
class NodeParameterValueType < Types::BaseUnion
55
description 'Represents a parameter value for a node.'
66

7-
possible_types Types::LiteralValueType, Types::ReferenceValueType, Types::NodeFunctionType,
8-
description: 'The value can be a literal, a reference, or a node function.'
7+
possible_types Types::LiteralValueType, Types::ReferenceValueType, Types::NodeFunctionIdWrapperType,
8+
description: 'The value can be a literal, a reference, or a node function id.'
99

1010
def self.resolve_type(object, _context)
1111
case object
1212
when ReferenceValue
1313
Types::ReferenceValueType
1414
when NodeFunction
15-
Types::NodeFunctionType
15+
Types::NodeFunctionIdWrapperType
1616
else
1717
Types::LiteralValueType
1818
end

app/models/flow.rb

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ class Flow < ApplicationRecord
55
belongs_to :flow_type
66
belongs_to :input_type, class_name: 'DataType', optional: true
77
belongs_to :return_type, class_name: 'DataType', optional: true
8-
belongs_to :starting_node, class_name: 'NodeFunction'
8+
belongs_to :starting_node, class_name: 'NodeFunction', optional: true
99

1010
has_many :flow_settings, class_name: 'FlowSetting', inverse_of: :flow
11+
has_many :node_functions, class_name: 'NodeFunction', inverse_of: :flow
1112

1213
validates :name, presence: true,
1314
allow_blank: false,
@@ -23,26 +24,7 @@ def to_grpc
2324
return_type_identifier: return_type&.identifier,
2425
settings: flow_settings.map(&:to_grpc),
2526
starting_node_id: starting_node.id,
26-
node_functions: collect_node_functions.map(&:to_grpc)
27+
node_functions: node_functions.map(&:to_grpc)
2728
)
2829
end
29-
30-
def collect_node_functions
31-
sql = <<-SQL
32-
WITH RECURSIVE node_function_tree AS (
33-
SELECT *
34-
FROM node_functions
35-
WHERE id = ? -- base case
36-
UNION ALL
37-
SELECT nf.*
38-
FROM node_functions nf
39-
INNER JOIN node_function_tree nf_tree
40-
ON nf.id = nf_tree.next_node_id
41-
)
42-
43-
SELECT DISTINCT * FROM node_function_tree ORDER BY id
44-
SQL
45-
46-
NodeFunction.find_by_sql([sql, starting_node_id])
47-
end
4830
end

app/models/node_function.rb

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,28 @@
33
class NodeFunction < ApplicationRecord
44
belongs_to :runtime_function, class_name: 'RuntimeFunctionDefinition'
55
belongs_to :next_node, class_name: 'NodeFunction', optional: true
6+
belongs_to :flow, class_name: 'Flow'
67

7-
has_one :previous_node, class_name: 'NodeFunction', foreign_key: :next_node_id, inverse_of: :next_node
8-
has_one :flow, class_name: 'Flow', inverse_of: :starting_node
8+
has_one :previous_node,
9+
class_name: 'NodeFunction',
10+
foreign_key: :next_node_id,
11+
inverse_of: :next_node
912

10-
has_many :node_parameter_values, class_name: 'NodeParameter', inverse_of: :function_value
11-
has_many :node_parameters, class_name: 'NodeParameter', inverse_of: :node_function
13+
has_many :node_parameter_values,
14+
class_name: 'NodeParameter',
15+
inverse_of: :function_value
1216

13-
validate :validate_recursion, if: :next_node_changed?
17+
has_many :node_parameters,
18+
class_name: 'NodeParameter',
19+
inverse_of: :node_function
1420

15-
def resolve_flow
16-
sql = <<-SQL
17-
WITH RECURSIVE node_function_tree AS (
18-
SELECT *
19-
FROM node_functions
20-
WHERE id = ? -- base case
21-
UNION ALL
22-
SELECT nf.*
23-
FROM node_functions nf
24-
INNER JOIN node_function_tree nf_tree
25-
ON nf.next_node_id = nf_tree.id
26-
)
27-
28-
SELECT f.*
29-
FROM flows f
30-
WHERE f.starting_node_id IN (
31-
SELECT id FROM node_function_tree
32-
)
33-
ORDER BY f.id
34-
SQL
35-
36-
flows = Flow.find_by_sql([sql, id])
37-
38-
raise 'Multiple flows found' if flows.count > 1
39-
40-
flows.first
41-
end
21+
validate :validate_recursion, if: :next_node_changed?
4222

4323
def validate_recursion
4424
current_node = self
25+
4526
until current_node.next_node.nil?
4627
current_node = current_node.next_node
47-
4828
if current_node == self
4929
errors.add(:next_node, :recursion)
5030
break
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
class NodeFunctionPolicy < BasePolicy
4-
delegate { subject.resolve_flow }
4+
delegate { subject.flow }
55
end

app/services/namespaces/projects/flows/create_service.rb

Lines changed: 19 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ class CreateService
77
include Sagittarius::Database::Transactional
88
include FlowServiceHelper
99

10-
attr_reader :current_authentication, :namespace_project, :params
10+
attr_reader :current_authentication, :namespace_project, :flow_input
1111

12-
def initialize(current_authentication, namespace_project:, **params)
12+
def initialize(current_authentication, namespace_project:, flow_input:)
1313
@current_authentication = current_authentication
1414
@namespace_project = namespace_project
15-
@params = params
15+
@flow_input = flow_input
1616
end
1717

1818
def execute
@@ -26,53 +26,29 @@ def execute
2626
error_code: :no_primary_runtime
2727
)
2828
end
29-
3029
transactional do |t|
31-
settings = []
32-
if params.key?(:flow_settings)
33-
params[:flow_settings].each do |graphql_setting|
34-
setting = FlowSetting.new(flow_setting_id: graphql_setting.flow_setting_identifier,
35-
object: graphql_setting.value)
36-
if setting.invalid?
37-
t.rollback_and_return! ServiceResponse.error(
38-
message: 'Invalid flow setting',
39-
error_code: :invalid_flow_setting,
40-
details: setting.errors
41-
)
42-
end
43-
44-
settings << setting
45-
end
46-
params[:flow_settings] = settings
47-
end
48-
49-
if params.key?(:starting_node_id)
50-
params[:starting_node] = create_node_function(params[:starting_node_id], params[:nodes], t)
51-
52-
params.delete(:starting_node_id)
53-
params.delete(:nodes)
54-
end
30+
flow_type = FlowType.find_by(id: flow_input.type.model_id, runtime_id: namespace_project.primary_runtime.id)
5531

56-
flow = Flow.create(project: namespace_project, **params)
57-
unless flow.persisted?
32+
if flow_type.nil?
5833
t.rollback_and_return! ServiceResponse.error(
59-
message: 'Failed to create flow',
60-
error_code: :invalid_flow,
61-
details: flow.errors
34+
message: 'Invalid flow type for the project runtime',
35+
error_code: :invalid_flow_type
6236
)
6337
end
6438

65-
res = Validation::ValidationService.new(current_authentication, flow).execute
66-
67-
if res.error?
68-
t.rollback_and_return! ServiceResponse.error(
69-
message: 'Flow validation failed',
70-
error_code: :flow_validation_failed,
71-
details: res.payload
72-
)
73-
end
39+
flow = Flow.new(
40+
project: namespace_project,
41+
name: flow_input.name,
42+
flow_type: flow_type,
43+
input_type: flow_type.input_type,
44+
return_type: flow_type.return_type
45+
)
7446

75-
UpdateRuntimesForProjectJob.perform_later(namespace_project.id)
47+
UpdateService.new(
48+
current_authentication,
49+
flow,
50+
flow_input
51+
).update_flow(t)
7652

7753
AuditService.audit(
7854
:flow_created,
@@ -87,98 +63,6 @@ def execute
8763
ServiceResponse.success(message: 'Created new flow', payload: flow)
8864
end
8965
end
90-
91-
def create_node_function(node_function_id, input_nodes, t)
92-
node_function = input_nodes.find { |n| n.id == node_function_id }
93-
94-
runtime_function_definition = namespace_project.primary_runtime.runtime_function_definitions.find_by(
95-
id: node_function.runtime_function_id.model_id
96-
)
97-
if runtime_function_definition.nil?
98-
t.rollback_and_return! ServiceResponse.error(
99-
message: 'Invalid runtime function id',
100-
error_code: :invalid_runtime_function_id
101-
)
102-
end
103-
104-
params = []
105-
node_function.parameters.each do |parameter|
106-
runtime_parameter = runtime_function_definition.parameters.find_by(
107-
id: parameter.runtime_parameter_definition_id.model_id
108-
)
109-
if runtime_parameter.nil?
110-
t.rollback_and_return! ServiceResponse.error(
111-
message: 'Invalid runtime parameter id',
112-
error_code: :invalid_runtime_parameter_id
113-
)
114-
end
115-
116-
if parameter.value.literal_value.present?
117-
params << NodeParameter.create(
118-
runtime_parameter: runtime_parameter,
119-
literal_value: parameter.value.literal_value
120-
)
121-
next
122-
end
123-
if parameter.value.function_value.present?
124-
# A little bit hacky but okay
125-
params << NodeParameter.create(
126-
runtime_parameter: runtime_parameter,
127-
function_value: create_node_function(
128-
parameter.value.function_value.id,
129-
[parameter.value.function_value], t
130-
)
131-
)
132-
next
133-
end
134-
135-
# This will be broken, because we cant reference nodes that arent created yet
136-
# And we will need to put all parameter nodes inside the flowinput.nodes
137-
# So we can reference them here because we will not recursively search for them
138-
referenced_node = NodeFunction.joins(:runtime_function).find_by(
139-
id: parameter.value.reference_value.node_function_id.model_id,
140-
runtime_function_definitions: { runtime_id: namespace_project.primary_runtime.id }
141-
)
142-
143-
if referenced_node.nil?
144-
t.rollback_and_return! ServiceResponse.error(
145-
message: 'Referenced node function not found',
146-
error_code: :referenced_value_not_found
147-
)
148-
end
149-
150-
params << NodeParameter.create(
151-
runtime_parameter: runtime_parameter,
152-
reference_value: ReferenceValue.create(
153-
node_function: referenced_node,
154-
data_type_identifier: get_data_type_identifier(
155-
namespace_project.primary_runtime,
156-
parameter.value.reference_value.data_type_identifier,
157-
t
158-
),
159-
depth: parameter.value.reference_value.depth,
160-
node: parameter.value.reference_value.node,
161-
scope: parameter.value.reference_value.scope,
162-
reference_paths: parameter.value.reference_value.reference_paths.map do |path|
163-
ReferencePath.create(
164-
path: path.path,
165-
array_index: path.array_index
166-
)
167-
end
168-
)
169-
)
170-
end
171-
172-
next_node = if node_function.next_node_id.present?
173-
create_node_function(node_function.next_node_id, input_nodes, t)
174-
end
175-
176-
NodeFunction.create(
177-
next_node: next_node,
178-
runtime_function: runtime_function_definition,
179-
node_parameters: params
180-
)
181-
end
18266
end
18367
end
18468
end

0 commit comments

Comments
 (0)