diff --git a/app/grpc/flow_handler.rb b/app/grpc/flow_handler.rb index 4861a86d..40abee55 100644 --- a/app/grpc/flow_handler.rb +++ b/app/grpc/flow_handler.rb @@ -12,6 +12,7 @@ def self.update_runtime(runtime) :namespace_project, module_configurations: { module_configuration_definition: :runtime_module } ) + runtime_modules = runtime.runtime_modules.includes(:module_configuration_definitions) flows = [] assignments.each do |assignment| @@ -29,7 +30,7 @@ def self.update_runtime(runtime) runtime.id ) - grouped_module_configurations(assignments).each do |module_configuration| + grouped_module_configurations(assignments, runtime_modules).each do |module_configuration| send_update( Tucana::Sagittarius::FlowResponse.new( module_configurations: module_configuration @@ -48,14 +49,18 @@ def self.update_started(runtime_id) update_runtime(runtime) end - def self.grouped_module_configurations(assignments) + def self.grouped_module_configurations(assignments, runtime_modules) grouped_entries = assignments.flat_map do |assignment| - assignment.module_configurations.map do |configuration| - [ - configuration.module_configuration_definition.runtime_module.identifier, - assignment, - configuration - ] + saved_configurations = assignment.module_configurations.index_by(&:module_configuration_definition_id) + + runtime_modules.flat_map do |runtime_module| + runtime_module.module_configuration_definitions.map do |definition| + [ + runtime_module.identifier, + assignment, + saved_configurations[definition.id] || definition + ] + end end end.group_by(&:first) @@ -81,8 +86,20 @@ def self.grouped_project_configurations(entries) def self.grpc_module_configurations(entries) entries.map(&:last) - .sort_by { |configuration| configuration.module_configuration_definition.identifier } - .map(&:to_grpc) + .sort_by { |configuration| module_configuration_identifier(configuration) } + .map { |configuration| module_configuration_to_grpc(configuration) } + end + + def self.module_configuration_identifier(configuration) + return configuration.identifier if configuration.is_a?(ModuleConfigurationDefinition) + + configuration.module_configuration_definition.identifier + end + + def self.module_configuration_to_grpc(configuration) + return configuration.to_default_grpc if configuration.is_a?(ModuleConfigurationDefinition) + + configuration.to_grpc end def self.encoders = { update: ->(grpc_object) { Tucana::Sagittarius::FlowResponse.encode(grpc_object) } } diff --git a/app/models/module_configuration_definition.rb b/app/models/module_configuration_definition.rb index cf6b9a62..56675a16 100644 --- a/app/models/module_configuration_definition.rb +++ b/app/models/module_configuration_definition.rb @@ -20,4 +20,11 @@ class ModuleConfigurationDefinition < ApplicationRecord validates :type, presence: true, length: { maximum: 8192 } validates :optional, inclusion: { in: [true, false] } validates :hidden, inclusion: { in: [true, false] } + + def to_default_grpc + Tucana::Shared::ModuleConfiguration.new( + identifier: identifier, + value: Tucana::Shared::Value.from_ruby(default_value) + ) + end end diff --git a/spec/grpc/flow_handler_spec.rb b/spec/grpc/flow_handler_spec.rb new file mode 100644 index 00000000..5f28565b --- /dev/null +++ b/spec/grpc/flow_handler_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe FlowHandler do + describe '.grouped_module_configurations' do + let(:runtime) { create(:runtime) } + let(:project) { create(:namespace_project) } + let(:assignment) do + create(:namespace_project_runtime_assignment, + namespace_project: project, + runtime: runtime, + compatible: true) + end + let(:runtime_module) { create(:runtime_module, runtime: runtime, identifier: 'example-action') } + let!(:saved_definition) do + create(:module_configuration_definition, + runtime_module: runtime_module, + identifier: 'EXAMPLE_CONFIG', + default_value: 'default') + end + let(:default_definition) do + create(:module_configuration_definition, + runtime_module: runtime_module, + identifier: 'SECOND_CONFIG', + default_value: 'second-default') + end + + before do + create(:module_configuration, + namespace_project_runtime_assignment: assignment, + module_configuration_definition: saved_definition, + value: 'saved') + end + + it 'uses saved values and falls back to definition defaults for missing project values' do + default_definition + + module_configurations = described_class.grouped_module_configurations([assignment], [runtime_module]) + + expect(module_configurations.length).to eq(1) + expect(module_configurations.first.module_identifier).to eq('example-action') + + project_configurations = module_configurations.first.module_configurations.sole + expect(project_configurations.project_id).to eq(project.id) + expect( + project_configurations.module_configurations.map do |configuration| + [configuration.identifier, configuration.value.to_ruby(true)] + end + ).to eq( + [ + %w[EXAMPLE_CONFIG saved], + %w[SECOND_CONFIG second-default] + ] + ) + end + end +end diff --git a/spec/models/module_configuration_definition_spec.rb b/spec/models/module_configuration_definition_spec.rb index af9584f5..6d4ab063 100644 --- a/spec/models/module_configuration_definition_spec.rb +++ b/spec/models/module_configuration_definition_spec.rb @@ -29,4 +29,15 @@ it { is_expected.to allow_values(true, false).for(:optional) } it { is_expected.to allow_values(true, false).for(:hidden) } end + + describe '#to_default_grpc' do + it 'serializes the identifier and default value as a module configuration' do + configuration_definition.update!(identifier: 'apiKey', default_value: 'default-secret') + + grpc_configuration = configuration_definition.to_default_grpc + + expect(grpc_configuration.identifier).to eq('apiKey') + expect(grpc_configuration.value.to_ruby(true)).to eq('default-secret') + end + end end diff --git a/spec/services/runtimes/grpc/modules/update_service_spec.rb b/spec/services/runtimes/grpc/modules/update_service_spec.rb new file mode 100644 index 00000000..8eb5f3dc --- /dev/null +++ b/spec/services/runtimes/grpc/modules/update_service_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Runtimes::Grpc::Modules::UpdateService do + describe '#execute' do + let(:runtime) { create(:runtime) } + + before do + allow(UpdateRuntimeCompatibilityJob).to receive(:perform_later) + end + + it 'schedules runtime compatibility updates' do + response = described_class.new(runtime, []).execute + + expect(response).to be_success + expect(UpdateRuntimeCompatibilityJob).to have_received(:perform_later).with({ runtime_id: runtime.id }) + end + end +end