From 90adc8ef9afaa1403fafc50d213d613fa774fa6f Mon Sep 17 00:00:00 2001 From: Raphael Date: Wed, 8 Jul 2026 08:46:57 +0200 Subject: [PATCH] fix: ordered parameter on a function before processing --- app/models/function_definition.rb | 11 +++- app/models/node_function.rb | 2 +- app/models/runtime_function_definition.rb | 6 +- .../projects/flows/update_service.rb | 3 +- .../projects/flows/validation_service.rb | 3 +- .../velorum/generation_flow_serializer.rb | 2 +- spec/models/function_definition_spec.rb | 33 +++++++++++ .../runtime_function_definition_spec.rb | 10 ++++ .../projects/flows/validation_service_spec.rb | 56 +++++++++++++++++++ 9 files changed, 120 insertions(+), 6 deletions(-) diff --git a/app/models/function_definition.rb b/app/models/function_definition.rb index 0fc59cac2..138924b85 100644 --- a/app/models/function_definition.rb +++ b/app/models/function_definition.rb @@ -27,7 +27,7 @@ class FunctionDefinition < ApplicationRecord def to_grpc Tucana::Shared::FunctionDefinition.new( runtime_name: identifier, - parameter_definitions: parameter_definitions.map(&:to_grpc), + parameter_definitions: ordered_parameter_definitions.map(&:to_grpc), signature: runtime_function_definition.signature, throws_error: runtime_function_definition.throws_error, name: names.map(&:to_grpc), @@ -44,4 +44,13 @@ def to_grpc design: design ) end + + def ordered_parameter_definitions + parameter_definitions.sort_by do |definition| + [ + definition.runtime_parameter_definition.id, + definition.id + ] + end + end end diff --git a/app/models/node_function.rb b/app/models/node_function.rb index e2dee4ee0..b96677ff5 100644 --- a/app/models/node_function.rb +++ b/app/models/node_function.rb @@ -63,7 +63,7 @@ def ordered_parameters ) .where(fd[:id].eq(function_definition_id)) .where(np[:node_function_id].eq(id)) - .order(rpd[:id].asc) + .order(rpd[:id].asc, pd[:id].asc, np[:id].asc) .select(np[Arel.star]) end end diff --git a/app/models/runtime_function_definition.rb b/app/models/runtime_function_definition.rb index b4b9dfe09..bcd977a96 100644 --- a/app/models/runtime_function_definition.rb +++ b/app/models/runtime_function_definition.rb @@ -45,7 +45,7 @@ def parsed_version def to_grpc Tucana::Shared::RuntimeFunctionDefinition.new( runtime_name: runtime_name, - runtime_parameter_definitions: parameters.map(&:to_grpc), + runtime_parameter_definitions: ordered_parameters.map(&:to_grpc), signature: signature, throws_error: throws_error, name: names.map(&:to_grpc), @@ -61,4 +61,8 @@ def to_grpc design: design ) end + + def ordered_parameters + parameters.sort_by(&:id) + end end diff --git a/app/services/namespaces/projects/flows/update_service.rb b/app/services/namespaces/projects/flows/update_service.rb index 639b9b656..c8ae06191 100644 --- a/app/services/namespaces/projects/flows/update_service.rb +++ b/app/services/namespaces/projects/flows/update_service.rb @@ -169,10 +169,11 @@ def update_next_node(t, current_node, current_node_input, all_nodes) def update_node_parameters(t, current_node, current_node_input, all_nodes) db_parameters = current_node.node_parameters.first(current_node_input.parameters.count) + parameter_definitions = current_node.function_definition.ordered_parameter_definitions current_node_input.parameters.each_with_index do |parameter, index| db_parameters[index] ||= current_node.node_parameters.build - parameter_definition = current_node.function_definition.parameter_definitions[index] + parameter_definition = parameter_definitions[index] if parameter_definition.nil? t.rollback_and_return! ServiceResponse.error( message: 'Invalid parameter index', diff --git a/app/services/namespaces/projects/flows/validation_service.rb b/app/services/namespaces/projects/flows/validation_service.rb index e46f65895..03b83becb 100644 --- a/app/services/namespaces/projects/flows/validation_service.rb +++ b/app/services/namespaces/projects/flows/validation_service.rb @@ -13,7 +13,8 @@ def initialize(flow) def execute function_definitions = FunctionDefinition .by_node_function(flow.node_functions) - .preload(:runtime_function_definition) + .preload(:runtime_function_definition, + parameter_definitions: :runtime_parameter_definition) data_types = DataType.where(runtime: flow.project.primary_runtime) result = Triangulum::Validation.new( diff --git a/app/services/velorum/generation_flow_serializer.rb b/app/services/velorum/generation_flow_serializer.rb index 0196f45cd..95075063d 100644 --- a/app/services/velorum/generation_flow_serializer.rb +++ b/app/services/velorum/generation_flow_serializer.rb @@ -286,7 +286,7 @@ def parameter_definition_for(function_definition, index) def ordered_parameter_definitions(function_definition) function_definition .parameter_definitions - .sort_by { |definition| definition.runtime_parameter_definition&.id || definition.id } + .sort_by { |definition| definition.runtime_parameter_definition.id } end def flow_type_setting_for(flow_type, setting, index) diff --git a/spec/models/function_definition_spec.rb b/spec/models/function_definition_spec.rb index 6808b7a41..9705ea237 100644 --- a/spec/models/function_definition_spec.rb +++ b/spec/models/function_definition_spec.rb @@ -20,4 +20,37 @@ it { is_expected.to have_many(:aliases).class_name('Translation') } it { is_expected.to have_many(:display_messages).class_name('Translation') } end + + describe '#ordered_parameter_definitions' do + let(:runtime_function_definition) { create(:runtime_function_definition) } + let(:function_definition) do + create(:function_definition, runtime_function_definition: runtime_function_definition) + end + + it 'orders parameter definitions by runtime parameter definition order' do + first_runtime_parameter = create( + :runtime_parameter_definition, + runtime_function_definition: runtime_function_definition, + runtime_name: 'first' + ) + second_runtime_parameter = create( + :runtime_parameter_definition, + runtime_function_definition: runtime_function_definition, + runtime_name: 'second' + ) + + second_parameter = create( + :parameter_definition, + function_definition: function_definition, + runtime_parameter_definition: second_runtime_parameter + ) + first_parameter = create( + :parameter_definition, + function_definition: function_definition, + runtime_parameter_definition: first_runtime_parameter + ) + + expect(function_definition.ordered_parameter_definitions).to eq([first_parameter, second_parameter]) + end + end end diff --git a/spec/models/runtime_function_definition_spec.rb b/spec/models/runtime_function_definition_spec.rb index 55101486c..d4fbc062e 100644 --- a/spec/models/runtime_function_definition_spec.rb +++ b/spec/models/runtime_function_definition_spec.rb @@ -103,4 +103,14 @@ ) end end + + describe '#ordered_parameters' do + it 'orders parameters by persisted order' do + first_parameter = create(:runtime_parameter_definition, runtime_function_definition: function) + second_parameter = create(:runtime_parameter_definition, runtime_function_definition: function) + + expect(function.parameters.to_a).to include(first_parameter, second_parameter) + expect(function.ordered_parameters).to eq([first_parameter, second_parameter]) + end + end end diff --git a/spec/services/namespaces/projects/flows/validation_service_spec.rb b/spec/services/namespaces/projects/flows/validation_service_spec.rb index e0e9e4852..c54bb11c4 100644 --- a/spec/services/namespaces/projects/flows/validation_service_spec.rb +++ b/spec/services/namespaces/projects/flows/validation_service_spec.rb @@ -95,4 +95,60 @@ expect(flow.reload.validation_status).to eq('valid') end end + + context 'with unordered parameter records' do + let(:runtime_function_definition) do + create(:runtime_function_definition, runtime: runtime, signature: '(first: string, second: string): void') + end + + let(:function_definition) do + create(:function_definition, runtime_function_definition: runtime_function_definition).tap do |fd| + first_runtime_parameter = create( + :runtime_parameter_definition, + runtime_function_definition: runtime_function_definition, + runtime_name: 'first' + ) + second_runtime_parameter = create( + :runtime_parameter_definition, + runtime_function_definition: runtime_function_definition, + runtime_name: 'second' + ) + + create(:parameter_definition, runtime_parameter_definition: second_runtime_parameter, function_definition: fd) + create(:parameter_definition, runtime_parameter_definition: first_runtime_parameter, function_definition: fd) + end + end + + let(:node_function) do + create(:node_function, function_definition: function_definition).tap do |nf| + create( + :node_parameter, + parameter_definition: function_definition.ordered_parameter_definitions.second, + node_function: nf, + literal_value: '2' + ) + create( + :node_parameter, + parameter_definition: function_definition.ordered_parameter_definitions.first, + node_function: nf, + literal_value: '1' + ) + end + end + + it 'passes parameters to triangulum in runtime definition order' do + allow(UpdateRuntimesForProjectJob).to receive(:perform_later) + + result = Triangulum::Validation::Result.new(valid?: true, return_type: nil, diagnostics: []) + validation = instance_double(Triangulum::Validation, validate: result) + allow(Triangulum::Validation).to receive(:new).and_return(validation) + + service.execute + + expect(Triangulum::Validation).to have_received(:new) do |grpc_flow, grpc_function_definitions, _data_types| + expect(grpc_flow.node_functions.first.parameters.map(&:runtime_parameter_id)).to eq(%w[first second]) + expect(grpc_function_definitions.first.parameter_definitions.map(&:runtime_name)).to eq(%w[first second]) + end + end + end end