Commit 3fbe1e3b authored by Lukas 'Eipi' Eipert's avatar Lukas 'Eipi' Eipert Committed by Heinrich Lee Yu

Move Jira Development Panel to Core

This moves the following things to Core:
- Jira DVCS Connector
- Jira Connect App
- Usage ping for those two features

and removes any license check related to them.
Co-authored-by: default avatarCraig Norris <cnorris@gitlab.com>
parent 16b88736
......@@ -116,10 +116,12 @@ linters:
- "app/views/import/bitbucket/status.html.haml"
- "app/views/import/bitbucket_server/status.html.haml"
- "app/views/invites/show.html.haml"
- "app/views/jira_connect/subscriptions/index.html.haml"
- "app/views/layouts/_mailer.html.haml"
- "app/views/layouts/experiment_mailer.html.haml"
- "app/views/layouts/header/_default.html.haml"
- "app/views/layouts/header/_new_dropdown.haml"
- "app/views/layouts/jira_connect.html.haml"
- "app/views/layouts/notify.html.haml"
- "app/views/notify/_failed_builds.html.haml"
- "app/views/notify/_reassigned_issuable_email.html.haml"
......@@ -333,8 +335,6 @@ linters:
- "ee/app/views/groups/group_members/_sync_button.html.haml"
- "ee/app/views/groups/hooks/edit.html.haml"
- "ee/app/views/groups/ldap_group_links/index.html.haml"
- "ee/app/views/jira_connect/subscriptions/index.html.haml"
- "ee/app/views/layouts/jira_connect.html.haml"
- "ee/app/views/layouts/nav/ee/admin/_new_monitoring_sidebar.html.haml"
- "ee/app/views/layouts/service_desk.html.haml"
- "ee/app/views/ldap_group_links/_form.html.haml"
......
......@@ -4,7 +4,7 @@
/**
* This script is not going through Webpack bundling
* as it is only included in `ee/app/views/jira_connect/subscriptions/index.html.haml`
* as it is only included in `app/views/jira_connect/subscriptions/index.html.haml`
* which is going to be rendered within iframe on Jira app dashboard
* hence any code written here needs to be IE11+ compatible (no fully ES6)
*/
......
......@@ -254,6 +254,7 @@ class Project < ApplicationRecord
has_one :import_data, class_name: 'ProjectImportData', inverse_of: :project, autosave: true
has_one :project_feature, inverse_of: :project
has_one :statistics, class_name: 'ProjectStatistics'
has_one :feature_usage, class_name: 'ProjectFeatureUsage'
has_one :cluster_project, class_name: 'Clusters::Project'
has_many :clusters, through: :cluster_project, class_name: 'Clusters::Cluster'
......@@ -393,6 +394,8 @@ class Project < ApplicationRecord
to: :project_setting
delegate :active?, to: :prometheus_service, allow_nil: true, prefix: true
delegate :log_jira_dvcs_integration_usage, :jira_dvcs_server_last_sync_at, :jira_dvcs_cloud_last_sync_at, to: :feature_usage
# Validations
validates :creator, presence: true, on: :create
validates :description, length: { maximum: 2000 }, allow_blank: true
......@@ -476,6 +479,9 @@ class Project < ApplicationRecord
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
scope :with_push, -> { joins(:events).merge(Event.pushed_action) }
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
scope :with_active_jira_services, -> { joins(:services).merge(::JiraService.active) } # rubocop:disable CodeReuse/ServiceClass
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 :inc_routes, -> { includes(:route, namespace: :route) }
scope :with_statistics, -> { includes(:statistics) }
scope :with_namespace, -> { includes(:namespace) }
......@@ -1444,6 +1450,10 @@ class Project < ApplicationRecord
http_url_to_repo
end
def feature_usage
super.presence || build_feature_usage
end
def forked?
fork_network && fork_network.root_project != self
end
......@@ -2426,6 +2436,10 @@ class Project < ApplicationRecord
false
end
def jira_subscription_exists?
JiraConnectSubscription.for_project(self).exists?
end
def uses_default_ci_config?
ci_config_path.blank? || ci_config_path == Gitlab::FileDetector::PATTERNS[:gitlab_ci]
end
......
......@@ -116,6 +116,7 @@ class GroupPolicy < BasePolicy
enable :update_cluster
enable :admin_cluster
enable :read_deploy_token
enable :create_jira_connect_subscription
end
rule { owner }.policy do
......
......@@ -12,6 +12,7 @@ class NamespacePolicy < BasePolicy
enable :admin_namespace
enable :read_namespace
enable :read_statistics
enable :create_jira_connect_subscription
end
rule { personal_project & ~can_create_personal_project }.prevent :create_projects
......
......@@ -75,6 +75,7 @@ module Git
def branch_change_hooks
enqueue_process_commit_messages
enqueue_jira_connect_sync_messages
end
def branch_remove_hooks
......@@ -103,6 +104,17 @@ module Git
end
end
def enqueue_jira_connect_sync_messages
return unless project.jira_subscription_exists?
branch_to_sync = branch_name if Atlassian::JiraIssueKeyExtractor.has_keys?(branch_name)
commits_to_sync = limited_commits.select { |commit| Atlassian::JiraIssueKeyExtractor.has_keys?(commit.safe_message) }.map(&:sha)
if branch_to_sync || commits_to_sync.any?
JiraConnect::SyncBranchWorker.perform_async(project.id, branch_to_sync, commits_to_sync)
end
end
def unsigned_x509_shas(commits)
X509CommitSignature.unsigned_commit_shas(commits.map(&:sha))
end
......
......@@ -9,8 +9,6 @@ module JiraConnectSubscriptions
return error('Invalid namespace. Please make sure you have sufficient permissions', 401)
end
return error('This feature is not available', 422) unless namespace.feature_available?(:jira_dev_panel_integration)
create_subscription
end
......
......@@ -23,6 +23,8 @@ module MergeRequests
merge_data = hook_data(merge_request, action, old_rev: old_rev, old_associations: old_associations)
merge_request.project.execute_hooks(merge_data, :merge_request_hooks)
merge_request.project.execute_services(merge_data, :merge_request_hooks)
enqueue_jira_connect_messages_for(merge_request)
end
def cleanup_environments(merge_request)
......@@ -52,6 +54,14 @@ module MergeRequests
private
def enqueue_jira_connect_messages_for(merge_request)
return unless project.jira_subscription_exists?
if Atlassian::JiraIssueKeyExtractor.has_keys?(merge_request.title, merge_request.description)
JiraConnect::SyncMergeRequestWorker.perform_async(merge_request.id)
end
end
def create(merge_request)
self.params = assign_allowed_merge_params(merge_request, params)
......
......@@ -25,4 +25,4 @@
%td= link_to 'Remove', jira_connect_subscription_path(subscription), class: 'remove-subscription'
= page_specific_javascript_tag('jira_connect.js')
= stylesheet_link_tag 'pages/jira_connect'
= stylesheet_link_tag 'page_bundles/jira_connect'
......@@ -723,6 +723,22 @@
:weight: 2
:idempotent:
:tags: []
- :name: jira_connect:jira_connect_sync_branch
:feature_category: :integrations
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
- :name: jira_connect:jira_connect_sync_merge_request
:feature_category: :integrations
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
- :name: jira_importer:jira_import_advance_stage
:feature_category: :importers
:has_external_dependencies:
......
---
title: Move Jira Development Panel integration to Core
merge_request: 40485
author:
type: changed
......@@ -178,6 +178,7 @@ module Gitlab
config.assets.precompile << "mailers/*.css"
config.assets.precompile << "page_bundles/_mixins_and_variables_and_functions.css"
config.assets.precompile << "page_bundles/ide.css"
config.assets.precompile << "page_bundles/jira_connect.css"
config.assets.precompile << "page_bundles/todos.css"
config.assets.precompile << "page_bundles/xterm.css"
config.assets.precompile << "performance_bar.css"
......@@ -187,6 +188,7 @@ module Gitlab
config.assets.precompile << "locale/**/app.js"
config.assets.precompile << "emoji_sprites.css"
config.assets.precompile << "errors.css"
config.assets.precompile << "jira_connect.js"
config.assets.precompile << "highlight/themes/*.css"
......@@ -205,14 +207,6 @@ module Gitlab
config.assets.paths << "#{config.root}/node_modules/xterm/src/"
config.assets.precompile << "xterm.css"
if Gitlab.ee?
%w[images javascripts stylesheets].each do |path|
config.assets.paths << "#{config.root}/ee/app/assets/#{path}"
config.assets.precompile << "jira_connect.js"
config.assets.precompile << "pages/jira_connect.css"
end
end
# Import path for EE specific SCSS entry point
# In CE it will import a noop file, in EE a functioning file
# Order is important, so that the ee file takes precedence:
......
......@@ -32,13 +32,10 @@ Rails.application.routes.draw do
# This prefixless path is required because Jira gets confused if we set it up with a path
# More information: https://gitlab.com/gitlab-org/gitlab/issues/6752
scope path: '/login/oauth', controller: 'oauth/jira/authorizations', as: :oauth_jira do
Gitlab.ee do
get :authorize, action: :new
get :callback
post :access_token
end
# This helps minimize merge conflicts with CE for this scope block
match '*all', via: [:get, :post], to: proc { [404, {}, ['']] }
end
......@@ -127,11 +124,11 @@ Rails.application.routes.draw do
get 'ide/*vueroute' => 'ide#index', format: false
draw :operations
draw :jira_connect
Gitlab.ee do
draw :security
draw :smartcard
draw :jira_connect
draw :username
draw :trial
draw :trial_registration
......
......@@ -564,3 +564,37 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# rubocop: enable Cop/PutProjectRoutesUnderScope
end
end
# It's under /-/jira scope but cop is only checking /-/
# rubocop: disable Cop/PutProjectRoutesUnderScope
scope path: '(/-/jira)', constraints: ::Constraints::JiraEncodedUrlConstrainer.new, as: :jira do
scope path: '*namespace_id/:project_id',
namespace_id: Gitlab::Jira::Dvcs::ENCODED_ROUTE_REGEX,
project_id: Gitlab::Jira::Dvcs::ENCODED_ROUTE_REGEX do
get '/', to: redirect { |params, req|
::Gitlab::Jira::Dvcs.restore_full_path(
namespace: params[:namespace_id],
project: params[:project_id]
)
}
get 'commit/:id', constraints: { id: /\h{7,40}/ }, to: redirect { |params, req|
project_full_path = ::Gitlab::Jira::Dvcs.restore_full_path(
namespace: params[:namespace_id],
project: params[:project_id]
)
"/#{project_full_path}/commit/#{params[:id]}"
}
get 'tree/*id', as: nil, to: redirect { |params, req|
project_full_path = ::Gitlab::Jira::Dvcs.restore_full_path(
namespace: params[:namespace_id],
project: params[:project_id]
)
"/#{project_full_path}/-/tree/#{params[:id]}"
}
end
end
# rubocop: enable Cop/PutProjectRoutesUnderScope
......@@ -4,9 +4,10 @@ group: Ecosystem
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# GitLab Jira Development Panel integration **(PREMIUM)**
# GitLab Jira Development Panel integration **(CORE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2381) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.0.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2381) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.0.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/233149) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.4.
The Jira Development Panel integration allows you to reference Jira issues within GitLab, displaying activity in the [Development panel](https://support.atlassian.com/jira-software-cloud/docs/view-development-information-for-an-issue/) in the issue. It complements the [GitLab Jira integration](../user/project/integrations/jira.md). You may choose to configure both integrations to take advantage of both sets of features. (See a [feature comparison](../user/project/integrations/jira_integrations.md#feature-comparison)).
......@@ -199,9 +200,8 @@ Potential resolutions:
- If you're using GitLab versions 11.10-12.7, upgrade to GitLab 12.8.10 or later
to resolve an identified [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/37012).
- The Jira Development Panel integration requires GitLab Premium, GitLab.com Silver,
or a higher tier. If you're using a lower tier of GitLab, you'll need to upgrade
to use this feature.
- If you're using GitLab Core or GitLab Starter, be sure you're using
GitLab 13.4 or later.
[Contact GitLab Support](https://about.gitlab.com/support) if none of these reasons apply.
......@@ -234,7 +234,9 @@ For a walkthrough of the integration with GitLab for Jira, watch [Configure GitL
1. After installing, click **Get started** to go to the configurations page. This page is always available under **Jira Settings > Apps > Manage apps**.
![Start GitLab App configuration on Jira](img/jira_dev_panel_setup_com_2.png)
1. Enter the group or personal namespace in the **Namespace** field and click **Link namespace to Jira**. Make sure you are logged in on GitLab.com and the namespace has a Silver or above license. The user setting up _GitLab for Jira_ must have **Maintainer** access to the GitLab namespace.
1. In **Namespace**, enter the group or personal namespace, and then click
**Link namespace to Jira**. The user setting up *GitLab for Jira* must have
*Maintainer* access to the GitLab namespace.
NOTE: **Note:**
The GitLab user only needs access when adding a new namespace. For syncing with Jira, we do not depend on the user's token.
......
......@@ -18,7 +18,7 @@ Although you can [migrate](../../../user/project/import/jira.md) your Jira issue
The following Jira integrations allow different types of cross-referencing between GitLab activity and Jira issues, with additional features:
- [**Jira integration**](jira.md) - This is built in to GitLab. In a given GitLab project, it can be configured to connect to any Jira instance, self-managed or Cloud.
- [**Jira development panel integration**](../../../integration/jira_development_panel.md) **(PREMIUM)** - This connects all GitLab projects under a specified group or personal namespace.
- [**Jira development panel integration**](../../../integration/jira_development_panel.md) - This connects all GitLab projects under a specified group or personal namespace.
- If you're using Jira Cloud and GitLab.com, install the [GitLab for Jira](https://marketplace.atlassian.com/apps/1221011/gitlab-for-jira) app in the Atlassian Marketplace and see its [documentation](../../../integration/jira_development_panel.md#gitlab-for-jira-app).
- For all other environments, use the [Jira DVCS Connector configuration instructions](../../../integration/jira_development_panel.md#configuration).
......
$header-item-height: 60px;
$item-height: 50px;
$details-cell-width: 320px;
$details-cell-width: px-to-rem(320px);
$timeline-cell-height: 32px;
$timeline-cell-width: 180px;
$border-style: 1px solid $border-gray-normal;
......
......@@ -43,7 +43,6 @@ module EE
has_one :gitlab_slack_application_service
has_one :tracing_setting, class_name: 'ProjectTracingSetting'
has_one :feature_usage, class_name: 'ProjectFeatureUsage'
has_one :status_page_setting, inverse_of: :project, class_name: 'StatusPage::ProjectSetting'
has_one :compliance_framework_setting, class_name: 'ComplianceManagement::ComplianceFramework::ProjectSettings', inverse_of: :project
has_one :security_setting, class_name: 'ProjectSecuritySetting'
......@@ -122,9 +121,6 @@ module EE
scope :requiring_code_owner_approval,
-> { joins(:protected_branches).where(protected_branches: { code_owner_approval_required: true }) }
scope :with_active_services, -> { joins(:services).merge(::Service.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_server, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false)) }
scope :github_imported, -> { where(import_type: 'github') }
scope :with_protected_branches, -> { joins(:protected_branches) }
scope :with_repositories_enabled, -> { joins(:project_feature).where(project_features: { repository_access_level: ::ProjectFeature::ENABLED }) }
......@@ -166,8 +162,6 @@ module EE
:ever_updated_successfully?, :hard_failed?,
to: :import_state, prefix: :mirror, allow_nil: true
delegate :log_jira_dvcs_integration_usage, :jira_dvcs_server_last_sync_at, :jira_dvcs_cloud_last_sync_at, to: :feature_usage
delegate :merge_pipelines_enabled, :merge_pipelines_enabled=, :merge_pipelines_enabled?, :merge_pipelines_were_disabled?, to: :ci_cd_settings
delegate :merge_trains_enabled?, to: :ci_cd_settings
delegate :closest_gitlab_subscription, to: :namespace
......@@ -628,10 +622,6 @@ module EE
geo_primary_http_url_to_repo(self)
end
def feature_usage
super.presence || build_feature_usage
end
def adjourned_deletion?
feature_available?(:adjourned_deletion_for_projects_and_groups) &&
::Gitlab::CurrentSettings.deletion_adjourned_period > 0 &&
......@@ -685,10 +675,6 @@ module EE
::Project.with_groups_level_repos_templates.exists?(id)
end
def jira_subscription_exists?
feature_available?(:jira_dev_panel_integration) && JiraConnectSubscription.for_project(self).exists?
end
override :predefined_variables
def predefined_variables
super.concat(requirements_ci_variables)
......
......@@ -87,7 +87,6 @@ class License < ApplicationRecord
group_project_templates
group_saml
issues_analytics
jira_dev_panel_integration
jira_issues_integration
ldap_group_sync_filter
merge_pipelines
......
......@@ -114,7 +114,6 @@ module EE
end
rule { maintainer }.policy do
enable :create_jira_connect_subscription
enable :maintainer_access
enable :admin_wiki
end
......
......@@ -7,10 +7,6 @@ module EE
prepended do
condition(:over_storage_limit, scope: :subject) { @subject.over_storage_limit? }
rule { owner | admin }.policy do
enable :create_jira_connect_subscription
end
rule { admin & is_gitlab_com }.enable :update_subscription_limit
rule { over_storage_limit }.policy do
......
......@@ -7,20 +7,6 @@ module EE
private
override :branch_change_hooks
def branch_change_hooks
super
return unless project.jira_subscription_exists?
branch_to_sync = branch_name if Atlassian::JiraIssueKeyExtractor.has_keys?(branch_name)
commits_to_sync = limited_commits.select { |commit| Atlassian::JiraIssueKeyExtractor.has_keys?(commit.safe_message) }.map(&:sha)
if branch_to_sync || commits_to_sync.any?
JiraConnect::SyncBranchWorker.perform_async(project.id, branch_to_sync, commits_to_sync)
end
end
override :pipeline_options
def pipeline_options
mirror_update = project.mirror? &&
......
......@@ -5,17 +5,6 @@ module EE
module BaseService
extend ::Gitlab::Utils::Override
override :execute_hooks
def execute_hooks(merge_request, action = 'open', old_rev: nil, old_associations: {})
super
return unless project.jira_subscription_exists?
if Atlassian::JiraIssueKeyExtractor.has_keys?(merge_request.title, merge_request.description)
JiraConnect::SyncMergeRequestWorker.perform_async(merge_request.id)
end
end
private
attr_accessor :blocking_merge_requests_params
......
......@@ -483,22 +483,6 @@
:weight: 1
:idempotent:
:tags: []
- :name: jira_connect:jira_connect_sync_branch
:feature_category: :integrations
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
- :name: jira_connect:jira_connect_sync_merge_request
:feature_category: :integrations
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
- :name: personal_access_tokens:personal_access_tokens_groups_policy
:feature_category: :authentication_and_authorization
:has_external_dependencies:
......
......@@ -146,37 +146,3 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
end
# It's under /-/jira scope but cop is only checking /-/
# rubocop: disable Cop/PutProjectRoutesUnderScope
scope path: '(/-/jira)', constraints: ::Constraints::JiraEncodedUrlConstrainer.new, as: :jira do
scope path: '*namespace_id/:project_id',
namespace_id: Gitlab::Jira::Dvcs::ENCODED_ROUTE_REGEX,
project_id: Gitlab::Jira::Dvcs::ENCODED_ROUTE_REGEX do
get '/', to: redirect { |params, req|
::Gitlab::Jira::Dvcs.restore_full_path(
namespace: params[:namespace_id],
project: params[:project_id]
)
}
get 'commit/:id', constraints: { id: /\h{7,40}/ }, to: redirect { |params, req|
project_full_path = ::Gitlab::Jira::Dvcs.restore_full_path(
namespace: params[:namespace_id],
project: params[:project_id]
)
"/#{project_full_path}/commit/#{params[:id]}"
}
get 'tree/*id', as: nil, to: redirect { |params, req|
project_full_path = ::Gitlab::Jira::Dvcs.restore_full_path(
namespace: params[:namespace_id],
project: params[:project_id]
)
"/#{project_full_path}/-/tree/#{params[:id]}"
}
end
end
# rubocop: enable Cop/PutProjectRoutesUnderScope
......@@ -50,16 +50,6 @@ module EE
mount ::API::Analytics::GroupActivityAnalytics
mount ::API::ProtectedEnvironments
mount ::API::ResourceWeightEvents
version 'v3', using: :path do
# Although the following endpoints are kept behind V3 namespace,
# they're not deprecated neither should be removed when V3 get
# removed. They're needed as a layer to integrate with Jira
# Development Panel.
namespace '/', requirements: ::API::V3::Github::ENDPOINT_REQUIREMENTS do
mount ::API::V3::Github
end
end
end
end
end
......
......@@ -189,8 +189,6 @@ module EE
override :jira_usage
def jira_usage
super.merge(
projects_jira_dvcs_cloud_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled),
projects_jira_dvcs_server_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false)),
projects_jira_issuelist_active: projects_jira_issuelist_active
)
end
......@@ -275,10 +273,7 @@ module EE
assignee_lists: distinct_count(::List.assignee.where(time_period), :user_id),
epics: distinct_count(::Epic.where(time_period), :author_id),
label_lists: distinct_count(::List.label.where(time_period), :user_id),
milestone_lists: distinct_count(::List.milestone.where(time_period), :user_id),
projects_jira_active: distinct_count(::Project.with_active_jira_services.where(time_period), :creator_id),
projects_jira_dvcs_cloud_active: distinct_count(::Project.with_active_jira_services.with_jira_dvcs_cloud.where(time_period), :creator_id),
projects_jira_dvcs_server_active: distinct_count(::Project.with_active_jira_services.with_jira_dvcs_server.where(time_period), :creator_id)
milestone_lists: distinct_count(::List.milestone.where(time_period), :user_id)
})
end
......
......@@ -23,18 +23,6 @@ FactoryBot.modify do
last_repository_updated_at { rand(1.year).seconds.ago }
end
trait :jira_dvcs_cloud do
before(:create) do |project|
create(:project_feature_usage, :dvcs_cloud, project: project)
end
end
trait :jira_dvcs_server do
before(:create) do |project|
create(:project_feature_usage, :dvcs_server, project: project)
end
end
trait :github_imported do
import_type { 'github' }
end
......
......@@ -102,8 +102,6 @@ RSpec.describe Gitlab::UsageData do
operations_dashboard_users_with_projects_added
pod_logs_usages_total
projects_jenkins_active
projects_jira_dvcs_cloud_active
projects_jira_dvcs_server_active
projects_jira_issuelist_active
projects_mirrored_with_pipelines_enabled
projects_reporting_ci_cd_back_to_github
......@@ -431,27 +429,19 @@ RSpec.describe Gitlab::UsageData do
create(:milestone_list, board: board, milestone: create(:milestone, project: project), user: user)
create(:list, board: board, label: create(:label, project: project), user: user)
create(:epic, author: user)
create(:jira_service, :jira_cloud_service, active: true, project: create(:project, :jira_dvcs_cloud, creator: user))
create(:jira_service, active: true, project: create(:project, :jira_dvcs_server, creator: user))
end
expect(described_class.usage_activity_by_stage_plan({})).to include(
assignee_lists: 2,
epics: 2,
label_lists: 2,
milestone_lists: 2,
projects_jira_active: 2,
projects_jira_dvcs_cloud_active: 2,
projects_jira_dvcs_server_active: 2
milestone_lists: 2
)
expect(described_class.usage_activity_by_stage_plan(described_class.last_28_days_time_period)).to include(
assignee_lists: 1,
epics: 1,
label_lists: 1,
milestone_lists: 1,
projects_jira_active: 1,
projects_jira_dvcs_cloud_active: 1,
projects_jira_dvcs_server_active: 1
milestone_lists: 1
)
end
end
......
......@@ -85,36 +85,6 @@ RSpec.describe Project do
end
end
describe '.with_active_jira_services' do
it 'returns the correct project' do
active_jira_service = create(:jira_service)
active_service = create(:service, active: true)
expect(described_class.with_active_jira_services).to include(active_jira_service.project)
expect(described_class.with_active_jira_services).not_to include(active_service.project)
end
end
describe '.with_jira_dvcs_cloud' do
it 'returns the correct project' do
jira_dvcs_cloud_project = create(:project, :jira_dvcs_cloud)
jira_dvcs_server_project = create(:project, :jira_dvcs_server)
expect(described_class.with_jira_dvcs_cloud).to include(jira_dvcs_cloud_project)
expect(described_class.with_jira_dvcs_cloud).not_to include(jira_dvcs_server_project)
end
end
describe '.with_jira_dvcs_server' do
it 'returns the correct project' do
jira_dvcs_server_project = create(:project, :jira_dvcs_server)
jira_dvcs_cloud_project = create(:project, :jira_dvcs_cloud)
expect(described_class.with_jira_dvcs_server).to include(jira_dvcs_server_project)
expect(described_class.with_jira_dvcs_server).not_to include(jira_dvcs_cloud_project)
end
end
describe '.github_imported' do
it 'returns the correct project' do
project_imported_from_github = create(:project, :github_imported)
......@@ -2536,24 +2506,6 @@ RSpec.describe Project do
end
end
describe '#jira_subscription_exists?' do
subject { project.jira_subscription_exists? }
context 'jira connect subscription exists' do
let!(:jira_connect_subscription) { create(:jira_connect_subscription, namespace: project.namespace) }
it { is_expected.to eq(false) }
context 'dev panel integration is available' do
before do
stub_licensed_features(jira_dev_panel_integration: true)
end
it { is_expected.to eq(true) }
end
end
end
describe '#remove_import_data' do
let(:import_data) { ProjectImportData.new(data: { 'test' => 'some data' }) }
......
......@@ -528,50 +528,6 @@ RSpec.describe GroupPolicy do
end
end
describe 'create_jira_connect_subscription' do
context 'admin' do
let(:current_user) { admin }
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'with reporter' do
let(:current_user) { reporter }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
context 'with guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
context 'with non member' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
context 'with anonymous' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
end
describe 'read_group_credentials_inventory' do
context 'with admin' do
let(:current_user) { admin }
......
......@@ -23,32 +23,6 @@ RSpec.describe NamespacePolicy do
end
end
describe 'create_jira_connect_subscription' do
context 'admin' do
let(:current_user) { build_stubbed(:admin) }
context 'when admin mode enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'when admin mode disabled' do
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'other user' do
let(:current_user) { build_stubbed(:user) }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
end
context ':over_storage_limit' do
let(:current_user) { owner }
......
......@@ -138,75 +138,4 @@ RSpec.describe Git::BranchPushService do
end
end
end
context 'Jira Connect hooks' do
let_it_be(:project) { create(:project, :repository) }
let(:branch_to_sync) { nil }
let(:commits_to_sync) { [] }
shared_examples 'enqueues Jira sync worker' do
specify do
Sidekiq::Testing.fake! do
expect(JiraConnect::SyncBranchWorker).to receive(:perform_async)
.with(project.id, branch_to_sync, commits_to_sync)
.and_call_original
expect { subject.execute }.to change(JiraConnect::SyncBranchWorker.jobs, :size).by(1)
end
end
end
shared_examples 'does not enqueue Jira sync worker' do
specify do
Sidekiq::Testing.fake! do
expect { subject.execute }.not_to change(JiraConnect::SyncBranchWorker.jobs, :size)
end
end
end
context 'has Jira dev panel integration license' do
before do
stub_licensed_features(jira_dev_panel_integration: true)
end
context 'with a Jira subscription' do
before do
create(:jira_connect_subscription, namespace: project.namespace)
end
context 'branch name contains Jira issue key' do
let(:branch_to_sync) { 'branch-JIRA-123' }
let(:ref) { "refs/heads/#{branch_to_sync}" }
it_behaves_like 'enqueues Jira sync worker'
end
context 'commit message contains Jira issue key' do
let(:commits_to_sync) { [newrev] }
before do
allow_any_instance_of(Commit).to receive(:safe_message).and_return('Commit with key JIRA-123')
end
it_behaves_like 'enqueues Jira sync worker'
end
context 'branch name and commit message does not contain Jira issue key' do
it_behaves_like 'does not enqueue Jira sync worker'
end
end
context 'without a Jira subscription' do
it_behaves_like 'does not enqueue Jira sync worker'
end
end
context 'does not have Jira dev panel integration license' do
before do
stub_licensed_features(jira_dev_panel_integration: false)
end
it_behaves_like 'does not enqueue Jira sync worker'
end
end
end
......@@ -18,58 +18,6 @@ RSpec.describe MergeRequests::BaseService do
subject { MergeRequests::CreateService.new(project, project.owner, params) }
describe '#execute_hooks' do
shared_examples 'enqueues Jira sync worker' do
it do
Sidekiq::Testing.fake! do
expect { subject.execute }.to change(JiraConnect::SyncMergeRequestWorker.jobs, :size).by(1)
end
end
end
shared_examples 'does not enqueue Jira sync worker' do
it do
Sidekiq::Testing.fake! do
expect { subject.execute }.not_to change(JiraConnect::SyncMergeRequestWorker.jobs, :size)
end
end
end
context 'has Jira dev panel integration license' do
before do
stub_licensed_features(jira_dev_panel_integration: true)
end
context 'with a Jira subscription' do
before do
create(:jira_connect_subscription, namespace: project.namespace)
end
context 'MR contains Jira issue key' do
let(:title) { 'Awesome merge_request with issue JIRA-123' }
it_behaves_like 'enqueues Jira sync worker'
end
context 'MR does not contain Jira issue key' do
it_behaves_like 'does not enqueue Jira sync worker'
end
end
context 'without a Jira subscription' do
it_behaves_like 'does not enqueue Jira sync worker'
end
end
context 'does not have Jira dev panel integration license' do
before do
stub_licensed_features(jira_dev_panel_integration: false)
end
it_behaves_like 'does not enqueue Jira sync worker'
end
end
describe '#filter_params' do
let(:params_filtering_service) { double(:params_filtering_service) }
......
......@@ -246,6 +246,16 @@ module API
mount ::API::Internal::Pages
mount ::API::Internal::Kubernetes
version 'v3', using: :path do
# Although the following endpoints are kept behind V3 namespace,
# they're not deprecated neither should be removed when V3 get
# removed. They're needed as a layer to integrate with Jira
# Development Panel.
namespace '/', requirements: ::API::V3::Github::ENDPOINT_REQUIREMENTS do
mount ::API::V3::Github
end
end
route :any, '*path' do
error!('404 Not Found', 404)
end
......
......@@ -8,7 +8,6 @@
module API
module V3
class Github < Grape::API::Instance
JIRA_DEV_PANEL_FEATURE = :jira_dev_panel_integration.freeze
NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze
ENDPOINT_REQUIREMENTS = {
namespace: NO_SLASH_URL_PART_REGEX,
......@@ -54,15 +53,14 @@ module API
project = find_project!(
::Gitlab::Jira::Dvcs.restore_full_path(params.slice(:namespace, :project).symbolize_keys)
)
not_found! unless licensed?(project) && can?(current_user, :download_code, project)
not_found! unless can?(current_user, :download_code, project)
project
end
# rubocop: disable CodeReuse/ActiveRecord
def find_merge_requests
merge_requests = authorized_merge_requests.reorder(updated_at: :desc).preload(:target_project)
merge_requests = paginate(merge_requests)
merge_requests.select { |mr| licensed?(mr.target_project) }
merge_requests = authorized_merge_requests.reorder(updated_at: :desc)
paginate(merge_requests)
end
# rubocop: enable CodeReuse/ActiveRecord
......@@ -90,10 +88,6 @@ module API
notes.select { |n| n.readable_by?(current_user) }
end
# rubocop: enable CodeReuse/ActiveRecord
def licensed?(routable)
routable.feature_available?(JIRA_DEV_PANEL_FEATURE)
end
end
resource :orgs do
......@@ -115,7 +109,7 @@ module API
get ':namespace/repos' do
namespace = Namespace.find_by_full_path(params[:namespace])
not_found!('Namespace') unless namespace && licensed?(namespace)
not_found!('Namespace') unless namespace
projects = current_user.can_read_all_resources? ? Project.all : current_user.authorized_projects
projects = projects.in_namespace(namespace.self_and_descendants)
......
......@@ -375,7 +375,9 @@ module Gitlab
# so we can just check for subdomains of atlassian.net
results = {
projects_jira_server_active: 0,
projects_jira_cloud_active: 0
projects_jira_cloud_active: 0,
projects_jira_dvcs_cloud_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled),
projects_jira_dvcs_server_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false))
}
# rubocop: disable UsageData/LargeTable:
......@@ -565,7 +567,10 @@ module Gitlab
projects: distinct_count(::Project.where(time_period), :creator_id),
todos: distinct_count(::Todo.where(time_period), :author_id),
service_desk_enabled_projects: distinct_count_service_desk_enabled_projects(time_period),
service_desk_issues: count(::Issue.service_desk.where(time_period))
service_desk_issues: count(::Issue.service_desk.where(time_period)),
projects_jira_active: distinct_count(::Project.with_active_jira_services.where(time_period), :creator_id),
projects_jira_dvcs_cloud_active: distinct_count(::Project.with_active_jira_services.with_jira_dvcs_cloud.where(time_period), :creator_id),
projects_jira_dvcs_server_active: distinct_count(::Project.with_active_jira_services.with_jira_dvcs_server.where(time_period), :creator_id)
}
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -64,10 +64,6 @@ RSpec.describe JiraConnect::SubscriptionsController do
end
context 'dev panel integration is available' do
before do
stub_licensed_features(jira_dev_panel_integration: true)
end
it 'creates a subscription' do
expect { subject }.to change { installation.subscriptions.count }.from(0).to(1)
end
......@@ -78,18 +74,6 @@ RSpec.describe JiraConnect::SubscriptionsController do
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'dev panel integration is not available' do
before do
stub_licensed_features(jira_dev_panel_integration: false)
end
it 'returns 422' do
subject
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
end
context 'not signed in to GitLab' do
......
......@@ -109,6 +109,18 @@ FactoryBot.define do
import_status { :failed }
end
trait :jira_dvcs_cloud do
before(:create) do |project|
create(:project_feature_usage, :dvcs_cloud, project: project)
end
end
trait :jira_dvcs_server do
before(:create) do |project|
create(:project_feature_usage, :dvcs_server, project: project)
end
end
trait :archived do
archived { true }
end
......
......@@ -17,5 +17,5 @@ RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity do
).to_json
end
it { is_expected.to match_schema('jira_connect/repository', dir: 'ee') }
it { is_expected.to match_schema('jira_connect/repository') }
end
......@@ -286,6 +286,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
create(:issue, project: project, author: User.support_bot)
create(:note, project: project, noteable: issue, author: user)
create(:todo, project: project, target: issue, author: user)
create(:jira_service, :jira_cloud_service, active: true, project: create(:project, :jira_dvcs_cloud, creator: user))
create(:jira_service, active: true, project: create(:project, :jira_dvcs_server, creator: user))
end
expect(described_class.usage_activity_by_stage_plan({})).to include(
......@@ -294,7 +296,10 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
projects: 2,
todos: 2,
service_desk_enabled_projects: 2,
service_desk_issues: 2
service_desk_issues: 2,
projects_jira_active: 2,
projects_jira_dvcs_cloud_active: 2,
projects_jira_dvcs_server_active: 2
)
expect(described_class.usage_activity_by_stage_plan(described_class.last_28_days_time_period)).to include(
issues: 2,
......@@ -302,7 +307,10 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
projects: 1,
todos: 1,
service_desk_enabled_projects: 1,
service_desk_issues: 1
service_desk_issues: 1,
projects_jira_active: 1,
projects_jira_dvcs_cloud_active: 1,
projects_jira_dvcs_server_active: 1
)
end
end
......
......@@ -1362,6 +1362,36 @@ RSpec.describe Project do
end
end
describe '.with_active_jira_services' do
it 'returns the correct project' do
active_jira_service = create(:jira_service)
active_service = create(:service, active: true)
expect(described_class.with_active_jira_services).to include(active_jira_service.project)
expect(described_class.with_active_jira_services).not_to include(active_service.project)
end
end
describe '.with_jira_dvcs_cloud' do
it 'returns the correct project' do
jira_dvcs_cloud_project = create(:project, :jira_dvcs_cloud)
jira_dvcs_server_project = create(:project, :jira_dvcs_server)
expect(described_class.with_jira_dvcs_cloud).to include(jira_dvcs_cloud_project)
expect(described_class.with_jira_dvcs_cloud).not_to include(jira_dvcs_server_project)
end
end
describe '.with_jira_dvcs_server' do
it 'returns the correct project' do
jira_dvcs_server_project = create(:project, :jira_dvcs_server)
jira_dvcs_cloud_project = create(:project, :jira_dvcs_cloud)
expect(described_class.with_jira_dvcs_server).to include(jira_dvcs_server_project)
expect(described_class.with_jira_dvcs_server).not_to include(jira_dvcs_cloud_project)
end
end
describe '.cached_count', :use_clean_rails_memory_store_caching do
let(:group) { create(:group, :public) }
let!(:project1) { create(:project, :public, group: group) }
......@@ -6043,6 +6073,18 @@ RSpec.describe Project do
end
end
describe '#jira_subscription_exists?' do
let(:project) { create(:project) }
subject { project.jira_subscription_exists? }
context 'jira connect subscription exists' do
let!(:jira_connect_subscription) { create(:jira_connect_subscription, namespace: project.namespace) }
it { is_expected.to eq(true) }
end
end
describe 'with services and chat names' do
subject { create(:project) }
......
......@@ -768,4 +768,48 @@ RSpec.describe GroupPolicy do
end
end
end
describe 'create_jira_connect_subscription' do
context 'admin' do
let(:current_user) { admin }
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'with reporter' do
let(:current_user) { reporter }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
context 'with guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
context 'with non member' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
context 'with anonymous' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
end
end
......@@ -48,4 +48,30 @@ RSpec.describe NamespacePolicy do
it { is_expected.to be_disallowed(*owner_permissions) }
end
end
describe 'create_jira_connect_subscription' do
context 'admin' do
let(:current_user) { build_stubbed(:admin) }
context 'when admin mode enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'when admin mode disabled' do
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:create_jira_connect_subscription) }
end
context 'other user' do
let(:current_user) { build_stubbed(:user) }
it { is_expected.to be_disallowed(:create_jira_connect_subscription) }
end
end
end
......@@ -10,7 +10,6 @@ RSpec.describe API::V3::Github do
before do
project.add_maintainer(user)
stub_licensed_features(jira_dev_panel_integration: true)
end
describe 'GET /orgs/:namespace/repos' do
......@@ -123,7 +122,7 @@ RSpec.describe API::V3::Github do
jira_get v3_api("/users/#{user.username}", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('entities/github/user', dir: 'ee')
expect(response).to match_response_schema('entities/github/user')
end
end
......@@ -220,7 +219,7 @@ RSpec.describe API::V3::Github do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an(Array)
expect(json_response.size).to eq(2)
expect(response).to match_response_schema('entities/github/pull_requests', dir: 'ee')
expect(response).to match_response_schema('entities/github/pull_requests')
end
end
......@@ -231,7 +230,7 @@ RSpec.describe API::V3::Github do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an(Array)
expect(json_response.size).to eq(1)
expect(response).to match_response_schema('entities/github/pull_requests', dir: 'ee')
expect(response).to match_response_schema('entities/github/pull_requests')
end
end
......@@ -241,7 +240,7 @@ RSpec.describe API::V3::Github do
jira_get v3_api("/repos/#{project.namespace.path}/#{project.path}/pulls/#{merge_request.id}", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('entities/github/pull_request', dir: 'ee')
expect(response).to match_response_schema('entities/github/pull_request')
end
end
......@@ -260,7 +259,7 @@ RSpec.describe API::V3::Github do
jira_get v3_api("/repos/#{project.namespace.path}/#{project.path}/pulls/#{merge_request.id}", admin)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('entities/github/pull_request', dir: 'ee')
expect(response).to match_response_schema('entities/github/pull_request')
end
end
end
......@@ -274,7 +273,7 @@ RSpec.describe API::V3::Github do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(response).to match_response_schema('entities/github/repositories', dir: 'ee')
expect(response).to match_response_schema('entities/github/repositories')
projects.each do |project|
hash = json_response.find do |hash|
......@@ -327,24 +326,6 @@ RSpec.describe API::V3::Github do
expect_project_under_namespace([parent_group_project, child_group_project], group.parent, user)
end
context 'when namespace license checks are enabled' do
before do
enable_namespace_license_check!
end
context 'when the root group does not have the correct license' do
it 'returns not found' do
jira_get v3_api("/users/#{group.parent.path}/repos", user)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when the root group has the correct license' do
before do
create(:gitlab_subscription, :gold, namespace: group.parent)
end
it 'avoids N+1 queries' do
jira_get v3_api("/users/#{group.parent.path}/repos", user)
......@@ -357,8 +338,6 @@ RSpec.describe API::V3::Github do
expect(response).to have_gitlab_http_status(:ok)
end
end
end
end
context 'user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
......@@ -389,18 +368,6 @@ RSpec.describe API::V3::Github do
end
end
it 'filters unlicensed namespace projects' do
licensed_project = create(:project, :empty_repo, group: group)
licensed_project.add_reporter(user)
create(:gitlab_subscription, :silver, namespace: licensed_project.namespace)
stub_application_setting_on_object(project, should_check_namespace_plan: true)
stub_application_setting_on_object(licensed_project, should_check_namespace_plan: true)
expect_project_under_namespace([licensed_project], group, user)
end
context 'namespace does not exist' do
it 'responds with not found status' do
jira_get v3_api('/users/noo/repos', user)
......@@ -441,7 +408,7 @@ RSpec.describe API::V3::Github do
expect(response).to include_pagination_headers
expect(json_response).to be_an(Array)
expect(response).to match_response_schema('entities/github/branches', dir: 'ee')
expect(response).to match_response_schema('entities/github/branches')
end
it 'returns 200 when project path include a dot' do
......@@ -479,15 +446,6 @@ RSpec.describe API::V3::Github do
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns 404 when not licensed' do
stub_licensed_features(jira_dev_panel_integration: false)
project.add_reporter(unauthorized_user)
jira_get v3_api("/repos/#{project.namespace.path}/#{project.path}/branches", unauthorized_user)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
......@@ -500,7 +458,7 @@ RSpec.describe API::V3::Github do
jira_get v3_api("/repos/#{project.namespace.path}/#{project.path}/commits/#{commit_id}", user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('entities/github/commit', dir: 'ee')
expect(response).to match_response_schema('entities/github/commit')
end
it 'returns 200 when project path include a dot' do
......@@ -539,16 +497,6 @@ RSpec.describe API::V3::Github do
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns 404 when not licensed' do
stub_licensed_features(jira_dev_panel_integration: false)
project.add_reporter(unauthorized_user)
jira_get v3_api("/repos/#{project.namespace.path}/#{project.path}/commits/#{commit_id}",
unauthorized_user)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
......
......@@ -704,4 +704,68 @@ RSpec.describe Git::BranchPushService, services: true do
service.execute
service
end
context 'Jira Connect hooks' do
let_it_be(:project) { create(:project, :repository) }
let(:branch_to_sync) { nil }
let(:commits_to_sync) { [] }
let(:params) do
{ change: { oldrev: oldrev, newrev: newrev, ref: ref } }
end
subject do
described_class.new(project, user, params)
end
shared_examples 'enqueues Jira sync worker' do
specify do
Sidekiq::Testing.fake! do
expect(JiraConnect::SyncBranchWorker).to receive(:perform_async)
.with(project.id, branch_to_sync, commits_to_sync)
.and_call_original
expect { subject.execute }.to change(JiraConnect::SyncBranchWorker.jobs, :size).by(1)
end
end
end
shared_examples 'does not enqueue Jira sync worker' do
specify do
Sidekiq::Testing.fake! do
expect { subject.execute }.not_to change(JiraConnect::SyncBranchWorker.jobs, :size)
end
end
end
context 'with a Jira subscription' do
before do
create(:jira_connect_subscription, namespace: project.namespace)
end
context 'branch name contains Jira issue key' do
let(:branch_to_sync) { 'branch-JIRA-123' }
let(:ref) { "refs/heads/#{branch_to_sync}" }
it_behaves_like 'enqueues Jira sync worker'
end
context 'commit message contains Jira issue key' do
let(:commits_to_sync) { [newrev] }
before do
allow_any_instance_of(Commit).to receive(:safe_message).and_return('Commit with key JIRA-123')
end
it_behaves_like 'enqueues Jira sync worker'
end
context 'branch name and commit message does not contain Jira issue key' do
it_behaves_like 'does not enqueue Jira sync worker'
end
end
context 'without a Jira subscription' do
it_behaves_like 'does not enqueue Jira sync worker'
end
end
end
......@@ -24,11 +24,7 @@ RSpec.describe JiraConnectSubscriptions::CreateService do
end
end
context 'when jira_dev_panel_integration is available' do
before do
stub_licensed_features(jira_dev_panel_integration: true)
end
context 'when user does have access' do
it 'creates a subscription' do
expect { subject }.to change { installation.subscriptions.count }.from(0).to(1)
end
......@@ -36,6 +32,7 @@ RSpec.describe JiraConnectSubscriptions::CreateService do
it 'returns success' do
expect(subject[:status]).to eq(:success)
end
end
context 'when path is invalid' do
let(:path) { 'some_invalid_namespace_path' }
......@@ -46,15 +43,6 @@ RSpec.describe JiraConnectSubscriptions::CreateService do
context 'when user does not have access' do
subject { described_class.new(installation, create(:user), namespace_path: path).execute }
it_behaves_like 'a failed execution'
end
end
context 'when jira_dev_panel_integration is not available' do
before do
stub_licensed_features(jira_dev_panel_integration: false)
end
it_behaves_like 'a failed execution'
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe MergeRequests::BaseService do
include ProjectForksHelper
let_it_be(:project) { create(:project, :repository) }
let(:title) { 'Awesome merge_request' }
let(:params) do
{
title: title,
description: 'please fix',
source_branch: 'feature',
target_branch: 'master'
}
end
subject { MergeRequests::CreateService.new(project, project.owner, params) }
describe '#execute_hooks' do
shared_examples 'enqueues Jira sync worker' do
it do
Sidekiq::Testing.fake! do
expect { subject.execute }.to change(JiraConnect::SyncMergeRequestWorker.jobs, :size).by(1)
end
end
end
shared_examples 'does not enqueue Jira sync worker' do
it do
Sidekiq::Testing.fake! do
expect { subject.execute }.not_to change(JiraConnect::SyncMergeRequestWorker.jobs, :size)
end
end
end
context 'with a Jira subscription' do
before do
create(:jira_connect_subscription, namespace: project.namespace)
end
context 'MR contains Jira issue key' do
let(:title) { 'Awesome merge_request with issue JIRA-123' }
it_behaves_like 'enqueues Jira sync worker'
end
context 'MR does not contain Jira issue key' do
it_behaves_like 'does not enqueue Jira sync worker'
end
end
context 'without a Jira subscription' do
it_behaves_like 'does not enqueue Jira sync worker'
end
end
end
......@@ -88,6 +88,8 @@ module UsageDataHelpers
projects_jira_active
projects_jira_server_active
projects_jira_cloud_active
projects_jira_dvcs_cloud_active
projects_jira_dvcs_server_active
projects_slack_active
projects_slack_slash_commands_active
projects_custom_issue_tracker_active
......
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