Commit 4596c5ee authored by charlie ablett's avatar charlie ablett

Merge branch '201855-rename-project_services_to_integrations-3' into 'master'

Move issue tracker integrations to the `Integrations::` namespace

See merge request gitlab-org/gitlab!61744
parents 03bee06c 6bec51ff
...@@ -1655,21 +1655,17 @@ Gitlab/NamespacedClass: ...@@ -1655,21 +1655,17 @@ Gitlab/NamespacedClass:
- 'app/models/project_repository_storage_move.rb' - 'app/models/project_repository_storage_move.rb'
- 'app/models/project_services/alerts_service.rb' - 'app/models/project_services/alerts_service.rb'
- 'app/models/project_services/alerts_service_data.rb' - 'app/models/project_services/alerts_service_data.rb'
- 'app/models/project_services/bugzilla_service.rb'
- 'app/models/project_services/buildkite_service.rb' - 'app/models/project_services/buildkite_service.rb'
- 'app/models/project_services/chat_notification_service.rb' - 'app/models/project_services/chat_notification_service.rb'
- 'app/models/project_services/ci_service.rb' - 'app/models/project_services/ci_service.rb'
- 'app/models/project_services/custom_issue_tracker_service.rb'
- 'app/models/project_services/discord_service.rb' - 'app/models/project_services/discord_service.rb'
- 'app/models/project_services/drone_ci_service.rb' - 'app/models/project_services/drone_ci_service.rb'
- 'app/models/project_services/ewm_service.rb'
- 'app/models/project_services/external_wiki_service.rb' - 'app/models/project_services/external_wiki_service.rb'
- 'app/models/project_services/flowdock_service.rb' - 'app/models/project_services/flowdock_service.rb'
- 'app/models/project_services/hangouts_chat_service.rb' - 'app/models/project_services/hangouts_chat_service.rb'
- 'app/models/project_services/hipchat_service.rb' - 'app/models/project_services/hipchat_service.rb'
- 'app/models/project_services/irker_service.rb' - 'app/models/project_services/irker_service.rb'
- 'app/models/project_services/issue_tracker_data.rb' - 'app/models/project_services/issue_tracker_data.rb'
- 'app/models/project_services/issue_tracker_service.rb'
- 'app/models/project_services/jenkins_service.rb' - 'app/models/project_services/jenkins_service.rb'
- 'app/models/project_services/jira_service.rb' - 'app/models/project_services/jira_service.rb'
- 'app/models/project_services/jira_tracker_data.rb' - 'app/models/project_services/jira_tracker_data.rb'
...@@ -1679,21 +1675,18 @@ Gitlab/NamespacedClass: ...@@ -1679,21 +1675,18 @@ Gitlab/NamespacedClass:
- 'app/models/project_services/mock_ci_service.rb' - 'app/models/project_services/mock_ci_service.rb'
- 'app/models/project_services/mock_monitoring_service.rb' - 'app/models/project_services/mock_monitoring_service.rb'
- 'app/models/project_services/monitoring_service.rb' - 'app/models/project_services/monitoring_service.rb'
- 'app/models/project_services/open_project_service.rb'
- 'app/models/project_services/open_project_tracker_data.rb' - 'app/models/project_services/open_project_tracker_data.rb'
- 'app/models/project_services/packagist_service.rb' - 'app/models/project_services/packagist_service.rb'
- 'app/models/project_services/pipelines_email_service.rb' - 'app/models/project_services/pipelines_email_service.rb'
- 'app/models/project_services/pivotaltracker_service.rb' - 'app/models/project_services/pivotaltracker_service.rb'
- 'app/models/project_services/prometheus_service.rb' - 'app/models/project_services/prometheus_service.rb'
- 'app/models/project_services/pushover_service.rb' - 'app/models/project_services/pushover_service.rb'
- 'app/models/project_services/redmine_service.rb'
- 'app/models/project_services/slack_service.rb' - 'app/models/project_services/slack_service.rb'
- 'app/models/project_services/slack_slash_commands_service.rb' - 'app/models/project_services/slack_slash_commands_service.rb'
- 'app/models/project_services/slash_commands_service.rb' - 'app/models/project_services/slash_commands_service.rb'
- 'app/models/project_services/teamcity_service.rb' - 'app/models/project_services/teamcity_service.rb'
- 'app/models/project_services/unify_circuit_service.rb' - 'app/models/project_services/unify_circuit_service.rb'
- 'app/models/project_services/webex_teams_service.rb' - 'app/models/project_services/webex_teams_service.rb'
- 'app/models/project_services/youtrack_service.rb'
- 'app/models/project_setting.rb' - 'app/models/project_setting.rb'
- 'app/models/project_snippet.rb' - 'app/models/project_snippet.rb'
- 'app/models/project_statistics.rb' - 'app/models/project_statistics.rb'
......
...@@ -152,7 +152,7 @@ Lint/MissingCopEnableDirective: ...@@ -152,7 +152,7 @@ Lint/MissingCopEnableDirective:
Lint/MixedRegexpCaptureTypes: Lint/MixedRegexpCaptureTypes:
Exclude: Exclude:
- 'app/models/alert_management/alert.rb' - 'app/models/alert_management/alert.rb'
- 'app/models/project_services/ewm_service.rb' - 'app/models/integrations/ewm.rb'
- 'app/uploaders/file_uploader.rb' - 'app/uploaders/file_uploader.rb'
- 'ee/lib/gitlab/code_owners/reference_extractor.rb' - 'ee/lib/gitlab/code_owners/reference_extractor.rb'
- 'lib/gitlab/ci/pipeline/expression/lexeme/string.rb' - 'lib/gitlab/ci/pipeline/expression/lexeme/string.rb'
......
...@@ -29,7 +29,7 @@ module Mentionable ...@@ -29,7 +29,7 @@ module Mentionable
def self.external_pattern def self.external_pattern
strong_memoize(:external_pattern) do strong_memoize(:external_pattern) do
issue_pattern = IssueTrackerService.reference_pattern issue_pattern = Integrations::IssueTracker.reference_pattern
link_patterns = URI::DEFAULT_PARSER.make_regexp(%w(http https)) link_patterns = URI::DEFAULT_PARSER.make_regexp(%w(http https))
reference_pattern(link_patterns, issue_pattern) reference_pattern(link_patterns, issue_pattern)
end end
......
# frozen_string_literal: true
module Integrations
class Bugzilla < IssueTracker
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def title
'Bugzilla'
end
def description
s_("IssueTracker|Use Bugzilla as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/bugzilla'), target: '_blank', rel: 'noopener noreferrer'
s_("IssueTracker|Use Bugzilla as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'bugzilla'
end
end
end
# frozen_string_literal: true
module Integrations
class CustomIssueTracker < IssueTracker
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def title
s_('IssueTracker|Custom issue tracker')
end
def description
s_("IssueTracker|Use a custom issue tracker as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/custom_issue_tracker'), target: '_blank', rel: 'noopener noreferrer'
s_('IssueTracker|Use a custom issue tracker that is not in the integration list. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'custom_issue_tracker'
end
end
end
# frozen_string_literal: true
module Integrations
class Ewm < IssueTracker
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def self.reference_pattern(only_long: true)
@reference_pattern ||= %r{(?<issue>\b(bug|task|work item|workitem|rtcwi|defect)\b\s+\d+)}i
end
def title
'EWM'
end
def description
s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/ewm'), target: '_blank', rel: 'noopener noreferrer'
s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'ewm'
end
def can_test?
false
end
def issue_url(iid)
issues_url.gsub(':id', iid.to_s.split(' ')[-1])
end
end
end
# frozen_string_literal: true
module Integrations
class IssueTracker < Integration
validate :one_issue_tracker, if: :activated?, on: :manual_change
# TODO: we can probably just delegate as part of
# https://gitlab.com/gitlab-org/gitlab/issues/29404
data_field :project_url, :issues_url, :new_issue_url
default_value_for :category, 'issue_tracker'
before_validation :handle_properties
before_validation :set_default_data, on: :create
# Pattern used to extract links from comments
# Override this method on services that uses different patterns
# This pattern does not support cross-project references
# The other code assumes that this pattern is a superset of all
# overridden patterns. See ReferenceRegexes.external_pattern
def self.reference_pattern(only_long: false)
if only_long
/(\b[A-Z][A-Z0-9_]*-)#{Gitlab::Regex.issue}/
else
/(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})#{Gitlab::Regex.issue}/
end
end
def handle_properties
# this has been moved from initialize_properties and should be improved
# as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
return unless properties
@legacy_properties_data = properties.dup
data_values = properties.slice!('title', 'description')
data_values.reject! { |key| data_fields.changed.include?(key) }
data_values.slice!(*data_fields.attributes.keys)
data_fields.assign_attributes(data_values) if data_values.present?
self.properties = {}
end
def legacy_properties_data
@legacy_properties_data ||= {}
end
def supports_data_fields?
true
end
def data_fields
issue_tracker_data || self.build_issue_tracker_data
end
def default?
default
end
def issue_url(iid)
issues_url.gsub(':id', iid.to_s)
end
def issue_tracker_path
project_url
end
def new_issue_path
new_issue_url
end
def issue_path(iid)
issue_url(iid)
end
def fields
[
{ type: 'text', name: 'project_url', title: _('Project URL'), help: s_('IssueTracker|The URL to the project in the external issue tracker.'), required: true },
{ type: 'text', name: 'issues_url', title: s_('IssueTracker|Issue URL'), help: s_('IssueTracker|The URL to view an issue in the external issue tracker. Must contain %{colon_id}.') % { colon_id: '<code>:id</code>'.html_safe }, required: true },
{ type: 'text', name: 'new_issue_url', title: s_('IssueTracker|New issue URL'), help: s_('IssueTracker|The URL to create an issue in the external issue tracker.'), required: true }
]
end
def initialize_properties
{}
end
# Initialize with default properties values
def set_default_data
return unless issues_tracker.present?
# we don't want to override if we have set something
return if project_url || issues_url || new_issue_url
data_fields.project_url = issues_tracker['project_url']
data_fields.issues_url = issues_tracker['issues_url']
data_fields.new_issue_url = issues_tracker['new_issue_url']
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again."
result = false
begin
response = Gitlab::HTTP.head(self.project_url, verify: true)
if response
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true
end
rescue Gitlab::HTTP::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error
message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
end
log_info(message)
result
end
def support_close_issue?
false
end
def support_cross_reference?
false
end
private
def enabled_in_gitlab_config
Gitlab.config.issues_tracker &&
Gitlab.config.issues_tracker.values.any? &&
issues_tracker
end
def issues_tracker
Gitlab.config.issues_tracker[to_param]
end
def one_issue_tracker
return if template? || instance?
return if project.blank?
if project.integrations.external_issue_trackers.where.not(id: id).any?
errors.add(:base, _('Another issue tracker is already in use. Only one issue tracker service can be active at a time'))
end
end
end
end
Integrations::IssueTracker.prepend_mod_with('Integrations::IssueTracker')
# frozen_string_literal: true
module Integrations
class OpenProject < IssueTracker
validates :url, public_url: true, presence: true, if: :activated?
validates :api_url, public_url: true, allow_blank: true, if: :activated?
validates :token, presence: true, if: :activated?
validates :project_identifier_code, presence: true, if: :activated?
data_field :url, :api_url, :token, :closed_status_id, :project_identifier_code
def data_fields
open_project_tracker_data || self.build_open_project_tracker_data
end
def self.to_param
'open_project'
end
end
end
# frozen_string_literal: true
module Integrations
class Redmine < IssueTracker
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def title
'Redmine'
end
def description
s_("IssueTracker|Use Redmine as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/redmine'), target: '_blank', rel: 'noopener noreferrer'
s_('IssueTracker|Use Redmine as the issue tracker. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'redmine'
end
end
end
# frozen_string_literal: true
module Integrations
class Youtrack < IssueTracker
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, presence: true, public_url: true, if: :activated?
# {PROJECT-KEY}-{NUMBER} Examples: YT-1, PRJ-1, gl-030
def self.reference_pattern(only_long: false)
if only_long
/(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)/
else
/(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)|(#{Issue.reference_prefix}#{Gitlab::Regex.issue})/
end
end
def title
'YouTrack'
end
def description
s_("IssueTracker|Use YouTrack as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/youtrack'), target: '_blank', rel: 'noopener noreferrer'
s_("IssueTracker|Use YouTrack as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'youtrack'
end
def fields
[
{ type: 'text', name: 'project_url', title: _('Project URL'), help: s_('IssueTracker|The URL to the project in YouTrack.'), required: true },
{ type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), help: s_('IssueTracker|The URL to view an issue in the YouTrack project. Must contain %{colon_id}.') % { colon_id: '<code>:id</code>'.html_safe }, required: true }
]
end
end
end
...@@ -186,13 +186,17 @@ class Project < ApplicationRecord ...@@ -186,13 +186,17 @@ class Project < ApplicationRecord
has_one :asana_service, class_name: 'Integrations::Asana' has_one :asana_service, class_name: 'Integrations::Asana'
has_one :assembla_service, class_name: 'Integrations::Assembla' has_one :assembla_service, class_name: 'Integrations::Assembla'
has_one :bamboo_service, class_name: 'Integrations::Bamboo' has_one :bamboo_service, class_name: 'Integrations::Bamboo'
has_one :bugzilla_service, class_name: 'Integrations::Bugzilla'
has_one :campfire_service, class_name: 'Integrations::Campfire' has_one :campfire_service, class_name: 'Integrations::Campfire'
has_one :confluence_service, class_name: 'Integrations::Confluence' has_one :confluence_service, class_name: 'Integrations::Confluence'
has_one :custom_issue_tracker_service, class_name: 'Integrations::CustomIssueTracker'
has_one :datadog_service, class_name: 'Integrations::Datadog' has_one :datadog_service, class_name: 'Integrations::Datadog'
has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush' has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush'
has_one :ewm_service, class_name: 'Integrations::Ewm'
has_one :redmine_service, class_name: 'Integrations::Redmine'
has_one :youtrack_service, class_name: 'Integrations::Youtrack'
has_one :discord_service has_one :discord_service
has_one :drone_ci_service has_one :drone_ci_service
has_one :ewm_service
has_one :pipelines_email_service has_one :pipelines_email_service
has_one :irker_service has_one :irker_service
has_one :pivotaltracker_service has_one :pivotaltracker_service
...@@ -206,10 +210,6 @@ class Project < ApplicationRecord ...@@ -206,10 +210,6 @@ class Project < ApplicationRecord
has_one :pushover_service has_one :pushover_service
has_one :jenkins_service has_one :jenkins_service
has_one :jira_service has_one :jira_service
has_one :redmine_service
has_one :youtrack_service
has_one :custom_issue_tracker_service
has_one :bugzilla_service
has_one :external_wiki_service has_one :external_wiki_service
has_one :prometheus_service, inverse_of: :project has_one :prometheus_service, inverse_of: :project
has_one :mock_ci_service has_one :mock_ci_service
......
# frozen_string_literal: true
class BugzillaService < IssueTrackerService
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def title
'Bugzilla'
end
def description
s_("IssueTracker|Use Bugzilla as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/bugzilla'), target: '_blank', rel: 'noopener noreferrer'
s_("IssueTracker|Use Bugzilla as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'bugzilla'
end
end
# frozen_string_literal: true
class CustomIssueTrackerService < IssueTrackerService
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def title
s_('IssueTracker|Custom issue tracker')
end
def description
s_("IssueTracker|Use a custom issue tracker as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/custom_issue_tracker'), target: '_blank', rel: 'noopener noreferrer'
s_('IssueTracker|Use a custom issue tracker that is not in the integration list. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'custom_issue_tracker'
end
end
# frozen_string_literal: true
class EwmService < IssueTrackerService
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def self.reference_pattern(only_long: true)
@reference_pattern ||= %r{(?<issue>\b(bug|task|work item|workitem|rtcwi|defect)\b\s+\d+)}i
end
def title
'EWM'
end
def description
s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/ewm'), target: '_blank', rel: 'noopener noreferrer'
s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'ewm'
end
def can_test?
false
end
def issue_url(iid)
issues_url.gsub(':id', iid.to_s.split(' ')[-1])
end
end
# frozen_string_literal: true
class IssueTrackerService < Integration
validate :one_issue_tracker, if: :activated?, on: :manual_change
# TODO: we can probably just delegate as part of
# https://gitlab.com/gitlab-org/gitlab/issues/29404
data_field :project_url, :issues_url, :new_issue_url
default_value_for :category, 'issue_tracker'
before_validation :handle_properties
before_validation :set_default_data, on: :create
# Pattern used to extract links from comments
# Override this method on services that uses different patterns
# This pattern does not support cross-project references
# The other code assumes that this pattern is a superset of all
# overridden patterns. See ReferenceRegexes.external_pattern
def self.reference_pattern(only_long: false)
if only_long
/(\b[A-Z][A-Z0-9_]*-)#{Gitlab::Regex.issue}/
else
/(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})#{Gitlab::Regex.issue}/
end
end
def handle_properties
# this has been moved from initialize_properties and should be improved
# as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
return unless properties
@legacy_properties_data = properties.dup
data_values = properties.slice!('title', 'description')
data_values.reject! { |key| data_fields.changed.include?(key) }
data_values.slice!(*data_fields.attributes.keys)
data_fields.assign_attributes(data_values) if data_values.present?
self.properties = {}
end
def legacy_properties_data
@legacy_properties_data ||= {}
end
def supports_data_fields?
true
end
def data_fields
issue_tracker_data || self.build_issue_tracker_data
end
def default?
default
end
def issue_url(iid)
issues_url.gsub(':id', iid.to_s)
end
def issue_tracker_path
project_url
end
def new_issue_path
new_issue_url
end
def issue_path(iid)
issue_url(iid)
end
def fields
[
{ type: 'text', name: 'project_url', title: _('Project URL'), help: s_('IssueTracker|The URL to the project in the external issue tracker.'), required: true },
{ type: 'text', name: 'issues_url', title: s_('IssueTracker|Issue URL'), help: s_('IssueTracker|The URL to view an issue in the external issue tracker. Must contain %{colon_id}.') % { colon_id: '<code>:id</code>'.html_safe }, required: true },
{ type: 'text', name: 'new_issue_url', title: s_('IssueTracker|New issue URL'), help: s_('IssueTracker|The URL to create an issue in the external issue tracker.'), required: true }
]
end
def initialize_properties
{}
end
# Initialize with default properties values
def set_default_data
return unless issues_tracker.present?
# we don't want to override if we have set something
return if project_url || issues_url || new_issue_url
data_fields.project_url = issues_tracker['project_url']
data_fields.issues_url = issues_tracker['issues_url']
data_fields.new_issue_url = issues_tracker['new_issue_url']
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again."
result = false
begin
response = Gitlab::HTTP.head(self.project_url, verify: true)
if response
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true
end
rescue Gitlab::HTTP::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error
message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
end
log_info(message)
result
end
def support_close_issue?
false
end
def support_cross_reference?
false
end
private
def enabled_in_gitlab_config
Gitlab.config.issues_tracker &&
Gitlab.config.issues_tracker.values.any? &&
issues_tracker
end
def issues_tracker
Gitlab.config.issues_tracker[to_param]
end
def one_issue_tracker
return if template? || instance?
return if project.blank?
if project.integrations.external_issue_trackers.where.not(id: id).any?
errors.add(:base, _('Another issue tracker is already in use. Only one issue tracker service can be active at a time'))
end
end
end
IssueTrackerService.prepend_mod_with('IssueTrackerService')
# frozen_string_literal: true # frozen_string_literal: true
# Accessible as Project#external_issue_tracker # Accessible as Project#external_issue_tracker
class JiraService < IssueTrackerService class JiraService < Integrations::IssueTracker
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
include Gitlab::Routing include Gitlab::Routing
include ApplicationHelper include ApplicationHelper
......
# frozen_string_literal: true
class OpenProjectService < IssueTrackerService
validates :url, public_url: true, presence: true, if: :activated?
validates :api_url, public_url: true, allow_blank: true, if: :activated?
validates :token, presence: true, if: :activated?
validates :project_identifier_code, presence: true, if: :activated?
data_field :url, :api_url, :token, :closed_status_id, :project_identifier_code
def data_fields
open_project_tracker_data || self.build_open_project_tracker_data
end
def self.to_param
'open_project'
end
end
# frozen_string_literal: true
class RedmineService < IssueTrackerService
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def title
'Redmine'
end
def description
s_("IssueTracker|Use Redmine as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/redmine'), target: '_blank', rel: 'noopener noreferrer'
s_('IssueTracker|Use Redmine as the issue tracker. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'redmine'
end
end
# frozen_string_literal: true
class YoutrackService < IssueTrackerService
include ActionView::Helpers::UrlHelper
validates :project_url, :issues_url, presence: true, public_url: true, if: :activated?
# {PROJECT-KEY}-{NUMBER} Examples: YT-1, PRJ-1, gl-030
def self.reference_pattern(only_long: false)
if only_long
/(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)/
else
/(?<issue>\b[A-Za-z][A-Za-z0-9_]*-\d+\b)|(#{Issue.reference_prefix}#{Gitlab::Regex.issue})/
end
end
def title
'YouTrack'
end
def description
s_("IssueTracker|Use YouTrack as this project's issue tracker.")
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/youtrack'), target: '_blank', rel: 'noopener noreferrer'
s_("IssueTracker|Use YouTrack as this project's issue tracker. %{docs_link}").html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'youtrack'
end
def fields
[
{ type: 'text', name: 'project_url', title: _('Project URL'), help: s_('IssueTracker|The URL to the project in YouTrack.'), required: true },
{ type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), help: s_('IssueTracker|The URL to view an issue in the YouTrack project. Must contain %{colon_id}.') % { colon_id: '<code>:id</code>'.html_safe }, required: true }
]
end
end
# frozen_string_literal: true # frozen_string_literal: true
module EE module EE
module IssueTrackerService module Integrations
module IssueTracker
def create_cross_reference_note def create_cross_reference_note
# implement inside child # implement inside child
end end
end end
end
end end
...@@ -777,16 +777,18 @@ module API ...@@ -777,16 +777,18 @@ module API
::Integrations::Asana, ::Integrations::Asana,
::Integrations::Assembla, ::Integrations::Assembla,
::Integrations::Bamboo, ::Integrations::Bamboo,
::Integrations::Bugzilla,
::Integrations::Campfire, ::Integrations::Campfire,
::Integrations::Confluence, ::Integrations::Confluence,
::Integrations::CustomIssueTracker,
::Integrations::Datadog, ::Integrations::Datadog,
::Integrations::EmailsOnPush, ::Integrations::EmailsOnPush,
::BugzillaService, ::Integrations::Ewm,
::Integrations::Redmine,
::Integrations::Youtrack,
::BuildkiteService, ::BuildkiteService,
::CustomIssueTrackerService,
::DiscordService, ::DiscordService,
::DroneCiService, ::DroneCiService,
::EwmService,
::ExternalWikiService, ::ExternalWikiService,
::FlowdockService, ::FlowdockService,
::HangoutsChatService, ::HangoutsChatService,
...@@ -800,8 +802,6 @@ module API ...@@ -800,8 +802,6 @@ module API
::PivotaltrackerService, ::PivotaltrackerService,
::PrometheusService, ::PrometheusService,
::PushoverService, ::PushoverService,
::RedmineService,
::YoutrackService,
::SlackService, ::SlackService,
::MattermostService, ::MattermostService,
::MicrosoftTeamsService, ::MicrosoftTeamsService,
......
...@@ -4,7 +4,8 @@ module Gitlab ...@@ -4,7 +4,8 @@ module Gitlab
module Integrations module Integrations
class StiType < ActiveRecord::Type::String class StiType < ActiveRecord::Type::String
NAMESPACED_INTEGRATIONS = Set.new(%w( NAMESPACED_INTEGRATIONS = Set.new(%w(
Asana Assembla Bamboo Campfire Confluence Datadog EmailsOnPush Asana Assembla Bamboo Bugzilla Campfire Confluence CustomIssueTracker Datadog
EmailsOnPush Ewm IssueTracker Redmine Youtrack
)).freeze )).freeze
def cast(value) def cast(value)
......
...@@ -44,7 +44,7 @@ RSpec.describe Admin::ServicesController do ...@@ -44,7 +44,7 @@ RSpec.describe Admin::ServicesController do
describe "#update" do describe "#update" do
let(:project) { create(:project) } let(:project) { create(:project) }
let!(:service_template) do let!(:service_template) do
RedmineService.create!( Integrations::Redmine.create!(
project: nil, project: nil,
active: false, active: false,
template: true, template: true,
......
...@@ -6,7 +6,7 @@ FactoryBot.define do ...@@ -6,7 +6,7 @@ FactoryBot.define do
type { 'Integration' } type { 'Integration' }
end end
factory :custom_issue_tracker_service, class: 'CustomIssueTrackerService' do factory :custom_issue_tracker_service, class: 'Integrations::CustomIssueTracker' do
project project
active { true } active { true }
issue_tracker issue_tracker
...@@ -85,25 +85,25 @@ FactoryBot.define do ...@@ -85,25 +85,25 @@ FactoryBot.define do
confluence_url { 'https://example.atlassian.net/wiki' } confluence_url { 'https://example.atlassian.net/wiki' }
end end
factory :bugzilla_service do factory :bugzilla_service, class: 'Integrations::Bugzilla' do
project project
active { true } active { true }
issue_tracker issue_tracker
end end
factory :redmine_service do factory :redmine_service, class: 'Integrations::Redmine' do
project project
active { true } active { true }
issue_tracker issue_tracker
end end
factory :youtrack_service do factory :youtrack_service, class: 'Integrations::Youtrack' do
project project
active { true } active { true }
issue_tracker issue_tracker
end end
factory :ewm_service do factory :ewm_service, class: 'Integrations::Ewm' do
project project
active { true } active { true }
issue_tracker issue_tracker
...@@ -134,7 +134,7 @@ FactoryBot.define do ...@@ -134,7 +134,7 @@ FactoryBot.define do
external_wiki_url { 'http://external-wiki-url.com' } external_wiki_url { 'http://external-wiki-url.com' }
end end
factory :open_project_service do factory :open_project_service, class: 'Integrations::OpenProject' do
project project
active { true } active { true }
...@@ -182,13 +182,13 @@ FactoryBot.define do ...@@ -182,13 +182,13 @@ FactoryBot.define do
create_data { false } create_data { false }
after(:build) do after(:build) do
IssueTrackerService.skip_callback(:validation, :before, :handle_properties) Integrations::IssueTracker.skip_callback(:validation, :before, :handle_properties)
end end
to_create { |instance| instance.save!(validate: false) } to_create { |instance| instance.save!(validate: false) }
after(:create) do after(:create) do
IssueTrackerService.set_callback(:validation, :before, :handle_properties) Integrations::IssueTracker.set_callback(:validation, :before, :handle_properties)
end end
end end
......
...@@ -159,7 +159,7 @@ RSpec.describe Integration do ...@@ -159,7 +159,7 @@ RSpec.describe Integration do
context 'when instance-level service' do context 'when instance-level service' do
Integration.available_services_types.each do |service_type| Integration.available_services_types.each do |service_type|
let(:service) do let(:service) do
service_type.constantize.new(instance: true) described_class.send(:service_type_to_model, service_type).new(instance: true)
end end
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
...@@ -169,7 +169,7 @@ RSpec.describe Integration do ...@@ -169,7 +169,7 @@ RSpec.describe Integration do
context 'when group-level service' do context 'when group-level service' do
Integration.available_services_types.each do |service_type| Integration.available_services_types.each do |service_type|
let(:service) do let(:service) do
service_type.constantize.new(group_id: group.id) described_class.send(:service_type_to_model, service_type).new(group_id: group.id)
end end
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
...@@ -672,7 +672,7 @@ RSpec.describe Integration do ...@@ -672,7 +672,7 @@ RSpec.describe Integration do
expect(described_class.service_name_to_model('asana')).to eq(Integrations::Asana) expect(described_class.service_name_to_model('asana')).to eq(Integrations::Asana)
# TODO We can remove this test when all models have been namespaced: # TODO We can remove this test when all models have been namespaced:
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60968#note_570994955 # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60968#note_570994955
expect(described_class.service_name_to_model('youtrack')).to eq(YoutrackService) expect(described_class.service_name_to_model('webex_teams')).to eq(WebexTeamsService)
end end
it 'raises an error if service name is invalid' do it 'raises an error if service name is invalid' do
...@@ -802,7 +802,7 @@ RSpec.describe Integration do ...@@ -802,7 +802,7 @@ RSpec.describe Integration do
describe 'initialize service with no properties' do describe 'initialize service with no properties' do
let(:service) do let(:service) do
BugzillaService.create!( Integrations::Bugzilla.create!(
project: project, project: project,
project_url: 'http://gitlab.example.com' project_url: 'http://gitlab.example.com'
) )
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe BugzillaService do RSpec.describe Integrations::Bugzilla do
describe 'Associations' do describe 'Associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe CustomIssueTrackerService do RSpec.describe Integrations::CustomIssueTracker do
describe 'Associations' do describe 'Associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe EwmService do RSpec.describe Integrations::Ewm do
describe 'Associations' do describe 'Associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe IssueTrackerService do RSpec.describe Integrations::IssueTracker do
describe 'Validations' do describe 'Validations' do
let(:project) { create :project } let(:project) { create :project }
describe 'only one issue tracker per project' do describe 'only one issue tracker per project' do
let(:service) { RedmineService.new(project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) } let(:service) { Integrations::Redmine.new(project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) }
before do before do
create(:custom_issue_tracker_service, project: project) create(:custom_issue_tracker_service, project: project)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe OpenProjectService do RSpec.describe Integrations::OpenProject do
describe 'Validations' do describe 'Validations' do
context 'when service is active' do context 'when service is active' do
before do before do
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe RedmineService do RSpec.describe Integrations::Redmine do
describe 'Associations' do describe 'Associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe YoutrackService do RSpec.describe Integrations::Youtrack do
describe 'Associations' do describe 'Associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
......
...@@ -1086,7 +1086,7 @@ RSpec.describe Project, factory_default: :keep do ...@@ -1086,7 +1086,7 @@ RSpec.describe Project, factory_default: :keep do
project = create(:redmine_project) project = create(:redmine_project)
expect(project).to receive(:integrations).once.and_call_original expect(project).to receive(:integrations).once.and_call_original
2.times { expect(project.external_issue_tracker).to be_a_kind_of(RedmineService) } 2.times { expect(project.external_issue_tracker).to be_a_kind_of(Integrations::Redmine) }
end end
end end
......
...@@ -23,7 +23,7 @@ RSpec.describe 'projects/services/_form' do ...@@ -23,7 +23,7 @@ RSpec.describe 'projects/services/_form' do
end end
it 'display merge_request_events and commit_events descriptions' do it 'display merge_request_events and commit_events descriptions' do
allow(RedmineService).to receive(:supported_events).and_return(%w(commit merge_request)) allow(Integrations::Redmine).to receive(:supported_events).and_return(%w(commit merge_request))
render render
......
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