From 7b71e91e4c92f3de981ecf46b634e2a4ab9710d8 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Fri, 24 Oct 2025 16:21:50 +0200 Subject: [PATCH 1/5] Add name field --- app/graphql/types/flow_type.rb | 3 ++- app/graphql/types/input/flow_input_type.rb | 2 ++ db/migrate/20250526124346_create_flows.rb | 4 ++++ db/structure.sql | 3 +++ docs/graphql/input_object/flowinput.md | 1 + docs/graphql/object/flow.md | 1 + spec/factories/flows.rb | 3 +++ .../mutation/namespace/projects/flows/create_mutation_spec.rb | 1 + 8 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/graphql/types/flow_type.rb b/app/graphql/types/flow_type.rb index 6b79e5f3..db2d53ff 100644 --- a/app/graphql/types/flow_type.rb +++ b/app/graphql/types/flow_type.rb @@ -6,7 +6,8 @@ class FlowType < Types::BaseObject authorize :read_flow - # field :name, String, null: false does exist in pictor but not in grpc + field :name, String, null: false, description: 'Name of the flow' + field :input_type, Types::DataTypeType, null: true, description: 'The input data type of the flow' diff --git a/app/graphql/types/input/flow_input_type.rb b/app/graphql/types/input/flow_input_type.rb index 78a46cc8..dda11ef3 100644 --- a/app/graphql/types/input/flow_input_type.rb +++ b/app/graphql/types/input/flow_input_type.rb @@ -5,6 +5,8 @@ module Input class FlowInputType < Types::BaseInputObject description 'Input type for creating or updating a flow' + argument :name, String, required: true, description: 'The name of the flow' + argument :settings, [Types::Input::FlowSettingInputType], required: false, description: 'The settings of the flow' argument :starting_node, Types::Input::NodeFunctionInputType, required: true, diff --git a/db/migrate/20250526124346_create_flows.rb b/db/migrate/20250526124346_create_flows.rb index 4501cf3c..cf018c29 100644 --- a/db/migrate/20250526124346_create_flows.rb +++ b/db/migrate/20250526124346_create_flows.rb @@ -59,6 +59,10 @@ def change t.references :starting_node, null: false, foreign_key: { to_table: :node_functions, on_delete: :restrict } + t.text :name, null: false + + t.index %i[name project_id], unique: true + t.timestamps_with_timezone end diff --git a/db/structure.sql b/db/structure.sql index c2cac7c4..3368734d 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -250,6 +250,7 @@ CREATE TABLE flows ( input_type_id bigint, return_type_id bigint, starting_node_id bigint NOT NULL, + name text NOT NULL, created_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL ); @@ -1099,6 +1100,8 @@ CREATE INDEX index_flows_on_flow_type_id ON flows USING btree (flow_type_id); CREATE INDEX index_flows_on_input_type_id ON flows USING btree (input_type_id); +CREATE UNIQUE INDEX index_flows_on_name_and_project_id ON flows USING btree (name, project_id); + CREATE INDEX index_flows_on_project_id ON flows USING btree (project_id); CREATE INDEX index_flows_on_return_type_id ON flows USING btree (return_type_id); diff --git a/docs/graphql/input_object/flowinput.md b/docs/graphql/input_object/flowinput.md index 2fdb7d83..c31f3398 100644 --- a/docs/graphql/input_object/flowinput.md +++ b/docs/graphql/input_object/flowinput.md @@ -8,6 +8,7 @@ Input type for creating or updating a flow | Name | Type | Description | |------|------|-------------| +| `name` | [`String!`](../scalar/string.md) | The name of the flow | | `settings` | [`[FlowSettingInput!]`](../input_object/flowsettinginput.md) | The settings of the flow | | `startingNode` | [`NodeFunctionInput!`](../input_object/nodefunctioninput.md) | The starting node of the flow | | `type` | [`FlowTypeID!`](../scalar/flowtypeid.md) | The identifier of the flow type | diff --git a/docs/graphql/object/flow.md b/docs/graphql/object/flow.md index ef01da7e..e496df00 100644 --- a/docs/graphql/object/flow.md +++ b/docs/graphql/object/flow.md @@ -11,6 +11,7 @@ Represents a flow | `createdAt` | [`Time!`](../scalar/time.md) | Time when this Flow was created | | `id` | [`FlowID!`](../scalar/flowid.md) | Global ID of this Flow | | `inputType` | [`DataType`](../object/datatype.md) | The input data type of the flow | +| `name` | [`String!`](../scalar/string.md) | Name of the flow | | `nodes` | [`NodeFunctionConnection!`](../object/nodefunctionconnection.md) | Nodes of the flow | | `returnType` | [`DataType`](../object/datatype.md) | The return data type of the flow | | `settings` | [`FlowSettingConnection!`](../object/flowsettingconnection.md) | The settings of the flow | diff --git a/spec/factories/flows.rb b/spec/factories/flows.rb index 799389ac..65b8ed02 100644 --- a/spec/factories/flows.rb +++ b/spec/factories/flows.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true FactoryBot.define do + sequence(:flow_name) { |n| "Flow#{n}" } + factory :flow do project factory: :namespace_project flow_type @@ -8,5 +10,6 @@ flow_settings { [] } input_type { nil } return_type { nil } + name { generate(:flow_name) } end end diff --git a/spec/requests/graphql/mutation/namespace/projects/flows/create_mutation_spec.rb b/spec/requests/graphql/mutation/namespace/projects/flows/create_mutation_spec.rb index c03591bd..2c15b982 100644 --- a/spec/requests/graphql/mutation/namespace/projects/flows/create_mutation_spec.rb +++ b/spec/requests/graphql/mutation/namespace/projects/flows/create_mutation_spec.rb @@ -57,6 +57,7 @@ { projectId: project.to_global_id.to_s, flow: { + name: generate(:flow_name), type: flow_type.to_global_id.to_s, settings: { flowSettingId: 'key', From b6cd85d5b8eb60c9dcb75592d866fdd5c075fdc0 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Sat, 1 Nov 2025 21:37:16 +0100 Subject: [PATCH 2/5] Fix missing flow name --- app/graphql/mutations/namespaces/projects/flows/create.rb | 3 ++- spec/services/namespaces/projects/flows/create_service_spec.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/graphql/mutations/namespaces/projects/flows/create.rb b/app/graphql/mutations/namespaces/projects/flows/create.rb index 5ce07a19..9e3eb04d 100644 --- a/app/graphql/mutations/namespaces/projects/flows/create.rb +++ b/app/graphql/mutations/namespaces/projects/flows/create.rb @@ -27,7 +27,8 @@ def resolve(project_id:, flow:, **_params) namespace_project: project, flow_type: flow_type, starting_node: flow.starting_node, - flow_settings: flow.settings || [] + flow_settings: flow.settings || [], + name: flow.name ).execute.to_mutation_response(success_key: :flow) end diff --git a/spec/services/namespaces/projects/flows/create_service_spec.rb b/spec/services/namespaces/projects/flows/create_service_spec.rb index 68cb79fe..6f66478c 100644 --- a/spec/services/namespaces/projects/flows/create_service_spec.rb +++ b/spec/services/namespaces/projects/flows/create_service_spec.rb @@ -13,7 +13,7 @@ create(:node_function, runtime_function: create(:runtime_function_definition, runtime: runtime)) end let(:params) do - { project: namespace_project, flow_type: create(:flow_type, runtime: runtime), starting_node: starting_node } + { project: namespace_project, name: generate(:flow_name), flow_type: create(:flow_type, runtime: runtime), starting_node: starting_node } end shared_examples 'does not create' do From 6ec056a004b4ea227fbc47a7adeaa258a45bbed2 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Mon, 3 Nov 2025 17:59:01 +0100 Subject: [PATCH 3/5] Run rubocop --- spec/services/namespaces/projects/flows/create_service_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/services/namespaces/projects/flows/create_service_spec.rb b/spec/services/namespaces/projects/flows/create_service_spec.rb index 6f66478c..a4cd747b 100644 --- a/spec/services/namespaces/projects/flows/create_service_spec.rb +++ b/spec/services/namespaces/projects/flows/create_service_spec.rb @@ -13,7 +13,8 @@ create(:node_function, runtime_function: create(:runtime_function_definition, runtime: runtime)) end let(:params) do - { project: namespace_project, name: generate(:flow_name), flow_type: create(:flow_type, runtime: runtime), starting_node: starting_node } + { project: namespace_project, name: generate(:flow_name), flow_type: create(:flow_type, runtime: runtime), + starting_node: starting_node } end shared_examples 'does not create' do From 70231d56eb75e11040ca2cc10c4fe764fb7dd883 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Mon, 3 Nov 2025 18:32:16 +0100 Subject: [PATCH 4/5] Add model validation for flow name --- app/models/flow.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/flow.rb b/app/models/flow.rb index c1012f3b..b1552797 100644 --- a/app/models/flow.rb +++ b/app/models/flow.rb @@ -9,6 +9,10 @@ class Flow < ApplicationRecord has_many :flow_settings, class_name: 'FlowSetting', inverse_of: :flow + validates :name, presence: true, + allow_blank: false, + uniqueness: { case_sensitive: false, scope: :project_id } + def to_grpc Tucana::Shared::ValidationFlow.new( flow_id: id, From 8821336ef8776a84b04f7da1b2529e5bf46258f9 Mon Sep 17 00:00:00 2001 From: Dario Pranjic Date: Mon, 3 Nov 2025 18:37:43 +0100 Subject: [PATCH 5/5] Add tests and fix formattation --- app/models/flow.rb | 4 ++-- spec/models/flow_spec.rb | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/flow.rb b/app/models/flow.rb index b1552797..6f7c5e9b 100644 --- a/app/models/flow.rb +++ b/app/models/flow.rb @@ -10,8 +10,8 @@ class Flow < ApplicationRecord has_many :flow_settings, class_name: 'FlowSetting', inverse_of: :flow validates :name, presence: true, - allow_blank: false, - uniqueness: { case_sensitive: false, scope: :project_id } + allow_blank: false, + uniqueness: { case_sensitive: false, scope: :project_id } def to_grpc Tucana::Shared::ValidationFlow.new( diff --git a/spec/models/flow_spec.rb b/spec/models/flow_spec.rb index 920d38a0..b14618f3 100644 --- a/spec/models/flow_spec.rb +++ b/spec/models/flow_spec.rb @@ -15,6 +15,11 @@ it { is_expected.to have_many(:flow_settings) } end + describe 'validations' do + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_uniqueness_of(:name).case_insensitive.scoped_to(:project_id) } + end + describe '#to_grpc' do let(:flow) do create(