Commit a078e802 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch 'issue_222856_be_models_api' into 'master'

Move models/API service desk code to core

See merge request gitlab-org/gitlab!35557
parents 87d6abca 6842fe9a
...@@ -60,6 +60,12 @@ module Types ...@@ -60,6 +60,12 @@ module Types
field :merge_requests_ff_only_enabled, GraphQL::BOOLEAN_TYPE, null: true, field :merge_requests_ff_only_enabled, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.' description: 'Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.'
field :service_desk_enabled, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if the project has service desk enabled.'
field :service_desk_address, GraphQL::STRING_TYPE, null: true,
description: 'E-mail address of the service desk.'
field :avatar_url, GraphQL::STRING_TYPE, null: true, calls_gitaly: true, field :avatar_url, GraphQL::STRING_TYPE, null: true, calls_gitaly: true,
description: 'URL to avatar image file of the project', description: 'URL to avatar image file of the project',
resolve: -> (project, args, ctx) do resolve: -> (project, args, ctx) do
......
...@@ -98,6 +98,8 @@ class Issue < ApplicationRecord ...@@ -98,6 +98,8 @@ class Issue < ApplicationRecord
scope :counts_by_state, -> { reorder(nil).group(:state_id).count } scope :counts_by_state, -> { reorder(nil).group(:state_id).count }
scope :service_desk, -> { where(author: ::User.support_bot) }
# An issue can be uniquely identified by project_id and iid # An issue can be uniquely identified by project_id and iid
# Takes one or more sets of composite IDs, expressed as hash-like records of # Takes one or more sets of composite IDs, expressed as hash-like records of
# `{project_id: x, iid: y}`. # `{project_id: x, iid: y}`.
...@@ -373,6 +375,10 @@ class Issue < ApplicationRecord ...@@ -373,6 +375,10 @@ class Issue < ApplicationRecord
) )
end end
def from_service_desk?
author.id == User.support_bot.id
end
private private
def ensure_metrics def ensure_metrics
......
...@@ -204,6 +204,7 @@ class Project < ApplicationRecord ...@@ -204,6 +204,7 @@ class Project < ApplicationRecord
has_one :grafana_integration, inverse_of: :project has_one :grafana_integration, inverse_of: :project
has_one :project_setting, inverse_of: :project, autosave: true has_one :project_setting, inverse_of: :project, autosave: true
has_one :alerting_setting, inverse_of: :project, class_name: 'Alerting::ProjectAlertingSetting' has_one :alerting_setting, inverse_of: :project, class_name: 'Alerting::ProjectAlertingSetting'
has_one :service_desk_setting, class_name: 'ServiceDeskSetting'
# Merge Requests for target project should be removed with it # Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
...@@ -495,6 +496,7 @@ class Project < ApplicationRecord ...@@ -495,6 +496,7 @@ class Project < ApplicationRecord
.where(repository_languages: { programming_language_id: lang_id_query }) .where(repository_languages: { programming_language_id: lang_id_query })
end end
scope :service_desk_enabled, -> { where(service_desk_enabled: true) }
scope :with_builds_enabled, -> { with_feature_enabled(:builds) } scope :with_builds_enabled, -> { with_feature_enabled(:builds) }
scope :with_issues_enabled, -> { with_feature_enabled(:issues) } scope :with_issues_enabled, -> { with_feature_enabled(:issues) }
scope :with_issues_available_for_user, ->(current_user) { with_feature_available_for_user(:issues, current_user) } scope :with_issues_available_for_user, ->(current_user) { with_feature_available_for_user(:issues, current_user) }
...@@ -718,6 +720,12 @@ class Project < ApplicationRecord ...@@ -718,6 +720,12 @@ class Project < ApplicationRecord
from_union([with_issues_enabled, with_merge_requests_enabled]).select(:id) from_union([with_issues_enabled, with_merge_requests_enabled]).select(:id)
end end
def find_by_service_desk_project_key(key)
# project_key is not indexed for now
# see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24063#note_282435524 for details
joins(:service_desk_setting).find_by('service_desk_settings.project_key' => key)
end
end end
def initialize(attributes = nil) def initialize(attributes = nil)
...@@ -2425,10 +2433,19 @@ class Project < ApplicationRecord ...@@ -2425,10 +2433,19 @@ class Project < ApplicationRecord
end end
def service_desk_enabled def service_desk_enabled
false Gitlab::ServiceDesk.enabled?(project: self)
end end
alias_method :service_desk_enabled?, :service_desk_enabled alias_method :service_desk_enabled?, :service_desk_enabled
def service_desk_address
return unless service_desk_enabled?
config = Gitlab.config.incoming_email
wildcard = Gitlab::IncomingEmail::WILDCARD_PLACEHOLDER
config.address&.gsub(wildcard, "#{full_path_slug}-#{id}-issue-")
end
def root_namespace def root_namespace
if namespace.has_parent? if namespace.has_parent?
namespace.root_ancestor namespace.root_ancestor
......
...@@ -6,12 +6,6 @@ module EE ...@@ -6,12 +6,6 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
field :service_desk_enabled, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if the project has service desk enabled.'
field :service_desk_address, GraphQL::STRING_TYPE, null: true,
description: 'E-mail address of the service desk.'
field :vulnerabilities, field :vulnerabilities,
::Types::VulnerabilityType.connection_type, ::Types::VulnerabilityType.connection_type,
null: true, null: true,
......
...@@ -19,7 +19,6 @@ module EE ...@@ -19,7 +19,6 @@ module EE
scope :order_weight_desc, -> { reorder ::Gitlab::Database.nulls_last_order('weight', 'DESC') } scope :order_weight_desc, -> { reorder ::Gitlab::Database.nulls_last_order('weight', 'DESC') }
scope :order_weight_asc, -> { reorder ::Gitlab::Database.nulls_last_order('weight') } scope :order_weight_asc, -> { reorder ::Gitlab::Database.nulls_last_order('weight') }
scope :service_desk, -> { where(author: ::User.support_bot) }
scope :no_epic, -> { left_outer_joins(:epic_issue).where(epic_issues: { epic_id: nil }) } scope :no_epic, -> { left_outer_joins(:epic_issue).where(epic_issues: { epic_id: nil }) }
scope :any_epic, -> { joins(:epic_issue) } scope :any_epic, -> { joins(:epic_issue) }
scope :in_epics, ->(epics) do scope :in_epics, ->(epics) do
...@@ -179,10 +178,6 @@ module EE ...@@ -179,10 +178,6 @@ module EE
IssueLink.inverse_link_type(type) IssueLink.inverse_link_type(type)
end end
def from_service_desk?
author.id == ::User.support_bot.id
end
class_methods do class_methods do
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
......
...@@ -41,7 +41,6 @@ module EE ...@@ -41,7 +41,6 @@ module EE
has_one :github_service has_one :github_service
has_one :gitlab_slack_application_service has_one :gitlab_slack_application_service
has_one :service_desk_setting, class_name: 'ServiceDeskSetting'
has_one :tracing_setting, class_name: 'ProjectTracingSetting' has_one :tracing_setting, class_name: 'ProjectTracingSetting'
has_one :feature_usage, class_name: 'ProjectFeatureUsage' has_one :feature_usage, class_name: 'ProjectFeatureUsage'
has_one :status_page_setting, inverse_of: :project, class_name: 'StatusPage::ProjectSetting' has_one :status_page_setting, inverse_of: :project, class_name: 'StatusPage::ProjectSetting'
...@@ -121,7 +120,6 @@ module EE ...@@ -121,7 +120,6 @@ module EE
scope :with_active_jira_services, -> { joins(:services).merge(::JiraService.active) } scope :with_active_jira_services, -> { joins(:services).merge(::JiraService.active) }
scope :with_jira_dvcs_cloud, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: true)) } scope :with_jira_dvcs_cloud, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: true)) }
scope :with_jira_dvcs_server, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false)) } scope :with_jira_dvcs_server, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false)) }
scope :service_desk_enabled, -> { where(service_desk_enabled: true) }
scope :github_imported, -> { where(import_type: 'github') } scope :github_imported, -> { where(import_type: 'github') }
scope :with_protected_branches, -> { joins(:protected_branches) } scope :with_protected_branches, -> { joins(:protected_branches) }
scope :with_repositories_enabled, -> { joins(:project_feature).where(project_features: { repository_access_level: ::ProjectFeature::ENABLED }) } scope :with_repositories_enabled, -> { joins(:project_feature).where(project_features: { repository_access_level: ::ProjectFeature::ENABLED }) }
...@@ -202,12 +200,6 @@ module EE ...@@ -202,12 +200,6 @@ module EE
.where('services.id IS NULL') .where('services.id IS NULL')
end end
def find_by_service_desk_project_key(key)
# project_key is not indexed for now
# see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24063#note_282435524 for details
joins(:service_desk_setting).find_by('service_desk_settings.project_key' => key)
end
override :with_web_entity_associations override :with_web_entity_associations
def with_web_entity_associations def with_web_entity_associations
super.preload(:compliance_framework_setting) super.preload(:compliance_framework_setting)
...@@ -333,21 +325,6 @@ module EE ...@@ -333,21 +325,6 @@ module EE
feature_available?(:github_project_service_integration) feature_available?(:github_project_service_integration)
end end
override :service_desk_enabled
def service_desk_enabled
::EE::Gitlab::ServiceDesk.enabled?(project: self)
end
alias_method :service_desk_enabled?, :service_desk_enabled
def service_desk_address
return unless service_desk_enabled?
config = ::Gitlab.config.incoming_email
wildcard = ::Gitlab::IncomingEmail::WILDCARD_PLACEHOLDER
config.address&.gsub(wildcard, "#{full_path_slug}-#{id}-issue-")
end
override :add_import_job override :add_import_job
def add_import_job def add_import_job
return if gitlab_custom_project_template_import? return if gitlab_custom_project_template_import?
......
# frozen_string_literal: true # frozen_string_literal: true
require 'ee/gitlab/service_desk' require 'gitlab/service_desk'
module EE module EE
module NotificationService module NotificationService
...@@ -97,7 +97,7 @@ module EE ...@@ -97,7 +97,7 @@ module EE
end end
def send_service_desk_notification(note) def send_service_desk_notification(note)
return unless EE::Gitlab::ServiceDesk.supported? return unless ::Gitlab::ServiceDesk.supported?
return unless note.noteable_type == 'Issue' return unless note.noteable_type == 'Issue'
issue = note.noteable issue = note.noteable
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- link_start = "<a href='#{help_page_path('user/project/service_desk')}' target='_blank' rel='noopener noreferrer'>".html_safe - link_start = "<a href='#{help_page_path('user/project/service_desk')}' target='_blank' rel='noopener noreferrer'>".html_safe
%p= _('Enable/disable your service desk. %{link_start}Learn more about service desk%{link_end}.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe } %p= _('Enable/disable your service desk. %{link_start}Learn more about service desk%{link_end}.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
.settings-content .settings-content
- if EE::Gitlab::ServiceDesk.supported? - if ::Gitlab::ServiceDesk.supported?
.js-service-desk-setting-root{ data: { endpoint: project_service_desk_path(@project), .js-service-desk-setting-root{ data: { endpoint: project_service_desk_path(@project),
enabled: "#{@project.service_desk_enabled}", enabled: "#{@project.service_desk_enabled}",
incoming_email: (@project.service_desk_address if @project.service_desk_enabled), incoming_email: (@project.service_desk_address if @project.service_desk_enabled),
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
- can_edit_project_settings = can?(current_user, :admin_project, @project) - can_edit_project_settings = can?(current_user, :admin_project, @project)
- title_text = _("Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab") - title_text = _("Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab")
- if ::EE::Gitlab::ServiceDesk.supported? - if ::Gitlab::ServiceDesk.supported?
%div{ class: "#{callout_selector}" } %div{ class: "#{callout_selector}" }
.svg-content .svg-content
= render svg_path = render svg_path
......
...@@ -25,8 +25,6 @@ module EE ...@@ -25,8 +25,6 @@ module EE
expose :external_authorization_classification_label, expose :external_authorization_classification_label,
if: ->(_, _) { License.feature_available?(:external_authorization_service_api_management) } if: ->(_, _) { License.feature_available?(:external_authorization_service_api_management) }
expose :packages_enabled, if: ->(project, _) { project.feature_available?(:packages) } expose :packages_enabled, if: ->(project, _) { project.feature_available?(:packages) }
expose :service_desk_enabled
expose :service_desk_address
expose :marked_for_deletion_at, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) } expose :marked_for_deletion_at, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) }
expose :marked_for_deletion_on, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) } do |project, _| expose :marked_for_deletion_on, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) } do |project, _|
project.marked_for_deletion_at project.marked_for_deletion_at
......
...@@ -20,7 +20,6 @@ module EE ...@@ -20,7 +20,6 @@ module EE
optional :mirror, type: Grape::API::Boolean, desc: 'Enables pull mirroring in a project' optional :mirror, type: Grape::API::Boolean, desc: 'Enables pull mirroring in a project'
optional :mirror_trigger_builds, type: Grape::API::Boolean, desc: 'Pull mirroring triggers builds' optional :mirror_trigger_builds, type: Grape::API::Boolean, desc: 'Pull mirroring triggers builds'
optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project' optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project'
optional :service_desk_enabled, type: Grape::API::Boolean, desc: 'Disable or enable the service desk'
end end
params :optional_filter_params_ee do params :optional_filter_params_ee do
...@@ -52,8 +51,7 @@ module EE ...@@ -52,8 +51,7 @@ module EE
:external_authorization_classification_label, :external_authorization_classification_label,
:import_url, :import_url,
:packages_enabled, :packages_enabled,
:fallback_approvals_required, :fallback_approvals_required
:service_desk_enabled
] ]
end end
end end
......
# frozen_string_literal: true
module EE
module Gitlab
module ServiceDesk
# Check whether a project or GitLab instance can support the Service Desk
# feature. Use `project.service_desk_enabled?` to check whether it is
# enabled for a particular project.
def self.enabled?(project:)
supported? && project[:service_desk_enabled]
end
def self.supported?
::Gitlab::IncomingEmail.enabled? && ::Gitlab::IncomingEmail.supports_wildcard?
end
end
end
end
...@@ -29,7 +29,7 @@ module Gitlab ...@@ -29,7 +29,7 @@ module Gitlab
end end
def can_handle? def can_handle?
::EE::Gitlab::ServiceDesk.supported? && (project_id || can_handle_legacy_format? || service_desk_key) ::Gitlab::ServiceDesk.supported? && (project_id || can_handle_legacy_format? || service_desk_key)
end end
def execute def execute
......
...@@ -68,14 +68,6 @@ FactoryBot.modify do ...@@ -68,14 +68,6 @@ FactoryBot.modify do
end end
end end
trait :service_desk_disabled do
service_desk_enabled { nil }
end
trait(:service_desk_enabled) do
service_desk_enabled { true }
end
trait :github_imported do trait :github_imported do
import_type { 'github' } import_type { 'github' }
end end
......
...@@ -33,7 +33,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js do ...@@ -33,7 +33,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
describe 'issues list' do describe 'issues list' do
context 'when service desk is misconfigured' do context 'when service desk is misconfigured' do
before do before do
allow(EE::Gitlab::ServiceDesk).to receive(:supported?).and_return(false) allow(::Gitlab::ServiceDesk).to receive(:supported?).and_return(false)
visit service_desk_project_issues_path(project) visit service_desk_project_issues_path(project)
end end
......
...@@ -25,7 +25,7 @@ RSpec.describe 'Promotions', :js do ...@@ -25,7 +25,7 @@ RSpec.describe 'Promotions', :js do
context 'when service desk is not supported' do context 'when service desk is not supported' do
before do before do
allow(::EE::Gitlab::ServiceDesk).to receive(:supported?).and_return(false) allow(::Gitlab::ServiceDesk).to receive(:supported?).and_return(false)
end end
it 'appears in project edit page' do it 'appears in project edit page' do
...@@ -51,7 +51,7 @@ RSpec.describe 'Promotions', :js do ...@@ -51,7 +51,7 @@ RSpec.describe 'Promotions', :js do
context 'when service desk is supported' do context 'when service desk is supported' do
before do before do
allow(::EE::Gitlab::ServiceDesk).to receive(:supported?).and_return(true) allow(::Gitlab::ServiceDesk).to receive(:supported?).and_return(true)
end end
it 'does not show promotion' do it 'does not show promotion' do
......
...@@ -15,9 +15,8 @@ RSpec.describe GitlabSchema.types['Project'] do ...@@ -15,9 +15,8 @@ RSpec.describe GitlabSchema.types['Project'] do
it 'includes the ee specific fields' do it 'includes the ee specific fields' do
expected_fields = %w[ expected_fields = %w[
service_desk_enabled service_desk_address vulnerabilities vulnerability_scanners vulnerabilities vulnerability_scanners requirement_states_count
requirement_states_count vulnerability_severities_count packages vulnerability_severities_count packages compliance_frameworks
compliance_frameworks
] ]
expect(described_class).to include_graphql_fields(*expected_fields) expect(described_class).to include_graphql_fields(*expected_fields)
......
...@@ -221,8 +221,6 @@ RSpec.describe Gitlab::UsageData do ...@@ -221,8 +221,6 @@ RSpec.describe Gitlab::UsageData do
it 'gathers Service Desk data' do it 'gathers Service Desk data' do
create_list(:issue, 2, confidential: true, author: User.support_bot, project: project) create_list(:issue, 2, confidential: true, author: User.support_bot, project: project)
allow(::EE::Gitlab::ServiceDesk).to receive(:enabled?).with(anything).and_return(true)
expect(subject).to eq(service_desk_enabled_projects: 1, expect(subject).to eq(service_desk_enabled_projects: 1,
service_desk_issues: 2) service_desk_issues: 2)
end end
......
...@@ -142,8 +142,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do ...@@ -142,8 +142,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
context 'is enabled' do context 'is enabled' do
before do before do
allow(::EE::Gitlab::ServiceDesk).to receive(:enabled?).and_return(true) allow(::Gitlab::ServiceDesk).to receive(:enabled?).with(project: project).and_return(true)
allow(::EE::Gitlab::ServiceDesk).to receive(:enabled?).with(project: project).and_return(true)
project.project_feature.update!(issues_access_level: issues_access_level) project.project_feature.update!(issues_access_level: issues_access_level)
end end
...@@ -186,8 +185,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do ...@@ -186,8 +185,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
context 'is disabled' do context 'is disabled' do
before do before do
allow(::EE::Gitlab::ServiceDesk).to receive(:enabled?).and_return(false) allow(::Gitlab::ServiceDesk).to receive(:enabled?).with(project: project).and_return(false)
allow(::EE::Gitlab::ServiceDesk).to receive(:enabled?).with(project: project).and_return(false)
end end
it 'does not create a comment' do it 'does not create a comment' do
......
...@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -20,7 +20,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
let_it_be(:project) { create(:project, :repository, :public, namespace: namespace, path: 'test', service_desk_enabled: true) } let_it_be(:project) { create(:project, :repository, :public, namespace: namespace, path: 'test', service_desk_enabled: true) }
before do before do
allow(::EE::Gitlab::ServiceDesk).to receive(:supported?).and_return(true) allow(::Gitlab::ServiceDesk).to receive(:supported?).and_return(true)
end end
shared_examples 'a new issue request' do shared_examples 'a new issue request' do
...@@ -248,7 +248,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -248,7 +248,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
context 'when service desk is not enabled for project' do context 'when service desk is not enabled for project' do
before do before do
allow(::EE::Gitlab::ServiceDesk).to receive(:enabled?).and_return(false) allow(::Gitlab::ServiceDesk).to receive(:enabled?).and_return(false)
end end
it 'does not create an issue' do it 'does not create an issue' do
......
...@@ -271,7 +271,7 @@ RSpec.describe Namespace do ...@@ -271,7 +271,7 @@ RSpec.describe Namespace do
it 'only checks the plan once' do it 'only checks the plan once' do
expect(group).to receive(:load_feature_available).once.and_call_original expect(group).to receive(:load_feature_available).once.and_call_original
2.times { group.feature_available?(:service_desk) } 2.times { group.feature_available?(:push_rules) }
end end
context 'when checking namespace plan' do context 'when checking namespace plan' do
......
...@@ -69,16 +69,6 @@ RSpec.describe Issue do ...@@ -69,16 +69,6 @@ RSpec.describe Issue do
end end
context 'scopes' do context 'scopes' do
describe '.service_desk' do
it 'returns the service desk issue' do
service_desk_issue = create(:issue, author: ::User.support_bot)
regular_issue = create(:issue)
expect(described_class.service_desk).to include(service_desk_issue)
expect(described_class.service_desk).not_to include(regular_issue)
end
end
describe '.counts_by_health_status' do describe '.counts_by_health_status' do
it 'returns counts grouped by health_status' do it 'returns counts grouped by health_status' do
create(:issue, health_status: :on_track) create(:issue, health_status: :on_track)
...@@ -671,22 +661,6 @@ RSpec.describe Issue do ...@@ -671,22 +661,6 @@ RSpec.describe Issue do
it_behaves_like 'having health status' it_behaves_like 'having health status'
describe '#service_desk?' do
subject { issue.from_service_desk? }
context 'when issue author is support bot' do
let(:issue) { create(:issue, author: ::User.support_bot) }
it { is_expected.to be_truthy }
end
context 'when issue author is not support bot' do
let(:issue) { create(:issue) }
it { is_expected.to be_falsey }
end
end
describe '#can_assign_epic?' do describe '#can_assign_epic?' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
......
...@@ -94,16 +94,6 @@ RSpec.describe Project do ...@@ -94,16 +94,6 @@ RSpec.describe Project do
end end
end end
describe '.service_desk_enabled' do
it 'returns the correct project' do
project_with_service_desk_enabled = create(:project)
project_with_service_desk_disabled = create(:project, :service_desk_disabled)
expect(described_class.service_desk_enabled).to include(project_with_service_desk_enabled)
expect(described_class.service_desk_enabled).not_to include(project_with_service_desk_disabled)
end
end
describe '.with_jira_dvcs_cloud' do describe '.with_jira_dvcs_cloud' do
it 'returns the correct project' do it 'returns the correct project' do
jira_dvcs_cloud_project = create(:project, :jira_dvcs_cloud) jira_dvcs_cloud_project = create(:project, :jira_dvcs_cloud)
...@@ -174,20 +164,6 @@ RSpec.describe Project do ...@@ -174,20 +164,6 @@ RSpec.describe Project do
end end
end end
describe '.find_by_service_desk_project_key' do
it 'returns the correct project' do
project2 = create(:project)
create(:service_desk_setting, project: project, project_key: 'key1')
create(:service_desk_setting, project: project2, project_key: 'key2')
expect(Project.find_by_service_desk_project_key('key2')).to eq(project2)
end
it 'returns nil if there is no project with the key' do
expect(Project.find_by_service_desk_project_key('some_key')).to be_nil
end
end
describe '.with_shared_runners_limit_enabled' do describe '.with_shared_runners_limit_enabled' do
let(:public_cost_factor) { 1.0 } let(:public_cost_factor) { 1.0 }
...@@ -953,7 +929,7 @@ RSpec.describe Project do ...@@ -953,7 +929,7 @@ RSpec.describe Project do
expect(project).to receive(:load_licensed_feature_available) expect(project).to receive(:load_licensed_feature_available)
.once.and_call_original .once.and_call_original
2.times { project.feature_available?(:service_desk) } 2.times { project.feature_available?(:push_rules) }
end end
context 'when feature symbol is not included on Namespace features code' do context 'when feature symbol is not included on Namespace features code' do
...@@ -1135,38 +1111,6 @@ RSpec.describe Project do ...@@ -1135,38 +1111,6 @@ RSpec.describe Project do
end end
end end
describe '#service_desk_enabled?' do
let!(:license) { create(:license, plan: License::PREMIUM_PLAN) }
let(:namespace) { create(:namespace) }
subject(:project) { build(:project, :private, namespace: namespace, service_desk_enabled: true) }
before do
allow(::Gitlab).to receive(:com?).and_return(true)
allow(::Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
allow(::Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
end
it 'is enabled' do
expect(project.service_desk_enabled?).to be_truthy
expect(project.service_desk_enabled).to be_truthy
end
end
describe '#service_desk_address' do
let(:project) { create(:project, service_desk_enabled: true) }
before do
allow(::EE::Gitlab::ServiceDesk).to receive(:enabled?).and_return(true)
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
allow(Gitlab.config.incoming_email).to receive(:address).and_return("test+%{key}@mail.com")
end
it 'uses project full path as service desk address key' do
expect(project.service_desk_address).to eq("test+#{project.full_path_slug}-#{project.project_id}-issue-@mail.com")
end
end
describe '#approvals_before_merge' do describe '#approvals_before_merge' do
where(:license_value, :db_value, :expected) do where(:license_value, :db_value, :expected) do
true | 5 | 5 true | 5 | 5
......
...@@ -41,9 +41,5 @@ module EE ...@@ -41,9 +41,5 @@ module EE
def stub_packages_setting(messages) def stub_packages_setting(messages)
allow(::Gitlab.config.packages).to receive_messages(to_settings(messages)) allow(::Gitlab.config.packages).to receive_messages(to_settings(messages))
end end
def stub_service_desk_email_setting(messages)
allow(::Gitlab.config.service_desk_email).to receive_messages(to_settings(messages))
end
end end
end end
...@@ -51,6 +51,8 @@ module API ...@@ -51,6 +51,8 @@ module API
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
expose :service_desk_enabled
expose :service_desk_address
expose(:can_create_merge_request_in) do |project, options| expose(:can_create_merge_request_in) do |project, options|
Ability.allowed?(options[:current_user], :create_merge_request_in, project) Ability.allowed?(options[:current_user], :create_merge_request_in, project)
......
...@@ -15,6 +15,7 @@ module API ...@@ -15,6 +15,7 @@ module API
optional :auto_cancel_pending_pipelines, type: String, values: %w(disabled enabled), desc: 'Auto-cancel pending pipelines' optional :auto_cancel_pending_pipelines, type: String, values: %w(disabled enabled), desc: 'Auto-cancel pending pipelines'
optional :build_coverage_regex, type: String, desc: 'Test coverage parsing' optional :build_coverage_regex, type: String, desc: 'Test coverage parsing'
optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`' optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`'
optional :service_desk_enabled, type: Boolean, desc: 'Disable or enable the service desk'
# TODO: remove in API v5, replaced by *_access_level # TODO: remove in API v5, replaced by *_access_level
optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled' optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
...@@ -136,6 +137,7 @@ module API ...@@ -136,6 +137,7 @@ module API
:suggestion_commit_message, :suggestion_commit_message,
:repository_storage, :repository_storage,
:compliance_framework_setting, :compliance_framework_setting,
:service_desk_enabled,
# TODO: remove in API v5, replaced by *_access_level # TODO: remove in API v5, replaced by *_access_level
:issues_enabled, :issues_enabled,
......
# frozen_string_literal: true
module Gitlab
module ServiceDesk
# Check whether a project or GitLab instance can support the Service Desk
# feature. Use `project.service_desk_enabled?` to check whether it is
# enabled for a particular project.
def self.enabled?(project:)
supported? && project[:service_desk_enabled]
end
def self.supported?
Gitlab::IncomingEmail.enabled? && Gitlab::IncomingEmail.supports_wildcard?
end
end
end
...@@ -316,6 +316,14 @@ FactoryBot.define do ...@@ -316,6 +316,14 @@ FactoryBot.define do
end end
end end
trait :service_desk_disabled do
service_desk_enabled { nil }
end
trait(:service_desk_enabled) do
service_desk_enabled { true }
end
# Project with empty repository # Project with empty repository
# #
# This is a case when you just created a project # This is a case when you just created a project
......
...@@ -26,7 +26,7 @@ RSpec.describe GitlabSchema.types['Project'] do ...@@ -26,7 +26,7 @@ RSpec.describe GitlabSchema.types['Project'] do
grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments
boards jira_import_status jira_imports services releases release boards jira_import_status jira_imports services releases release
alert_management_alerts alert_management_alert alert_management_alert_status_counts alert_management_alerts alert_management_alert alert_management_alert_status_counts
container_expiration_policy sast_ci_configuration container_expiration_policy sast_ci_configuration service_desk_enabled service_desk_address
] ]
expect(described_class).to include_graphql_fields(*expected_fields) expect(described_class).to include_graphql_fields(*expected_fields)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe EE::Gitlab::ServiceDesk do RSpec.describe Gitlab::ServiceDesk do
before do before do
allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true) allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true) allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
......
...@@ -406,6 +406,22 @@ RSpec.describe Issue do ...@@ -406,6 +406,22 @@ RSpec.describe Issue do
end end
end end
describe '#from_service_desk?' do
subject { issue.from_service_desk? }
context 'when issue author is support bot' do
let(:issue) { create(:issue, author: ::User.support_bot) }
it { is_expected.to be_truthy }
end
context 'when issue author is not support bot' do
let(:issue) { create(:issue) }
it { is_expected.to be_falsey }
end
end
describe '#suggested_branch_name' do describe '#suggested_branch_name' do
let(:repository) { double } let(:repository) { double }
...@@ -1002,6 +1018,16 @@ RSpec.describe Issue do ...@@ -1002,6 +1018,16 @@ RSpec.describe Issue do
end end
end end
describe '.service_desk' do
it 'returns the service desk issue' do
service_desk_issue = create(:issue, author: ::User.support_bot)
regular_issue = create(:issue)
expect(described_class.service_desk).to include(service_desk_issue)
expect(described_class.service_desk).not_to include(regular_issue)
end
end
it_behaves_like 'throttled touch' do it_behaves_like 'throttled touch' do
subject { create(:issue, updated_at: 1.hour.ago) } subject { create(:issue, updated_at: 1.hour.ago) }
end end
......
...@@ -1378,6 +1378,62 @@ RSpec.describe Project do ...@@ -1378,6 +1378,62 @@ RSpec.describe Project do
end end
end end
describe '.service_desk_enabled' do
it 'returns the correct project' do
project_with_service_desk_enabled = create(:project)
project_with_service_desk_disabled = create(:project, :service_desk_disabled)
expect(described_class.service_desk_enabled).to include(project_with_service_desk_enabled)
expect(described_class.service_desk_enabled).not_to include(project_with_service_desk_disabled)
end
end
describe '#service_desk_enabled?' do
let_it_be(:namespace) { create(:namespace) }
subject(:project) { build(:project, :private, namespace: namespace, service_desk_enabled: true) }
before do
allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true)
allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true)
end
it 'is enabled' do
expect(project.service_desk_enabled?).to be_truthy
expect(project.service_desk_enabled).to be_truthy
end
end
describe '#service_desk_address' do
let_it_be(:project) { create(:project, service_desk_enabled: true) }
before do
allow(Gitlab::ServiceDesk).to receive(:enabled?).and_return(true)
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
allow(Gitlab.config.incoming_email).to receive(:address).and_return("test+%{key}@mail.com")
end
it 'uses project full path as service desk address key' do
expect(project.service_desk_address).to eq("test+#{project.full_path_slug}-#{project.project_id}-issue-@mail.com")
end
end
describe '.find_by_service_desk_project_key' do
it 'returns the correct project' do
project1 = create(:project)
project2 = create(:project)
create(:service_desk_setting, project: project1, project_key: 'key1')
create(:service_desk_setting, project: project2, project_key: 'key2')
expect(Project.find_by_service_desk_project_key('key1')).to eq(project1)
expect(Project.find_by_service_desk_project_key('key2')).to eq(project2)
end
it 'returns nil if there is no project with the key' do
expect(Project.find_by_service_desk_project_key('some_key')).to be_nil
end
end
context 'repository storage by default' do context 'repository storage by default' do
let(:project) { build(:project) } let(:project) { build(:project) }
......
...@@ -113,6 +113,10 @@ module StubConfiguration ...@@ -113,6 +113,10 @@ module StubConfiguration
allow(Gitlab.config.rack_attack.git_basic_auth).to receive_messages(to_settings(messages)) allow(Gitlab.config.rack_attack.git_basic_auth).to receive_messages(to_settings(messages))
end end
def stub_service_desk_email_setting(messages)
allow(::Gitlab.config.service_desk_email).to receive_messages(to_settings(messages))
end
private private
# Modifies stubbed messages to also stub possible predicate versions # Modifies stubbed messages to also stub possible predicate versions
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment