diff --git a/app/finders/issues/issue_types_filter.rb b/app/finders/issues/issue_types_filter.rb index ad5b54e4c50c02853c09fa77fb47d032ced7dcd8..563904b174b475f36b2ecc215d492c9442c6a124 100644 --- a/app/finders/issues/issue_types_filter.rb +++ b/app/finders/issues/issue_types_filter.rb @@ -16,7 +16,8 @@ def by_issue_types(issues) end def valid_param_types? - (::WorkItems::Type.base_types.keys & param_types).sort == param_types.sort + provider = ::WorkItems::TypesFramework::Provider.new(parent) + (provider.base_types & param_types).sort == param_types.sort end def param_types diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index addb14cebf0aa58668008d41a418e66a9aed6022..7b31941df4313a25fdd691d8c5f5202e19a0aaa9 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -28,7 +28,7 @@ # updated_after: datetime # updated_before: datetime # confidential: boolean -# issue_types: array of strings (one of WorkItems::Type.base_types) +# issue_types: array of strings (one of WorkItems::TypesFramework::Provider.new(root_ancestor).base_types) # class IssuesFinder < IssuableFinder extend ::Gitlab::Utils::Override @@ -137,7 +137,8 @@ def by_service_desk(items) end def by_negated_issue_types(items) - issue_type_params = Array(not_params[:issue_types]).map(&:to_s) & WorkItems::Type.base_types.keys + provider = WorkItems::TypesFramework::Provider.new(params.parent) + issue_type_params = Array(not_params[:issue_types]).map(&:to_s) & provider.base_types return items if issue_type_params.blank? items.without_issue_type(issue_type_params) diff --git a/app/finders/work_items/types_finder.rb b/app/finders/work_items/types_finder.rb index 643d7f84309b11daa21f7485f1e12b96075d1dd2..822a3570c6d79fd0e78b8d59ea2f5de8f024ccde 100644 --- a/app/finders/work_items/types_finder.rb +++ b/app/finders/work_items/types_finder.rb @@ -7,16 +7,17 @@ def initialize(container:) end def execute(name: nil, only_available: false) - return WorkItems::Type.none if unavailable_container? - return order(WorkItems::Type.by_type(name)) if name.present? && !only_available - return order(WorkItems::Type) unless only_available + return [] if unavailable_container? + + provider = ::WorkItems::TypesFramework::Provider.new(@container.root_ancestor) + return Array.wrap(provider.find_by_base_type(name)) if name.present? && !only_available + return provider.all_ordered_by_name unless only_available ::WorkItems::TypesFilter .new(container: @container) .allowed_types .then { |types| name.present? ? types.intersection(Array.wrap(name)) : types } - .then { |types| WorkItems::Type.by_type(types) } - .then { |scope| order(scope) } + .then { |types| provider.by_base_types_ordered_by_name(types) } end private diff --git a/app/graphql/mutations/work_items/convert.rb b/app/graphql/mutations/work_items/convert.rb index 737bb90c4112e2cce33fb150737115073bee1937..334cba92f2c1c98e660de7ee28610f3d35983c4f 100644 --- a/app/graphql/mutations/work_items/convert.rb +++ b/app/graphql/mutations/work_items/convert.rb @@ -24,7 +24,7 @@ class Convert < BaseMutation def resolve(attributes) work_item = authorized_find!(id: attributes[:id]) - work_item_type = find_work_item_type!(attributes[:work_item_type_id]) + work_item_type = find_work_item_type!(attributes[:work_item_type_id], work_item.namespace.root_ancestor) authorize_work_item_type!(work_item, work_item_type) update_result = ::WorkItems::UpdateService.new( @@ -44,12 +44,10 @@ def resolve(attributes) private - def find_work_item_type!(gid) - work_item_type = ::WorkItems::Type.find_by_id(gid.model_id) - - return work_item_type if work_item_type.present? - - message = format(_('Work Item type with id %{id} was not found'), id: gid.model_id) + def find_work_item_type!(gid, root_ancestor) + ::WorkItems::TypesFramework::Provider.new(root_ancestor).find_by_gid(gid) + rescue ActiveRecord::RecordNotFound + message = format(_('Work Item type with %{gid} was not found'), gid: gid) raise_resource_not_available_error! message end diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb index 31a8ba2af7f4e1bb1c94d82fd6d341a6102b78ed..73ef7a104ec8d4aec5c912e47bbfda73a2f89ee6 100644 --- a/app/graphql/mutations/work_items/create.rb +++ b/app/graphql/mutations/work_items/create.rb @@ -107,7 +107,7 @@ def resolve(project_path: nil, namespace_path: nil, **attributes) container_path = project_path || namespace_path container = authorized_find!(container_path) - params = params_with_work_item_type(attributes).merge(author_id: current_user.id, + params = params_with_work_item_type(attributes, container).merge(author_id: current_user.id, scope_validator: context[:scope_validator]) params = params_with_resolve_discussion_params(params) type = params[:work_item_type] @@ -169,11 +169,11 @@ def params_with_resolve_discussion_params(attributes) attributes end - def params_with_work_item_type(attributes) - work_item_type_id = attributes.delete(:work_item_type_id)&.model_id - work_item_type = ::WorkItems::Type.find_by_id(work_item_type_id) + def params_with_work_item_type(attributes, container) + work_item_type_gid = attributes.delete(:work_item_type_id) + provider = ::WorkItems::TypesFramework::Provider.new(container.root_ancestor) - attributes[:work_item_type] = work_item_type + attributes[:work_item_type] = provider.find_by_gid(work_item_type_gid) attributes end diff --git a/app/models/issue.rb b/app/models/issue.rb index b459712130a5f64bcee9064c3703add24b004a67..fd68a0bfad30f5fc01cef5de3fe693a417c1677a 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -53,9 +53,6 @@ class Issue < ApplicationRecord # Types of issues that should be displayed on issue board lists TYPES_FOR_BOARD_LIST = %w[issue incident].freeze - # This default came from the enum `issue_type` column. Defined as default in the DB - DEFAULT_ISSUE_TYPE = :issue - # Interim columns to convert integer IDs to bigint ignore_column :author_id_convert_to_bigint, remove_with: '17.8', remove_after: '2024-12-13' ignore_column :closed_by_id_convert_to_bigint, remove_with: '17.8', remove_after: '2024-12-13' @@ -275,12 +272,14 @@ def most_recent scope :counts_by_state, -> { reorder(nil).group(:state_id).count } scope :service_desk, -> { + provider = WorkItems::TypesFramework::Provider.new + where( author: User.support_bot, - work_item_type: WorkItems::Type.default_issue_type + work_item_type: provider.default_issue_type.id ) .or( - where(work_item_type: WorkItems::Type.default_by_type(:ticket)) + where(work_item_type: provider.find_by_base_type(:ticket).id) ) } @@ -829,7 +828,7 @@ def issuing_parent_id # Persisted records will always have a work_item_type. This method is useful # in places where we use a non persisted issue to perform feature checks def work_item_type_with_default - work_item_type || WorkItems::Type.default_by_type(DEFAULT_ISSUE_TYPE) + work_item_type || work_item_type_provider.default_issue_type end def issue_type @@ -991,7 +990,7 @@ def ensure_namespace_id def ensure_work_item_type return if work_item_type.present? || work_item_type_id.present? || work_item_type_id_change&.last.present? - self.work_item_type = WorkItems::Type.default_by_type(DEFAULT_ISSUE_TYPE) + self.work_item_type = work_item_type_provider.default_issue_type end def ensure_namespace_traversal_ids @@ -1001,9 +1000,12 @@ def ensure_namespace_traversal_ids def allowed_work_item_type_change return unless changes[:work_item_type_id] - involved_types = WorkItems::Type.where( - id: changes[:work_item_type_id].compact - ).pluck(:base_type).uniq + # With system-defined and custom types changes might happen in both work_item_type_id and custom_type_id. + # We'll need to change this accordingly to support both cases. + involved_types = work_item_type_provider.by_ids( + changes[:work_item_type_id].compact + ).map(&:base_type).uniq + disallowed_types = involved_types - WorkItems::Type::CHANGEABLE_BASE_TYPES return if disallowed_types.empty? @@ -1022,6 +1024,11 @@ def linked_issues_select def validate_due_date? true end + + def work_item_type_provider + ::WorkItems::TypesFramework::Provider.new(namespace&.root_ancestor) + end + strong_memoize_attr :work_item_type_provider end Issue.prepend_mod_with('Issue') diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 15e757a6e14ccbfeb729e4b30458f93842cb7690..87bde62d94b9782852a1edc2dd3662d11d971ba6 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -881,7 +881,7 @@ def allowed_work_item_types def allowed_work_item_type?(type) type = type.to_s - unless ::WorkItems::TypesFilter.base_types.include?(type) + unless ::WorkItems::TypesFilter.new.base_types.include?(type) raise ArgumentError, %("#{type}" is not a valid WorkItems::Type.base_types) end diff --git a/app/models/work_items/types_filter.rb b/app/models/work_items/types_filter.rb index f5af39efa0c4c20dbc61af7b050ed17c8a20758e..d9fde82b0a407c51f95d91aa793618ae8e9b4c64 100644 --- a/app/models/work_items/types_filter.rb +++ b/app/models/work_items/types_filter.rb @@ -8,20 +8,24 @@ class TypesFilter DISABLED_WORKFLOW_TYPES = %w[requirement test_case].freeze class << self - include ::Gitlab::Utils::StrongMemoize + # include ::Gitlab::Utils::StrongMemoize def allowed_types_for_issues - base_types.excluding('epic', *OKR_TYPES) + new(container: nil).base_types.excluding('epic', *OKR_TYPES) end - def base_types - ::WorkItems::Type.base_types.keys.map(&:to_s) - end - strong_memoize_attr :base_types + # def base_types + # # ::WorkItems::Type.base_types.keys.map(&:to_s) + # ::WorkItems::TypesFramework::Provider.new(resource_parent).base_types.map(&:to_s) + # end + # strong_memoize_attr :base_types end - def initialize(container:) + attr_reader :base_types + + def initialize(container:, base_types: []) @container = container + @base_types = base_types end # Filter types by the given resource_parent. The filters take in consideration @@ -41,13 +45,15 @@ def allowed_types .then { |types| filter_okr(types) } # overridden in EE end - private + # def base_types + # ::WorkItems::TypesFramework::Provider.new(resource_parent).base_types.map(&:to_s) + # end + # strong_memoize_attr :base_types - def base_types - self.class.base_types - end + private def resource_parent + return unless @container return if @container.owner_entity_name == :user @container.owner_entity diff --git a/app/models/work_items/types_framework/provider.rb b/app/models/work_items/types_framework/provider.rb new file mode 100644 index 0000000000000000000000000000000000000000..388d1feb175f344f5651fe56a241bc9d8437d55d --- /dev/null +++ b/app/models/work_items/types_framework/provider.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +module WorkItems + module TypesFramework + # This is the single source of truth to fetch work item types. + # + # Namespaces can use system-defined and custom work item types. + # This class aims to abstract that fetching logic away so application code doesn't need to care + # about the composition of types of a given namespace. + class Provider + include Gitlab::Utils::StrongMemoize + + def initialize(namespace = nil) + # Take the namespace as is for now. + # + # For custom types we need to either + # 1. fetch types by organization_id of the namespace for Self-Managed + # 2. fetch types by the root group for Saas + @namespace = namespace + end + + # Misses that don't use this provider class yet: + # TODO: app/models/issue.rb: 976 + # TODO: ee/app/finders/work_items/widgets/rolledup_dates_finder.rb:112 + # TODO: ee/lib/gitlab/epic_work_item_sync/diff.rb:93 + # TODO: ee/lib/search/elastic/work_item_query_builder.rb:180 + + # This would likely exclude custom types or we'd need to build the base type from the name of the type. + # Usually we use the base types in cases where we know an item needs to have a certain type. + def unfiltered_base_types + return type_class.base_types.keys unless system_defined_types_available? + + type_class.all.map(&:base_type) + end + + def filtered_base_types + WorkItems::TypesFilter.new(container: @namespace, base_types: unfiltered_base_types).allowed_types.to_a + end + strong_memoize_attr :filtered_base_types + + def filtered_types + type_class.where(base_type: filtered_base_types) + end + strong_memoize_attr :filtered_types + alias_method :all, :filtered_types + + def by_base_types(names) + filtered_types.select { |type| type.base_type.in?(names) } + end + + def find_by_base_type(name) + filtered_types.find { |type| type.base_type == name } + end + + def default_issue_type + find_by_base_type(:issue) + end + + def find_by_gid(gid) + # There're a couple of cases for the future here: + # 1. New custom type which has a different GID pattern. + # Check whether GID contains `Custom` and then resolve custom type + # 2. Converted type. When the system-defined type was modified in the namespace we + # create a custom type that references back to the system-defined one. So look it up + # from the `converted_from_system_defined_type_identifier`of the custom type. + # 3. System-defined type. Simply find by model_id. + # + # For now we still use the DB-based types so we resolve normally. + return unless gid + + filtered_types.find { |type| type.id == gid.model_id } + end + + # Id is ambiguous in terms of system-defined and custom types. + # Let's try to get rid of this as fast as possible. + # + # This has some API related usages where a work item type id is passed. + # We should change these interfaces to use a GID instead so we can properly distinguish + # between system-defined and custom types. + # + # For now it looks like we can use the GID in most cases. + def find_by_id(id) + filtered_types.find { |type| type.id == id } + end + + def by_ids(ids) + filtered_types.select { |type| type.id.in?(ids) } + end + + def all_ordered_by_name + sort_by_name(filtered_types) + end + + def by_ids_ordered_by_name(ids) + sort_by_name(by_ids(ids)) + end + + def by_base_types_ordered_by_name(names) + sort_by_name(by_base_types(names)) + end + + private + + def sort_by_name(items) + items.sort_by(&:name) + end + + def type_class + return WorkItems::Type unless system_defined_types_available? + + SystemDefined::Type + end + strong_memoize_attr :type_class + + def system_defined_types_available? + # Do instance check when we use the provider without a namespace. + return Feature.enabled?(:work_item_system_defined_type, type: :gitlab_com_derisk) unless @namespace # rubocop:disable Gitlab/FeatureFlagWithoutActor -- reason above + + Feature.enabled?(:work_item_system_defined_type, @namespace, type: :gitlab_com_derisk) + end + strong_memoize_attr :system_defined_types_available? + end + end +end diff --git a/app/serializers/issue_board_entity.rb b/app/serializers/issue_board_entity.rb index b6aa421b603bee073bcdc0556a6d76869c2558bd..411bc5dcc3823b8218f524ea8dca6ba9900d3b01 100644 --- a/app/serializers/issue_board_entity.rb +++ b/app/serializers/issue_board_entity.rb @@ -59,7 +59,7 @@ class IssueBoardEntity < Grape::Entity expose :issue_type, as: :type, format_with: :upcase, - documentation: { type: "String", desc: "One of #{::WorkItems::Type.base_types.keys.map(&:upcase)}" } + documentation: { type: "String", desc: "One of #{::WorkItems::TypesFramework::Provider.new.base_types.map(&:upcase)}" } end IssueBoardEntity.prepend_mod_with('IssueBoardEntity') diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index dca42b6b0d6766bf84a564deccab8686a87ac38c..801f074ccbab3e4d7d7b11a57ebd2cadf6bfcd96 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -101,7 +101,9 @@ class IssueEntity < IssuableEntity expose :issue_type, as: :type, format_with: :upcase, - documentation: { type: "String", desc: "One of #{::WorkItems::Type.base_types.keys.map(&:upcase)}" } + documentation: { + type: "String", desc: "One of #{::WorkItems::TypesFramework::Provider.new.base_types.map(&:upcase)}" + } end IssueEntity.prepend_mod_with('IssueEntity') diff --git a/app/serializers/linked_issue_entity.rb b/app/serializers/linked_issue_entity.rb index 9f24b465248f6733d010bdb089d1870efa3288d5..4e4aef2180e83b2bf2acd1848e8020d51a2a3981 100644 --- a/app/serializers/linked_issue_entity.rb +++ b/app/serializers/linked_issue_entity.rb @@ -28,7 +28,9 @@ class LinkedIssueEntity < Grape::Entity expose :issue_type, as: :type, format_with: :upcase, - documentation: { type: "String", desc: "One of #{::WorkItems::Type.base_types.keys.map(&:upcase)}" } + documentation: { + type: "String", desc: "One of #{::WorkItems::TypesFramework::Provider.new.base_types.map(&:upcase)}" + } expose :relation_path diff --git a/app/services/concerns/issues/issue_type_helpers.rb b/app/services/concerns/issues/issue_type_helpers.rb index e6ac08a567d431ba15030fdf97aca7793e76c90b..b30b0e8f3c0228bfe21f558cbaf7ea39b0e156e9 100644 --- a/app/services/concerns/issues/issue_type_helpers.rb +++ b/app/services/concerns/issues/issue_type_helpers.rb @@ -5,8 +5,10 @@ module IssueTypeHelpers # @param object [Issue, Project] # @param issue_type [String, Symbol] def create_issue_type_allowed?(object, issue_type) - WorkItems::Type.base_types.key?(issue_type.to_s) && - can?(current_user, :"create_#{issue_type}", object) + return false unless can?(current_user, :"create_#{issue_type}", object) + + root_ancestor = object.is_a?(Issue) ? object.namespace.root_ancestor : object.owner_entity.root_ancestor + ::WorkItems::TypesFramework::Provider.new(root_ancestor).base_types.include?(issue_type.to_s) end end end diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 0a5475ba18956b4b87f20b5d0e4545c516890ea0..79426fdbcdf868b907802e4c1266cb2951acecde 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -58,8 +58,10 @@ def self.constructor_container_arg(value) end def find_work_item_type_id(issue_type) - work_item_type = WorkItems::Type.default_by_type(issue_type) - work_item_type ||= WorkItems::Type.default_issue_type + provider = work_item_type_provider + + work_item_type = provider.find_by_base_type(issue_type) + work_item_type ||= provider.default_issue_type work_item_type.id end @@ -164,6 +166,11 @@ def filter_timestamp_params params.delete(param) unless current_user.can?(:"set_issue_#{param}", container) end end + + # Maybe memoize? + def work_item_type_provider + ::WorkItems::TypesFramework::Provider.new(container.root_ancestor) + end end end diff --git a/app/services/issues/build_service.rb b/app/services/issues/build_service.rb index 7025632a3dccddaf24c8295741fe22262751ffc4..28276a0959fd4e60b09bbec866b685170685565b 100644 --- a/app/services/issues/build_service.rb +++ b/app/services/issues/build_service.rb @@ -73,9 +73,11 @@ def issue_params private def set_work_item_type(issue) + provider = work_item_type_provider + work_item_type = if params[:work_item_type_id].present? params.delete(:work_item_type) - WorkItems::Type.find_by(id: params.delete(:work_item_type_id)) # rubocop: disable CodeReuse/ActiveRecord + provider.find_by_id(params.delete(:work_item_type_id)) else params.delete(:work_item_type) end @@ -85,11 +87,11 @@ def set_work_item_type(issue) base_type = work_item_type&.base_type || params[:issue_type] issue.work_item_type = if create_issue_type_allowed?(container, base_type) - work_item_type || WorkItems::Type.default_by_type(base_type) + work_item_type || provider.find_by_base_type(base_type) else # If no work item type was provided or not allowed, we need to set it to # the default issue_type - WorkItems::Type.default_by_type(::Issue::DEFAULT_ISSUE_TYPE) + provider.find_by_base_type(::Issue::DEFAULT_ISSUE_TYPE) end end diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 0c2283744ae0ae6d3194283eb08002346d94baea..76cbda6cb3e6ab1ee8cc3783e7e6125d3f3de273 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -248,7 +248,7 @@ def handle_issue_type_change(issue) end def do_handle_issue_type_change(issue) - old_work_item_type = ::WorkItems::Type.find_by_id(issue.work_item_type_id_before_last_save).base_type + old_work_item_type = work_item_type_provider.find_by_id(issue.work_item_type_id_before_last_save).base_type SystemNoteService.change_issue_type(issue, current_user, old_work_item_type) ::IncidentManagement::IssuableEscalationStatuses::CreateService.new(issue).execute if issue.supports_escalation? diff --git a/app/services/quick_actions/target_service.rb b/app/services/quick_actions/target_service.rb index 089b1736b651068b8701a40f24d81bcf08c60de2..abc66642c70b6b7f864218a0fba14042009649ec 100644 --- a/app/services/quick_actions/target_service.rb +++ b/app/services/quick_actions/target_service.rb @@ -21,8 +21,10 @@ def execute(type, type_iid) def work_item(type_iid) if type_iid.blank? parent = group_container? ? { namespace: group } : { project: project, namespace: project.project_namespace } + provider = ::WorkItems::TypesFramework::Provider.new(parent.root_ancestor) + return WorkItem.new( - work_item_type_id: params[:work_item_type_id] || WorkItems::Type.default_issue_type.id, + work_item_type_id: params[:work_item_type_id] || provider.default_issue_type.id, **parent ) end diff --git a/config/feature_flags/gitlab_com_derisk/work_item_system_defined_type.yml b/config/feature_flags/gitlab_com_derisk/work_item_system_defined_type.yml new file mode 100644 index 0000000000000000000000000000000000000000..efc257f6cde26c573957acff9892c8651aaf1f38 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/work_item_system_defined_type.yml @@ -0,0 +1,10 @@ +--- +name: work_item_system_defined_type +description: Switch WorkItemType model from database to fixed item model. +feature_issue_url: +introduced_by_url: +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/583465 +milestone: '18.7' +group: group::project management +type: gitlab_com_derisk +default_enabled: false diff --git a/ee/lib/gitlab/elastic/search_results.rb b/ee/lib/gitlab/elastic/search_results.rb index 68cb7d8e59211d5a1fddf542a09b559415b6bc17..82af5ab432e85882acd1d34d939f2e78968f7527 100644 --- a/ee/lib/gitlab/elastic/search_results.rb +++ b/ee/lib/gitlab/elastic/search_results.rb @@ -368,6 +368,7 @@ def scope_options(scope) work_item_scope_options.merge( not_work_item_type_ids: nil, klass: WorkItem, + # TODO: Rework so it uses base type work_item_type_ids: [::WorkItems::Type.find_by_name(::WorkItems::Type::TYPE_NAMES[:epic]).id] ).except(:fields) when :users @@ -386,12 +387,14 @@ def work_item_scope_options { klass: Issue, # For rendering the UI index_name: ::Search::Elastic::References::WorkItem.index, + # TODO: Rework so it uses base type not_work_item_type_ids: [::WorkItems::Type.find_by_name(::WorkItems::Type::TYPE_NAMES[:epic]).id] }, filters.slice(*::Search::Elastic::References::WorkItem::PERMITTED_FILTER_KEYS) ) if filters[:type].present? + # TODO: Rework so it uses base type work_item_type_id = ::WorkItems::Type.find_by_name(::WorkItems::Type::TYPE_NAMES[filters[:type].to_sym])&.id work_item_scope_options[:work_item_type_ids] = [work_item_type_id] unless work_item_type_id.nil? end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 27ff1c676bb9192f46a29527f9827dfca3f605d5..0fd386cb515f765157b741c7c3967f07803f120b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -76553,7 +76553,7 @@ msgstr "" msgid "Withdraw access request" msgstr "" -msgid "Work Item type with id %{id} was not found" +msgid "Work Item type with %{gid} was not found" msgstr "" msgid "Work in progress (open and unassigned)" diff --git a/spec/requests/api/graphql/mutations/work_items/convert_spec.rb b/spec/requests/api/graphql/mutations/work_items/convert_spec.rb index edc598c842ec1923af13d8519f1ded4dd17a5f50..658da75ce34497062defab8fc0d8cb2b26f8d573 100644 --- a/spec/requests/api/graphql/mutations/work_items/convert_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/convert_spec.rb @@ -39,7 +39,7 @@ post_graphql_mutation(mutation, current_user: current_user) expect(graphql_errors).to include( - a_hash_including('message' => "Work Item type with id #{non_existing_record_id} was not found") + a_hash_including('message' => "Work Item type with #{work_item_type_id} was not found") ) end end diff --git a/spec/support/finder_collection_allowlist.yml b/spec/support/finder_collection_allowlist.yml index cdb1c471f8701853cf527893a9ef688cc9a7cae9..548f1607faf456d735ea1e2ddbab2d318a02427b 100644 --- a/spec/support/finder_collection_allowlist.yml +++ b/spec/support/finder_collection_allowlist.yml @@ -19,6 +19,7 @@ - Security::AnalyzerGroupStatusFinder # Reason: To give accurate counts, return all analyzer types, even when there is no DB record - Ai::ClickHouseUsageEventsFinder # Reason: The finder's data is coming from Clickhouse and not Postgres, no ActiveRelation involved - Ai::UsageEventsFinder # Reason: The finder's data is coming from Clickhouse and not Postgres, no ActiveRelation involved +- WorkItems::TypesFinder # Reason: Types can come from system-defined or ActiveRecord, no ActiveRelation involved # Temporary excludes (aka TODOs) # For example: