diff --git a/app/graphql/resolvers/work_items/widgets/status_resolver.rb b/app/graphql/resolvers/work_items/widgets/status_resolver.rb deleted file mode 100644 index 80ee6fe3c094081d32a854c4fe20adfa0754dd78..0000000000000000000000000000000000000000 --- a/app/graphql/resolvers/work_items/widgets/status_resolver.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - module WorkItems - module Widgets - class StatusResolver < BaseResolver - type ::Types::WorkItems::Widgets::StatusType.connection_type, null: true - - def resolve - [] - end - end - end - end -end - -Resolvers::WorkItems::Widgets::StatusResolver.prepend_mod diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index acd4f9351dcaa535bdd3bc69ffbc97e6db826dd5..68ceae4efa43a7861b24f6bd2aa2f8973062e221 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -347,10 +347,6 @@ class GroupType < NamespaceType method: :linked_to_subscription?, description: 'Indicates if group is linked to a subscription.' - field :allowed_statuses, Types::WorkItems::Widgets::StatusType.connection_type, - null: true, description: 'Allowed statuses for the group.', - experiment: { milestone: '17.8' }, resolver: Resolvers::WorkItems::Widgets::StatusResolver - def label(title:) BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args| LabelsFinder diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb index 3a012d4892f6b54937e093bf936f7961d72e3d60..3ac3607fcc86387c303fd4ca6a1f2e1c1b2ee8b3 100644 --- a/app/graphql/types/namespace_type.rb +++ b/app/graphql/types/namespace_type.rb @@ -133,10 +133,6 @@ class NamespaceType < BaseObject calls_gitaly: true, description: 'Work item description templates available to the namespace.' - field :allowed_statuses, Types::WorkItems::Widgets::StatusType.connection_type, - null: true, description: 'Allowed statuses for the namespace.', - experiment: { milestone: '17.8' }, resolver: Resolvers::WorkItems::Widgets::StatusResolver - markdown_field :description_html, null: true def achievements_path diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 90447cd389652350865a7030c1b0273ebbbef0bf..e1d560ff7059963909dd8cd90c42df5198a23dbe 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -805,10 +805,6 @@ class ProjectType < BaseObject connection: true, description: "List of the project's Pages Deployments." - field :allowed_statuses, Types::WorkItems::Widgets::StatusType.connection_type, - null: true, description: 'Allowed statuses for the project.', - experiment: { milestone: '17.8' }, resolver: Resolvers::WorkItems::Widgets::StatusResolver - field :pages_force_https, GraphQL::Types::Boolean, null: false, description: "Project's Pages site redirects unsecured connections to HTTPS." diff --git a/app/graphql/types/work_items/widget_definition_interface.rb b/app/graphql/types/work_items/widget_definition_interface.rb index d5a45d4aa0342ca964a5e6ad5bb437eac3602bf0..96ae5acb841e9c3f722528e6731f8df2aa36460c 100644 --- a/app/graphql/types/work_items/widget_definition_interface.rb +++ b/app/graphql/types/work_items/widget_definition_interface.rb @@ -14,14 +14,12 @@ module WidgetDefinitionInterface ORPHAN_TYPES = [ ::Types::WorkItems::WidgetDefinitions::AssigneesType, ::Types::WorkItems::WidgetDefinitions::GenericType, - ::Types::WorkItems::WidgetDefinitions::HierarchyType, - ::Types::WorkItems::WidgetDefinitions::StatusType + ::Types::WorkItems::WidgetDefinitions::HierarchyType ].freeze TYPE_MAPPING = { ::WorkItems::Widgets::Assignees => ::Types::WorkItems::WidgetDefinitions::AssigneesType, - ::WorkItems::Widgets::Hierarchy => ::Types::WorkItems::WidgetDefinitions::HierarchyType, - ::WorkItems::Widgets::Status => ::Types::WorkItems::WidgetDefinitions::StatusType + ::WorkItems::Widgets::Hierarchy => ::Types::WorkItems::WidgetDefinitions::HierarchyType }.freeze def self.ce_orphan_types diff --git a/app/graphql/types/work_items/widget_interface.rb b/app/graphql/types/work_items/widget_interface.rb index aadb28270e19a0ed914f208d67119fc3e2cc52bc..2a51a81e02ea9c358a21c220ad3a318e5fcf7dd0 100644 --- a/app/graphql/types/work_items/widget_interface.rb +++ b/app/graphql/types/work_items/widget_interface.rb @@ -33,7 +33,6 @@ module WidgetInterface ::WorkItems::Widgets::Development => ::Types::WorkItems::Widgets::DevelopmentType, ::WorkItems::Widgets::CrmContacts => ::Types::WorkItems::Widgets::CrmContactsType, ::WorkItems::Widgets::EmailParticipants => ::Types::WorkItems::Widgets::EmailParticipantsType, - ::WorkItems::Widgets::Status => ::Types::WorkItems::Widgets::StatusType, ::WorkItems::Widgets::LinkedResources => ::Types::WorkItems::Widgets::LinkedResourcesType, ::WorkItems::Widgets::ErrorTracking => ::Types::WorkItems::Widgets::ErrorTrackingType }.freeze diff --git a/app/models/work_items/widget_definition.rb b/app/models/work_items/widget_definition.rb index feee08572518c9ba065d8e523b710fda965dfbba..1974b30f40ae4a10502d7d0fa396260ebf272fc3 100644 --- a/app/models/work_items/widget_definition.rb +++ b/app/models/work_items/widget_definition.rb @@ -42,7 +42,7 @@ class WidgetDefinition < ApplicationRecord development: 23, crm_contacts: 24, email_participants: 25, - status: 26, + status: 26, # EE-only linked_resources: 27, custom_fields: 28, # EE-only error_tracking: 29, diff --git a/ee/app/graphql/ee/resolvers/work_items/widgets/status_resolver.rb b/ee/app/graphql/ee/resolvers/work_items/widgets/status_resolver.rb deleted file mode 100644 index 314b2ebfdfe68a8b3f4ccb4929e6b35deed550a9..0000000000000000000000000000000000000000 --- a/ee/app/graphql/ee/resolvers/work_items/widgets/status_resolver.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module EE - module Resolvers - module WorkItems - module Widgets - module StatusResolver - extend ::Gitlab::Utils::Override - - override :resolve - def resolve - return [] unless work_item_status_feature_available? - - # As part of iteration 1, we only support system defined statuses - # Custom lifecycle based on namespace will be supported in iteration 2 - lifecycle_for(object.work_item_type)&.statuses || [] - end - - private - - def work_item_status_feature_available? - root_ancestor&.try(:work_item_status_feature_available?) - end - - def root_ancestor - context[:resource_parent]&.root_ancestor - end - - def lifecycle_for(work_item_type) - base_type = work_item_type.base_type.to_sym - ::WorkItems::Statuses::SystemDefined::Lifecycle.of_work_item_base_type(base_type) - end - end - end - end - end -end diff --git a/ee/app/graphql/ee/types/group_type.rb b/ee/app/graphql/ee/types/group_type.rb index 14ecdd3f2136b664fc43f12859d85ca6b63d5b1d..833cf3c28e45a380d43358db043b42df8b26d18e 100644 --- a/ee/app/graphql/ee/types/group_type.rb +++ b/ee/app/graphql/ee/types/group_type.rb @@ -360,6 +360,10 @@ module GroupType resolver: ::Resolvers::Issuables::CustomFieldResolver, experiment: { milestone: '17.6' } + field :allowed_statuses, ::Types::WorkItems::Widgets::StatusType.connection_type, + null: true, description: 'Allowed statuses for the group.', + experiment: { milestone: '17.8' }, resolver: ::Resolvers::WorkItems::Widgets::StatusResolver + def epics_enabled object.licensed_feature_available?(:epics) end diff --git a/ee/app/graphql/ee/types/namespace_type.rb b/ee/app/graphql/ee/types/namespace_type.rb index ab6372766814bbd7d86c1811e29cf8a1a09293d5..23137130b395a8a550b6f2c533a2db0971d177d9 100644 --- a/ee/app/graphql/ee/types/namespace_type.rb +++ b/ee/app/graphql/ee/types/namespace_type.rb @@ -164,6 +164,10 @@ module NamespaceType resolver: ::Resolvers::Issuables::CustomFieldsResolver, experiment: { milestone: '17.10' } + field :allowed_statuses, ::Types::WorkItems::Widgets::StatusType.connection_type, + null: true, description: 'Allowed statuses for the namespace.', + experiment: { milestone: '17.8' }, resolver: ::Resolvers::WorkItems::Widgets::StatusResolver + def product_analytics_stored_events_limit object.root_ancestor.product_analytics_stored_events_limit end diff --git a/ee/app/graphql/ee/types/project_type.rb b/ee/app/graphql/ee/types/project_type.rb index fbc26dab8d8a48d310a45517d4912c73fc95eece..2b4f4767467b8f8914c4026e3a68f943157dc567 100644 --- a/ee/app/graphql/ee/types/project_type.rb +++ b/ee/app/graphql/ee/types/project_type.rb @@ -589,6 +589,10 @@ module ProjectType field :target_branch_rules, ::Types::Projects::TargetBranchRuleType.connection_type, null: true, description: 'Target branch rules of the project.' + + field :allowed_statuses, ::Types::WorkItems::Widgets::StatusType.connection_type, + null: true, description: 'Allowed statuses for the project.', + experiment: { milestone: '17.8' }, resolver: ::Resolvers::WorkItems::Widgets::StatusResolver end def tracking_key diff --git a/ee/app/graphql/ee/types/work_items/widget_definition_interface.rb b/ee/app/graphql/ee/types/work_items/widget_definition_interface.rb index 666c8ee80f9b65fa185d6885bf422deb123d7021..eaae46d29f7eb3e4e63f14972c0615fa4a283d7d 100644 --- a/ee/app/graphql/ee/types/work_items/widget_definition_interface.rb +++ b/ee/app/graphql/ee/types/work_items/widget_definition_interface.rb @@ -9,13 +9,15 @@ module WidgetDefinitionInterface EE_ORPHAN_TYPES = [ ::Types::WorkItems::WidgetDefinitions::LabelsType, ::Types::WorkItems::WidgetDefinitions::WeightType, - ::Types::WorkItems::WidgetDefinitions::CustomFieldsType + ::Types::WorkItems::WidgetDefinitions::CustomFieldsType, + ::Types::WorkItems::WidgetDefinitions::StatusType ].freeze EE_TYPE_MAPPING = { ::WorkItems::Widgets::Labels => ::Types::WorkItems::WidgetDefinitions::LabelsType, ::WorkItems::Widgets::Weight => ::Types::WorkItems::WidgetDefinitions::WeightType, - ::WorkItems::Widgets::CustomFields => ::Types::WorkItems::WidgetDefinitions::CustomFieldsType + ::WorkItems::Widgets::CustomFields => ::Types::WorkItems::WidgetDefinitions::CustomFieldsType, + ::WorkItems::Widgets::Status => ::Types::WorkItems::WidgetDefinitions::StatusType }.freeze class_methods do diff --git a/ee/app/graphql/ee/types/work_items/widget_interface.rb b/ee/app/graphql/ee/types/work_items/widget_interface.rb index 046194ba72f10cd2e1722070428aab432d7caaf0..45c54afacbe9497702506bbec6703349d14d7f0c 100644 --- a/ee/app/graphql/ee/types/work_items/widget_interface.rb +++ b/ee/app/graphql/ee/types/work_items/widget_interface.rb @@ -26,7 +26,8 @@ def type_mappings ::WorkItems::Widgets::TestReports => ::Types::WorkItems::Widgets::TestReportsType, ::WorkItems::Widgets::Color => ::Types::WorkItems::Widgets::ColorType, ::WorkItems::Widgets::CustomFields => ::Types::WorkItems::Widgets::CustomFieldsType, - ::WorkItems::Widgets::Vulnerabilities => ::Types::WorkItems::Widgets::VulnerabilitiesType + ::WorkItems::Widgets::Vulnerabilities => ::Types::WorkItems::Widgets::VulnerabilitiesType, + ::WorkItems::Widgets::Status => ::Types::WorkItems::Widgets::StatusType }.freeze orphan_types(*type_mappings.values) diff --git a/ee/app/graphql/resolvers/work_items/widgets/status_resolver.rb b/ee/app/graphql/resolvers/work_items/widgets/status_resolver.rb new file mode 100644 index 0000000000000000000000000000000000000000..c2cef46cf69c63fdcfbe144da035ae88d15e3934 --- /dev/null +++ b/ee/app/graphql/resolvers/work_items/widgets/status_resolver.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Resolvers + module WorkItems + module Widgets + class StatusResolver < BaseResolver + type ::Types::WorkItems::Widgets::StatusType.connection_type, null: true + + def resolve + return [] unless work_item_status_feature_available? + + # As part of iteration 1, we only support system defined statuses + # Custom lifecycle based on namespace will be supported in iteration 2 + lifecycle_for(object.work_item_type)&.statuses || [] + end + + private + + def work_item_status_feature_available? + root_ancestor&.try(:work_item_status_feature_available?) + end + + def root_ancestor + context[:resource_parent]&.root_ancestor + end + + def lifecycle_for(work_item_type) + base_type = work_item_type.base_type.to_sym + ::WorkItems::Statuses::SystemDefined::Lifecycle.of_work_item_base_type(base_type) + end + end + end + end +end diff --git a/app/graphql/types/work_items/widget_definitions/status_type.rb b/ee/app/graphql/types/work_items/widget_definitions/status_type.rb similarity index 91% rename from app/graphql/types/work_items/widget_definitions/status_type.rb rename to ee/app/graphql/types/work_items/widget_definitions/status_type.rb index fb43ad4b0b69b061ded1f0b8d0593bc02d67a562..158917ecf42f1de7b23eb27b2c51a41071096a5d 100644 --- a/app/graphql/types/work_items/widget_definitions/status_type.rb +++ b/ee/app/graphql/types/work_items/widget_definitions/status_type.rb @@ -13,7 +13,7 @@ class StatusType < BaseObject field :allowed_statuses, ::Types::WorkItems::Widgets::StatusType.connection_type, null: true, experiment: { milestone: '17.8' }, description: 'Allowed statuses for the work item type.', - resolver: Resolvers::WorkItems::Widgets::StatusResolver + resolver: ::Resolvers::WorkItems::Widgets::StatusResolver end # rubocop:enable Graphql/AuthorizeTypes end diff --git a/app/graphql/types/work_items/widgets/status_type.rb b/ee/app/graphql/types/work_items/widgets/status_type.rb similarity index 100% rename from app/graphql/types/work_items/widgets/status_type.rb rename to ee/app/graphql/types/work_items/widgets/status_type.rb diff --git a/app/models/work_items/widgets/status.rb b/ee/app/models/work_items/widgets/status.rb similarity index 100% rename from app/models/work_items/widgets/status.rb rename to ee/app/models/work_items/widgets/status.rb diff --git a/app/services/work_items/data_sync/widgets/status.rb b/ee/app/services/work_items/data_sync/widgets/status.rb similarity index 100% rename from app/services/work_items/data_sync/widgets/status.rb rename to ee/app/services/work_items/data_sync/widgets/status.rb diff --git a/ee/spec/graphql/ee/types/group_type_spec.rb b/ee/spec/graphql/ee/types/group_type_spec.rb index c300a39034b3abd7a82c1854a39059a91ecee1e9..40ea5d84e3634189367ca8acafd50ceeef4c716c 100644 --- a/ee/spec/graphql/ee/types/group_type_spec.rb +++ b/ee/spec/graphql/ee/types/group_type_spec.rb @@ -49,6 +49,7 @@ it { expect(described_class).to have_graphql_field(:dependencies) } it { expect(described_class).to have_graphql_field(:components) } it { expect(described_class).to have_graphql_field(:custom_fields) } + it { expect(described_class).to have_graphql_field(:allowed_statuses) } describe 'components' do let_it_be(:guest) { create(:user) } diff --git a/ee/spec/graphql/ee/types/namespace_type_spec.rb b/ee/spec/graphql/ee/types/namespace_type_spec.rb index 120de6f95200c91cd331a25ebb8537807da9055c..d21323a251e792d27d91fa12b521c8ae47f71405 100644 --- a/ee/spec/graphql/ee/types/namespace_type_spec.rb +++ b/ee/spec/graphql/ee/types/namespace_type_spec.rb @@ -24,6 +24,7 @@ product_analytics_stored_events_limit subscription_history custom_fields + allowed_statuses ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/ee/spec/graphql/types/project_type_spec.rb b/ee/spec/graphql/types/project_type_spec.rb index b2a37d4663aea82a7ba0c7fc52a928288b86224a..fa57cc41188063905c88f14057a1ebf8cabc4cf1 100644 --- a/ee/spec/graphql/types/project_type_spec.rb +++ b/ee/spec/graphql/types/project_type_spec.rb @@ -37,7 +37,7 @@ merge_trains pending_member_approvals observability_logs_links observability_metrics_links observability_traces_links dependencies security_exclusions security_exclusion compliance_standards_adherence target_branch_rules duo_workflow_status_check component_usages - vulnerability_archives + vulnerability_archives allowed_statuses ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/ee/spec/graphql/types/work_items/widget_definition_interface_spec.rb b/ee/spec/graphql/types/work_items/widget_definition_interface_spec.rb index d4d61493ed176ff73f3137c366ad938328457a8e..41a71858635727d967d558852a88bf3f89cca160 100644 --- a/ee/spec/graphql/types/work_items/widget_definition_interface_spec.rb +++ b/ee/spec/graphql/types/work_items/widget_definition_interface_spec.rb @@ -9,6 +9,7 @@ where(:widget_type, :widget_definition_type_class) do 'labels' | Types::WorkItems::WidgetDefinitions::LabelsType 'custom_fields' | Types::WorkItems::WidgetDefinitions::CustomFieldsType + 'status' | Types::WorkItems::WidgetDefinitions::StatusType end subject { described_class.resolve_type(object, {}) } diff --git a/ee/spec/graphql/types/work_items/widget_definitions/status_type_spec.rb b/ee/spec/graphql/types/work_items/widget_definitions/status_type_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..589fefd6ef12af69a346378f1842b598c0e3cf81 --- /dev/null +++ b/ee/spec/graphql/types/work_items/widget_definitions/status_type_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::WorkItems::WidgetDefinitions::StatusType, feature_category: :team_planning do + it 'exposes the expected fields' do + expected_fields = %i[type allowed_statuses] + + expect(described_class).to have_graphql_fields(*expected_fields) + end + + specify { expect(described_class.graphql_name).to eq('WorkItemWidgetDefinitionStatus') } +end diff --git a/ee/spec/graphql/types/work_items/widget_interface_spec.rb b/ee/spec/graphql/types/work_items/widget_interface_spec.rb index 4089a125cf99795a9e776758a9aacbfa63e07fb2..a69d12dcaeb97474df75ba04e74645ad6538648e 100644 --- a/ee/spec/graphql/types/work_items/widget_interface_spec.rb +++ b/ee/spec/graphql/types/work_items/widget_interface_spec.rb @@ -15,6 +15,7 @@ WorkItems::Widgets::RequirementLegacy | Types::WorkItems::Widgets::RequirementLegacyType WorkItems::Widgets::TestReports | Types::WorkItems::Widgets::TestReportsType WorkItems::Widgets::Vulnerabilities | Types::WorkItems::Widgets::VulnerabilitiesType + WorkItems::Widgets::Status | Types::WorkItems::Widgets::StatusType end with_them do diff --git a/ee/spec/requests/api/graphql/work_item_spec.rb b/ee/spec/requests/api/graphql/work_item_spec.rb index d260dc212377ed7844d18f8d8769cca59f9a26f3..b1d43e7688df4eff31121805728197db1cf2ad2a 100644 --- a/ee/spec/requests/api/graphql/work_item_spec.rb +++ b/ee/spec/requests/api/graphql/work_item_spec.rb @@ -323,6 +323,39 @@ end end + describe 'status widget' do + let_it_be(:task_work_item) { create(:work_item, :task, project: project) } + let_it_be(:global_id) { task_work_item.to_global_id } + let(:work_item_fields) do + <<~GRAPHQL + id + widgets { + type + ... on WorkItemWidgetStatus { + id + name + iconName + } + } + GRAPHQL + end + + it 'returns mock status data' do + post_graphql(query, current_user: current_user) + + expect(work_item_data).to include( + 'widgets' => array_including( + hash_including( + 'type' => 'STATUS', + 'id' => 'gid://gitlab/WorkItems::Widgets::Status/10', + 'name' => 'Status', + 'iconName' => 'status icon' + ) + ) + ) + end + end + describe 'health status widget' do let_it_be(:work_item) { create(:work_item, :epic, namespace: group) } let_it_be(:sub_issue) { create(:work_item, :issue, :closed, project: project, health_status: :on_track) } diff --git a/ee/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb b/ee/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb index f8d8c2476876bb8af482a9640b7dde0b0c50c344..44d9891ca1c88535a4ad0c326a73d757fbd2488d 100644 --- a/ee/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb +++ b/ee/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb @@ -58,4 +58,69 @@ base_widget_attributes.deep_merge(ee_attributes) end + + def widgets_for(work_item_type, resource_parent) + work_item_type.widget_classes(resource_parent).map do |widget| + base_attributes = { 'type' => widget.type.to_s.upcase } + + if widget == WorkItems::Widgets::Hierarchy + next hierarchy_widget_attributes(work_item_type, base_attributes, resource_parent) + end + + if widget == WorkItems::Widgets::Status + next status_widget_attributes(work_item_type, + base_attributes, resource_parent) + end + + next base_attributes unless widget_attributes[widget.type] + + base_attributes.merge(widget_attributes[widget.type]) + end + end + + def status_widget_attributes(_work_item_type, base_attributes, resource_parent) + unless resource_parent&.root_ancestor&.try(:work_item_status_feature_available?) + return base_attributes.merge({ 'allowedStatuses' => { 'nodes' => [] } }) + end + + statuses = [ + { + 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/1', + 'name' => 'To do', + 'iconName' => 'status-waiting', + 'color' => '#535158', + 'position' => 0 + }, + { + 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/2', + 'name' => 'In progress', + 'iconName' => 'status-running', + 'color' => '#0b5cad', + 'position' => 0 + }, + { + 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/3', + 'name' => 'Done', + 'iconName' => 'status-success', + 'color' => '#23663b', + 'position' => 0 + }, + { + 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/4', + 'name' => "Won't do", + 'iconName' => 'status-cancelled', + 'color' => '#ae1901', + 'position' => 0 + }, + { + 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/5', + 'name' => 'Duplicate', + 'iconName' => 'status-cancelled', + 'color' => '#ae1901', + 'position' => 10 + } + ] + + base_attributes.merge({ 'allowedStatuses' => { 'nodes' => statuses } }) + end end diff --git a/ee/spec/support/shared_examples/requests/api/graphql/work_item_type_list_ee_shared_examples.rb b/ee/spec/support/shared_examples/requests/api/graphql/work_item_type_list_ee_shared_examples.rb index 564924abddfe8a288331fac97ac93d92a212a7af..67636251d2632b0b58ce4176541260150d24d8b5 100644 --- a/ee/spec/support/shared_examples/requests/api/graphql/work_item_type_list_ee_shared_examples.rb +++ b/ee/spec/support/shared_examples/requests/api/graphql/work_item_type_list_ee_shared_examples.rb @@ -21,6 +21,27 @@ ) end + describe 'allowed statuses' do + include_context 'with work item types request context EE' + + it 'returns the allowed custom statuses' do + stub_licensed_features(work_item_custom_status: true) + post_graphql(query, current_user: current_user) + + work_item_types = graphql_data_at(parent_key, :workItemTypes, :nodes) + status_widgets = work_item_types.flat_map do |work_item_type| + work_item_type['widgetDefinitions'].select { |widget| widget['type'] == 'STATUS' } + end + + expect(status_widgets).to be_present + status_widgets.each do |widget| + expect(widget['allowedStatuses']).to be_present + expect(widget['allowedStatuses']['nodes']).to all(include('id', 'name', 'iconName', 'color', + 'position')) + end + end + end + describe 'licensed widgets' do before do stub_licensed_features(**feature_hash) diff --git a/spec/graphql/resolvers/work_items/widgets/status_resolver_spec.rb b/spec/graphql/resolvers/work_items/widgets/status_resolver_spec.rb deleted file mode 100644 index c08b99bf346a9df992743af4479089960d9b372c..0000000000000000000000000000000000000000 --- a/spec/graphql/resolvers/work_items/widgets/status_resolver_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Resolvers::WorkItems::Widgets::StatusResolver, feature_category: :team_planning do - include GraphqlHelpers - - let_it_be(:group) { create(:group) } - let_it_be(:project) { create(:project, group: group) } - let_it_be(:current_user) { create(:user) } - let_it_be(:task_type) { create(:work_item_type, :task) } - let_it_be(:widget_definition) do - create(:widget_definition, widget_type: :status, work_item_type: task_type, name: 'TesT Widget') - end - - shared_examples 'does not return system defined statuses' do - it 'returns an empty array' do - expect(resolve_statuses&.items).to eq([]) - end - end - - describe '#resolve' do - let(:resource_parent) { group } - - context 'with group' do - it_behaves_like 'does not return system defined statuses' - end - - context 'with project' do - let(:resource_parent) { project } - - it_behaves_like 'does not return system defined statuses' - end - - context 'with unsupported namespace' do - let(:resource_parent) { current_user.namespace } - - it_behaves_like 'does not return system defined statuses' - end - - context 'with work_item_status feature flag disabled' do - before do - stub_feature_flags(work_item_status: false) - end - - it_behaves_like 'does not return system defined statuses' - end - end - - def resolve_statuses(args = {}, context = { current_user: current_user, resource_parent: resource_parent }) - resolve(described_class, obj: widget_definition, args: args, ctx: context) - end -end diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb index 815edf2b3c4ce261ab6bb9f170c2b66ef3ce2da2..67fee7d5d2209904b529aae7621d409322872e47 100644 --- a/spec/graphql/types/group_type_spec.rb +++ b/spec/graphql/types/group_type_spec.rb @@ -29,7 +29,7 @@ contact_state_counts contacts work_item_types recent_issue_boards ci_variables releases environment_scopes work_items autocomplete_users lock_math_rendering_limits_enabled math_rendering_limits_enabled created_at updated_at - organization_edit_path is_linked_to_subscription allowed_statuses + organization_edit_path is_linked_to_subscription ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/namespace_type_spec.rb b/spec/graphql/types/namespace_type_spec.rb index daae713b235a9a5d1aaee91c5eff38a16e6c7158..1a1e37ec741c67c0442fbbacf2241b4286b1ec0d 100644 --- a/spec/graphql/types/namespace_type_spec.rb +++ b/spec/graphql/types/namespace_type_spec.rb @@ -12,7 +12,7 @@ id name path full_name full_path achievements_path description description_html visibility lfs_enabled request_access_enabled projects root_storage_statistics shared_runners_setting timelog_categories achievements work_item pages_deployments import_source_users work_item_types - sidebar work_item_description_templates allowed_statuses ci_cd_settings avatar_url + sidebar work_item_description_templates ci_cd_settings avatar_url ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 8dbf90a597c4095f14d9822910264a0babfb9fa4..3dd91dfde7bd7bbe50baed4a54217bb7e231235b 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -46,8 +46,8 @@ ci_cd_settings detailed_import_status value_streams ml_models allows_multiple_merge_request_assignees allows_multiple_merge_request_reviewers is_forked protectable_branches available_deploy_keys explore_catalog_path - container_protection_tag_rules allowed_statuses - pages_force_https pages_use_unique_domain ci_pipeline_creation_request ci_pipeline_creation_inputs + container_protection_tag_rules pages_force_https pages_use_unique_domain ci_pipeline_creation_request + ci_pipeline_creation_inputs ] expect(described_class).to include_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/work_items/widget_definition_interface_spec.rb b/spec/graphql/types/work_items/widget_definition_interface_spec.rb index a26ce71099ca93d27cb8984ea29d8d2718d524f7..a74754ec9922627b7815f129e566d9cd7aec8365 100644 --- a/spec/graphql/types/work_items/widget_definition_interface_spec.rb +++ b/spec/graphql/types/work_items/widget_definition_interface_spec.rb @@ -26,12 +26,6 @@ it { is_expected.to eq(Types::WorkItems::WidgetDefinitions::HierarchyType) } end - context 'for status widget' do - let(:object) { build(:widget_definition, widget_type: 'status') } - - it { is_expected.to eq(Types::WorkItems::WidgetDefinitions::StatusType) } - end - context 'for other widgets' do let(:object) { build(:widget_definition, widget_type: 'description') } diff --git a/spec/graphql/types/work_items/widget_interface_spec.rb b/spec/graphql/types/work_items/widget_interface_spec.rb index f14ce3193f5523b6e580d08b7fda25c194448d9b..9421dc451587c98194031c42411576338a37fe08 100644 --- a/spec/graphql/types/work_items/widget_interface_spec.rb +++ b/spec/graphql/types/work_items/widget_interface_spec.rb @@ -30,7 +30,6 @@ WorkItems::Widgets::Designs | Types::WorkItems::Widgets::DesignsType WorkItems::Widgets::CrmContacts | Types::WorkItems::Widgets::CrmContactsType WorkItems::Widgets::EmailParticipants | Types::WorkItems::Widgets::EmailParticipantsType - WorkItems::Widgets::Status | Types::WorkItems::Widgets::StatusType WorkItems::Widgets::ErrorTracking | Types::WorkItems::Widgets::ErrorTrackingType end diff --git a/spec/graphql/types/work_items/widgets/status_type_spec.rb b/spec/graphql/types/work_items/widgets/status_type_spec.rb deleted file mode 100644 index fae4ffb4f149b7968bd688953d08bc3b2747816f..0000000000000000000000000000000000000000 --- a/spec/graphql/types/work_items/widgets/status_type_spec.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Types::WorkItems::Widgets::StatusType, feature_category: :team_planning do - it 'exposes the expected fields' do - expected_fields = %i[id name icon_name color position] - - expected_fields.each do |field| - expect(described_class).to have_graphql_field(field) - end - end -end diff --git a/spec/models/work_items/widget_definition_spec.rb b/spec/models/work_items/widget_definition_spec.rb index 0c9dbc6c69433aae7cbad5053d282d43167b3861..1ce4563608917d5201de1ef077a778d1f59cc057 100644 --- a/spec/models/work_items/widget_definition_spec.rb +++ b/spec/models/work_items/widget_definition_spec.rb @@ -22,7 +22,6 @@ ::WorkItems::Widgets::Development, ::WorkItems::Widgets::CrmContacts, ::WorkItems::Widgets::EmailParticipants, - ::WorkItems::Widgets::Status, ::WorkItems::Widgets::ErrorTracking ] @@ -37,7 +36,8 @@ ::WorkItems::Widgets::TestReports, ::WorkItems::Widgets::Color, ::WorkItems::Widgets::CustomFields, - ::WorkItems::Widgets::Vulnerabilities + ::WorkItems::Widgets::Vulnerabilities, + ::WorkItems::Widgets::Status ] end diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb index da89701d6abc32270c37efe4fb35a384a96fbcf3..e90d9f8a9f016b35fb69c693e17ef1a3618e107c 100644 --- a/spec/requests/api/graphql/work_item_spec.rb +++ b/spec/requests/api/graphql/work_item_spec.rb @@ -1513,37 +1513,6 @@ def id_hash(object) end end - describe 'status widget' do - let_it_be(:task_work_item) { create(:work_item, :task, project: project) } - let_it_be(:global_id) { task_work_item.to_global_id } - let(:work_item_fields) do - <<~GRAPHQL - id - widgets { - type - ... on WorkItemWidgetStatus { - id - name - iconName - } - } - GRAPHQL - end - - it 'returns mock status data' do - expect(work_item_data).to include( - 'widgets' => array_including( - hash_including( - 'type' => 'STATUS', - 'id' => 'gid://gitlab/WorkItems::Widgets::Status/10', - 'name' => 'Status', - 'iconName' => 'status icon' - ) - ) - ) - end - end - context 'when an Issue Global ID is provided' do let(:global_id) { Issue.find(work_item.id).to_gid.to_s } diff --git a/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb b/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb index ec24d4314fd742f506885cc5d0e7bd51dbd89019..2bbbfb80509bd02e8b472abfa6b9c325a50de41a 100644 --- a/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/graphql/work_items/work_item_types_shared_context.rb @@ -19,11 +19,6 @@ nodes { id name } } } - ... on WorkItemWidgetDefinitionStatus { - allowedStatuses { - nodes { id name iconName color position } - } - } } supportedConversionTypes { id @@ -70,11 +65,6 @@ def widgets_for(work_item_type, resource_parent) next hierarchy_widget_attributes(work_item_type, base_attributes, resource_parent) end - if widget == WorkItems::Widgets::Status - next status_widget_attributes(work_item_type, - base_attributes, resource_parent) - end - next base_attributes unless widget_attributes[widget.type] base_attributes.merge(widget_attributes[widget.type]) @@ -95,50 +85,4 @@ def hierarchy_widget_attributes(work_item_type, base_attributes, resource_parent base_attributes .merge({ 'allowedChildTypes' => { 'nodes' => child_types }, 'allowedParentTypes' => { 'nodes' => parent_types } }) end - - def status_widget_attributes(_work_item_type, base_attributes, resource_parent) - unless resource_parent&.root_ancestor&.try(:work_item_status_feature_available?) - return base_attributes.merge({ 'allowedStatuses' => { 'nodes' => [] } }) - end - - statuses = [ - { - 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/1', - 'name' => 'To do', - 'iconName' => 'status-waiting', - 'color' => '#535158', - 'position' => 0 - }, - { - 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/2', - 'name' => 'In progress', - 'iconName' => 'status-running', - 'color' => '#0b5cad', - 'position' => 0 - }, - { - 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/3', - 'name' => 'Done', - 'iconName' => 'status-success', - 'color' => '#23663b', - 'position' => 0 - }, - { - 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/4', - 'name' => "Won't do", - 'iconName' => 'status-cancelled', - 'color' => '#ae1901', - 'position' => 0 - }, - { - 'id' => 'gid://gitlab/WorkItems::Statuses::SystemDefined::Status/5', - 'name' => 'Duplicate', - 'iconName' => 'status-cancelled', - 'color' => '#ae1901', - 'position' => 10 - } - ] - - base_attributes.merge({ 'allowedStatuses' => { 'nodes' => statuses } }) - end end diff --git a/spec/support/shared_examples/requests/api/graphql/work_item_type_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/work_item_type_list_shared_examples.rb index 294a5449e49dac285bebde9780a8f7ade0850076..28ac13d8d1feb1b32f51c06de90d3ad9b2a75536 100644 --- a/spec/support/shared_examples/requests/api/graphql/work_item_type_list_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/work_item_type_list_shared_examples.rb @@ -33,25 +33,6 @@ ) end - if Gitlab.ee? - it 'returns the allowed custom statuses' do - stub_licensed_features(work_item_custom_status: true) - post_graphql(query, current_user: current_user) - - work_item_types = graphql_data_at(parent_key, :workItemTypes, :nodes) - status_widgets = work_item_types.flat_map do |work_item_type| - work_item_type['widgetDefinitions'].select { |widget| widget['type'] == 'STATUS' } - end - - expect(status_widgets).to be_present - status_widgets.each do |widget| - expect(widget['allowedStatuses']).to be_present - expect(widget['allowedStatuses']['nodes']).to all(include('id', 'name', 'iconName', 'color', - 'position')) - end - end - end - it 'prevents N+1 queries' do # Destroy 2 existing types WorkItems::Type.by_type([:issue, :task]).delete_all