Commit db64b768 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 27704ed5 c1e16db6
/* global DocumentTouch */ /* global DocumentTouch */
import sortableConfig from 'ee_else_ce/sortable/sortable_config'; import sortableConfig from '~/sortable/sortable_config';
export function sortableStart() { export function sortableStart() {
document.body.classList.add('is-dragging'); document.body.classList.add('is-dragging');
......
...@@ -550,8 +550,8 @@ export default { ...@@ -550,8 +550,8 @@ export default {
if (id.startsWith('#note_')) { if (id.startsWith('#note_')) {
const noteId = id.replace('#note_', ''); const noteId = id.replace('#note_', '');
const discussion = this.$store.state.notes.discussions.find((d) => const discussion = this.$store.state.notes.discussions.find(
d.notes.find((n) => n.id === noteId), (d) => d.diff_file && d.notes.find((n) => n.id === noteId),
); );
if (discussion) { if (discussion) {
......
...@@ -99,7 +99,7 @@ export default { ...@@ -99,7 +99,7 @@ export default {
v-gl-tooltip.hover v-gl-tooltip.hover
variant="default" variant="default"
icon="file-tree" icon="file-tree"
class="gl-mr-3 js-toggle-tree-list" class="gl-mr-3 js-toggle-tree-list btn-icon"
:title="toggleFileBrowserTitle" :title="toggleFileBrowserTitle"
:aria-label="toggleFileBrowserTitle" :aria-label="toggleFileBrowserTitle"
:selected="showTreeList" :selected="showTreeList"
...@@ -109,7 +109,7 @@ export default { ...@@ -109,7 +109,7 @@ export default {
{{ __('Viewing commit') }} {{ __('Viewing commit') }}
<gl-link :href="commit.commit_url" class="monospace">{{ commit.short_id }}</gl-link> <gl-link :href="commit.commit_url" class="monospace">{{ commit.short_id }}</gl-link>
</div> </div>
<div v-if="hasNeighborCommits" class="commit-nav-buttons ml-3"> <div v-if="hasNeighborCommits" class="commit-nav-buttons">
<gl-button-group> <gl-button-group>
<gl-button <gl-button
:href="previousCommitUrl" :href="previousCommitUrl"
...@@ -160,20 +160,21 @@ export default { ...@@ -160,20 +160,21 @@ export default {
/> />
</template> </template>
</gl-sprintf> </gl-sprintf>
<div v-if="hasChanges" class="inline-parallel-buttons d-none d-md-flex ml-auto">
<diff-stats
:diff-files-count-text="diffFilesCountText"
:added-lines="addedLines"
:removed-lines="removedLines"
/>
<gl-button <gl-button
v-if="commit || startVersion" v-if="commit || startVersion"
:href="latestVersionPath" :href="latestVersionPath"
variant="default" variant="default"
class="gl-mr-3 js-latest-version" class="js-latest-version"
:class="{ 'gl-ml-3': commit && !hasNeighborCommits }"
> >
{{ __('Show latest version') }} {{ __('Show latest version') }}
</gl-button> </gl-button>
<div v-if="hasChanges" class="inline-parallel-buttons d-none d-md-flex ml-auto">
<diff-stats
:diff-files-count-text="diffFilesCountText"
:added-lines="addedLines"
:removed-lines="removedLines"
/>
<gl-button <gl-button
v-show="whichCollapsedTypes.any" v-show="whichCollapsedTypes.any"
variant="default" variant="default"
......
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import sortableConfig from 'ee_else_ce/sortable/sortable_config'; import sortableConfig from '~/sortable/sortable_config';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue'; import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
export default { export default {
......
...@@ -183,6 +183,8 @@ ...@@ -183,6 +183,8 @@
} }
.commit-nav-buttons { .commit-nav-buttons {
margin: 0 0.5rem;
a.btn, a.btn,
button { button {
// See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/730 // See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/730
......
...@@ -706,7 +706,7 @@ $tabs-holder-z-index: 250; ...@@ -706,7 +706,7 @@ $tabs-holder-z-index: 250;
.mr-version-dropdown, .mr-version-dropdown,
.mr-version-compare-dropdown { .mr-version-compare-dropdown {
margin: 0 7px; margin: 0 0.5rem;
} }
.dropdown-title { .dropdown-title {
...@@ -715,7 +715,7 @@ $tabs-holder-z-index: 250; ...@@ -715,7 +715,7 @@ $tabs-holder-z-index: 250;
// Shortening button height by 1px to make compare-versions // Shortening button height by 1px to make compare-versions
// header 56px and fit into our 8px design grid // header 56px and fit into our 8px design grid
button { .btn {
height: 34px; height: 34px;
} }
......
...@@ -19,7 +19,7 @@ module HasIntegrations ...@@ -19,7 +19,7 @@ module HasIntegrations
def without_integration(integration) def without_integration(integration)
integrations = Integration integrations = Integration
.select('1') .select('1')
.where('services.project_id = projects.id') .where("#{Integration.table_name}.project_id = projects.id")
.where(type: integration.type) .where(type: integration.type)
Project Project
......
...@@ -167,7 +167,7 @@ class Group < Namespace ...@@ -167,7 +167,7 @@ class Group < Namespace
def without_integration(integration) def without_integration(integration)
integrations = Integration integrations = Integration
.select('1') .select('1')
.where('services.group_id = namespaces.id') .where("#{Integration.table_name}.group_id = namespaces.id")
.where(type: integration.type) .where(type: integration.type)
where('NOT EXISTS (?)', integrations) where('NOT EXISTS (?)', integrations)
......
...@@ -10,9 +10,6 @@ class Integration < ApplicationRecord ...@@ -10,9 +10,6 @@ class Integration < ApplicationRecord
include FromUnion include FromUnion
include EachBatch include EachBatch
# TODO Rename the table: https://gitlab.com/gitlab-org/gitlab/-/issues/201856
self.table_name = 'services'
INTEGRATION_NAMES = %w[ INTEGRATION_NAMES = %w[
asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat irker jira drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat irker jira
...@@ -299,7 +296,7 @@ class Integration < ApplicationRecord ...@@ -299,7 +296,7 @@ class Integration < ApplicationRecord
array = group_ids.to_sql.present? ? "array(#{group_ids.to_sql})" : 'ARRAY[]' array = group_ids.to_sql.present? ? "array(#{group_ids.to_sql})" : 'ARRAY[]'
where(type: type, group_id: group_ids, inherit_from_id: nil) where(type: type, group_id: group_ids, inherit_from_id: nil)
.order(Arel.sql("array_position(#{array}::bigint[], services.group_id)")) .order(Arel.sql("array_position(#{array}::bigint[], #{table_name}.group_id)"))
.first .first
end end
private_class_method :closest_group_integration private_class_method :closest_group_integration
...@@ -317,7 +314,7 @@ class Integration < ApplicationRecord ...@@ -317,7 +314,7 @@ class Integration < ApplicationRecord
with_templates ? active.where(template: true) : none, with_templates ? active.where(template: true) : none,
active.where(instance: true), active.where(instance: true),
active.where(group_id: group_ids, inherit_from_id: nil) active.where(group_id: group_ids, inherit_from_id: nil)
]).order(Arel.sql("type ASC, array_position(#{array}::bigint[], services.group_id), instance DESC")).group_by(&:type).each do |type, records| ]).order(Arel.sql("type ASC, array_position(#{array}::bigint[], #{table_name}.group_id), instance DESC")).group_by(&:type).each do |type, records|
build_from_integration(records.first, association => scope.id).save build_from_integration(records.first, association => scope.id).save
end end
end end
......
# frozen_string_literal: true
class RenameServicesToIntegrations < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
include Gitlab::Database::SchemaHelpers
# Function and trigger names match those migrated in:
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49916
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51852
WIKI_FUNCTION_NAME = 'set_has_external_wiki'
TRACKER_FUNCTION_NAME = 'set_has_external_issue_tracker'
WIKI_TRIGGER_ON_INSERT_NAME = 'trigger_has_external_wiki_on_insert'
WIKI_TRIGGER_ON_UPDATE_NAME = 'trigger_has_external_wiki_on_update'
WIKI_TRIGGER_ON_DELETE_NAME = 'trigger_has_external_wiki_on_delete'
TRACKER_TRIGGER_ON_INSERT_NAME = 'trigger_has_external_issue_tracker_on_insert'
TRACKER_TRIGGER_ON_UPDATE_NAME = 'trigger_has_external_issue_tracker_on_update'
TRACKER_TRIGGER_ON_DELETE_NAME = 'trigger_has_external_issue_tracker_on_delete'
ALL_TRIGGERS = [
WIKI_TRIGGER_ON_INSERT_NAME,
WIKI_TRIGGER_ON_UPDATE_NAME,
WIKI_TRIGGER_ON_DELETE_NAME,
TRACKER_TRIGGER_ON_INSERT_NAME,
TRACKER_TRIGGER_ON_UPDATE_NAME,
TRACKER_TRIGGER_ON_DELETE_NAME
].freeze
def up
execute('LOCK services IN ACCESS EXCLUSIVE MODE')
drop_all_triggers(:services)
rename_table_safely(:services, :integrations)
recreate_all_triggers(:integrations)
end
def down
execute('LOCK integrations IN ACCESS EXCLUSIVE MODE')
drop_all_triggers(:integrations)
undo_rename_table_safely(:services, :integrations)
recreate_all_triggers(:services)
end
private
def drop_all_triggers(table_name)
ALL_TRIGGERS.each do |trigger_name|
drop_trigger(table_name, trigger_name)
end
end
def recreate_all_triggers(table_name)
wiki_create_insert_trigger(table_name)
wiki_create_update_trigger(table_name)
wiki_create_delete_trigger(table_name)
tracker_replace_trigger_function(table_name)
tracker_create_insert_trigger(table_name)
tracker_create_update_trigger(table_name)
tracker_create_delete_trigger(table_name)
end
def wiki_create_insert_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{WIKI_TRIGGER_ON_INSERT_NAME}
AFTER INSERT ON #{table_name}
FOR EACH ROW
WHEN (NEW.active = TRUE AND NEW.type = 'ExternalWikiService' AND NEW.project_id IS NOT NULL)
EXECUTE FUNCTION #{WIKI_FUNCTION_NAME}();
SQL
end
def wiki_create_update_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{WIKI_TRIGGER_ON_UPDATE_NAME}
AFTER UPDATE ON #{table_name}
FOR EACH ROW
WHEN (NEW.type = 'ExternalWikiService' AND OLD.active != NEW.active AND NEW.project_id IS NOT NULL)
EXECUTE FUNCTION #{WIKI_FUNCTION_NAME}();
SQL
end
def wiki_create_delete_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{WIKI_TRIGGER_ON_DELETE_NAME}
AFTER DELETE ON #{table_name}
FOR EACH ROW
WHEN (OLD.type = 'ExternalWikiService' AND OLD.project_id IS NOT NULL)
EXECUTE FUNCTION #{WIKI_FUNCTION_NAME}();
SQL
end
# Using `replace: true` to rewrite the existing function
def tracker_replace_trigger_function(table_name)
create_trigger_function(TRACKER_FUNCTION_NAME, replace: true) do
<<~SQL
UPDATE projects SET has_external_issue_tracker = (
EXISTS
(
SELECT 1
FROM #{table_name}
WHERE project_id = COALESCE(NEW.project_id, OLD.project_id)
AND active = TRUE
AND category = 'issue_tracker'
)
)
WHERE projects.id = COALESCE(NEW.project_id, OLD.project_id);
RETURN NULL;
SQL
end
end
def tracker_create_insert_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{TRACKER_TRIGGER_ON_INSERT_NAME}
AFTER INSERT ON #{table_name}
FOR EACH ROW
WHEN (NEW.category = 'issue_tracker' AND NEW.active = TRUE AND NEW.project_id IS NOT NULL)
EXECUTE FUNCTION #{TRACKER_FUNCTION_NAME}();
SQL
end
def tracker_create_update_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{TRACKER_TRIGGER_ON_UPDATE_NAME}
AFTER UPDATE ON #{table_name}
FOR EACH ROW
WHEN (NEW.category = 'issue_tracker' AND OLD.active != NEW.active AND NEW.project_id IS NOT NULL)
EXECUTE FUNCTION #{TRACKER_FUNCTION_NAME}();
SQL
end
def tracker_create_delete_trigger(table_name)
execute(<<~SQL)
CREATE TRIGGER #{TRACKER_TRIGGER_ON_DELETE_NAME}
AFTER DELETE ON #{table_name}
FOR EACH ROW
WHEN (OLD.category = 'issue_tracker' AND OLD.active = TRUE AND OLD.project_id IS NOT NULL)
EXECUTE FUNCTION #{TRACKER_FUNCTION_NAME}();
SQL
end
end
# frozen_string_literal: true
class RenameServicesIndexesToIntegrations < ActiveRecord::Migration[6.1]
INDEXES = %w(
project_and_type_where_inherit_null
project_id_and_type_unique
template
type
type_and_instance_partial
type_and_template_partial
type_id_when_active_and_project_id_not_null
unique_group_id_and_type
).freeze
def up
INDEXES.each do |index|
execute(<<~SQL)
ALTER INDEX IF EXISTS "index_services_on_#{index}" RENAME TO "index_integrations_on_#{index}"
SQL
end
end
def down
INDEXES.each do |index|
execute(<<~SQL)
ALTER INDEX IF EXISTS "index_integrations_on_#{index}" RENAME TO "index_services_on_#{index}"
SQL
end
end
end
# frozen_string_literal: true
class StealBackgroundJobsThatReferenceServices < ActiveRecord::Migration[6.1]
def up
Gitlab::BackgroundMigration.steal('BackfillJiraTrackerDeploymentType2')
Gitlab::BackgroundMigration.steal('FixProjectsWithoutPrometheusService')
Gitlab::BackgroundMigration.steal('MigrateIssueTrackersSensitiveData')
Gitlab::BackgroundMigration.steal('RemoveDuplicateServices')
end
def down
# no-op
end
end
# frozen_string_literal: true
class FinalizeRenameServicesToIntegrations < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
def up
finalize_table_rename(:services, :integrations)
end
def down
undo_finalize_table_rename(:services, :integrations)
end
end
cfe35a1297c4a92c4b5e62757ed74c11ffd6f207777291c11b05a4e3cee91618
\ No newline at end of file
64babbed04b9e3bf59bb723b43e3c30730527f0e0e09906073b5bd9379067ab6
\ No newline at end of file
07d0de05b6a59ba0d1f464ae488f5ead812bc643984ac3dc662c78a02a978f7f
\ No newline at end of file
eeee178019c259a6fff85219490abf62f2694227cc2facf454d93e57c373833b
\ No newline at end of file
This diff is collapsed.
...@@ -191,6 +191,10 @@ changes. ...@@ -191,6 +191,10 @@ changes.
You can always change the **Security policy project** by navigating to your project's You can always change the **Security policy project** by navigating to your project's
**Security & Compliance > Policies** and modifying the selected project. **Security & Compliance > Policies** and modifying the selected project.
NOTE:
Only project Owners have the [permissions](../../permissions.md#project-members-permissions)
to select Security Policy Project.
## Roadmap ## Roadmap
See the [Category Direction page](https://about.gitlab.com/direction/protect/container_network_security/) See the [Category Direction page](https://about.gitlab.com/direction/protect/container_network_security/)
......
...@@ -168,6 +168,8 @@ The following table lists project permissions available for each role: ...@@ -168,6 +168,8 @@ The following table lists project permissions available for each role:
| Manage Project Operations | | | | ✓ | ✓ | | Manage Project Operations | | | | ✓ | ✓ |
| Manage Terraform state | | | | ✓ | ✓ | | Manage Terraform state | | | | ✓ | ✓ |
| Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ | | Manage license policy **(ULTIMATE)** | | | | ✓ | ✓ |
| Manage security policy **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Create or assign security policy project **(ULTIMATE)** | | | | | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ | | Edit comments (posted by any user) | | | | ✓ | ✓ |
| Reposition comments on images (posted by any user)|✓ (*10*) | ✓ (*10*) | ✓ (*10*) | ✓ | ✓ | | Reposition comments on images (posted by any user)|✓ (*10*) | ✓ (*10*) | ✓ (*10*) | ✓ | ✓ |
| Manage Error Tracking | | | | ✓ | ✓ | | Manage Error Tracking | | | | ✓ | ✓ |
......
/* global DocumentTouch */ /* global DocumentTouch */
import sortableConfig from 'ee_else_ce/sortable/sortable_config'; import sortableConfig from '~/sortable/sortable_config';
import { NO_DRAG_CLASS } from '../constants'; import { NO_DRAG_CLASS } from '../constants';
export default () => { export default () => {
......
export default {
animation: 200,
forceFallback: true,
fallbackClass: 'is-dragging',
fallbackOnBody: true,
ghostClass: 'is-ghost',
fallbackTolerance: 1,
};
...@@ -5,9 +5,12 @@ module Projects ...@@ -5,9 +5,12 @@ module Projects
class PoliciesController < Projects::ApplicationController class PoliciesController < Projects::ApplicationController
include SecurityAndCompliancePermissions include SecurityAndCompliancePermissions
before_action :authorize_security_orchestration_policies!
before_action :authorize_update_security_orchestration_policy_project!, only: [:assign]
before_action do before_action do
push_frontend_feature_flag(:security_orchestration_policies_configuration, project) push_frontend_feature_flag(:security_orchestration_policies_configuration, project)
check_permissions! check_feature_flag!
end end
feature_category :security_orchestration feature_category :security_orchestration
...@@ -32,8 +35,8 @@ module Projects ...@@ -32,8 +35,8 @@ module Projects
private private
def check_permissions! def check_feature_flag!
render_404 unless Feature.enabled?(:security_orchestration_policies_configuration, project) && can?(current_user, :security_orchestration_policies, project) render_404 if Feature.disabled?(:security_orchestration_policies_configuration, project)
end end
def policy_project_params def policy_project_params
......
...@@ -7,7 +7,7 @@ module Mutations ...@@ -7,7 +7,7 @@ module Mutations
graphql_name 'SecurityPolicyProjectAssign' graphql_name 'SecurityPolicyProjectAssign'
authorize :security_orchestration_policies authorize :update_security_orchestration_policy_project
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::ID_TYPE,
required: true, required: true,
......
...@@ -7,7 +7,7 @@ module Mutations ...@@ -7,7 +7,7 @@ module Mutations
graphql_name 'SecurityPolicyProjectCreate' graphql_name 'SecurityPolicyProjectCreate'
authorize :security_orchestration_policies authorize :update_security_orchestration_policy_project
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::ID_TYPE,
required: true, required: true,
......
...@@ -195,6 +195,10 @@ module EE ...@@ -195,6 +195,10 @@ module EE
end end
end end
def can_update_security_orchestration_policy_project?(project)
can?(current_user, :update_security_orchestration_policy_project, project)
end
def can_create_feedback?(project, feedback_type) def can_create_feedback?(project, feedback_type)
feedback = Vulnerabilities::Feedback.new(project: project, feedback_type: feedback_type) feedback = Vulnerabilities::Feedback.new(project: project, feedback_type: feedback_type)
can?(current_user, :create_vulnerability_feedback, feedback) can?(current_user, :create_vulnerability_feedback, feedback)
......
...@@ -37,11 +37,12 @@ module EE ...@@ -37,11 +37,12 @@ module EE
@project&.namespace || @group @project&.namespace || @group
end end
def license_message(signed_in: signed_in?, is_admin: current_user&.admin?, license: License.current) def license_message(signed_in: signed_in?, is_admin: current_user&.admin?, license: License.current, force_notification: false)
::Gitlab::ExpiringSubscriptionMessage.new( ::Gitlab::ExpiringSubscriptionMessage.new(
subscribable: license, subscribable: license,
signed_in: signed_in, signed_in: signed_in,
is_admin: is_admin is_admin: is_admin,
force_notification: force_notification
).message ).message
end end
......
...@@ -292,8 +292,12 @@ module EE ...@@ -292,8 +292,12 @@ module EE
end end
def with_slack_application_disabled def with_slack_application_disabled
joins('LEFT JOIN services ON services.project_id = projects.id AND services.type = \'GitlabSlackApplicationService\' AND services.active IS true') joins(<<~SQL)
.where(services: { id: nil }) LEFT JOIN #{::Integration.table_name} ON #{::Integration.table_name}.project_id = projects.id
AND #{::Integration.table_name}.type = 'GitlabSlackApplicationService'
AND #{::Integration.table_name}.active IS true
SQL
.where(integrations: { id: nil })
end end
override :with_web_entity_associations override :with_web_entity_associations
......
...@@ -195,6 +195,10 @@ module EE ...@@ -195,6 +195,10 @@ module EE
enable :security_orchestration_policies enable :security_orchestration_policies
end end
rule { security_orchestration_policies_enabled & can?(:owner_access) }.policy do
enable :update_security_orchestration_policy_project
end
rule { security_dashboard_enabled & can?(:developer_access) }.policy do rule { security_dashboard_enabled & can?(:developer_access) }.policy do
enable :read_security_resource enable :read_security_resource
enable :read_vulnerability_scanner enable :read_vulnerability_scanner
......
...@@ -7,8 +7,11 @@ ...@@ -7,8 +7,11 @@
= s_('SecurityOrchestration|Security policy project') = s_('SecurityOrchestration|Security policy project')
%p %p
= project_select_tag('orchestration[policy_project_id]', class: 'hidden-filter-value', toggle_class: 'js-project-search js-project-filter js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit', = project_select_tag('orchestration[policy_project_id]', class: 'hidden-filter-value', toggle_class: 'js-project-search js-project-filter js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
placeholder: _('Select project'), idAttribute: 'id', data: { order_by: 'last_activity_at', idattribute: 'id', simple_filter: true, allow_clear: true, include_groups: false, include_projects_in_subgroups: true, user_id: current_user.id }, value: @assigned_policy_id) placeholder: _('Select project'), idAttribute: 'id', disabled: !can_update_security_orchestration_policy_project?(@project), data: { order_by: 'last_activity_at', idattribute: 'id', simple_filter: true, allow_clear: true, include_groups: false, include_projects_in_subgroups: true, user_id: current_user.id }, value: @assigned_policy_id)
.text-muted .text-muted
= html_escape(s_('SecurityOrchestration|A security policy project can be used enforce policies for a given project, group, or instance. It allows you to specify security policies that are important to you and enforce them with every commit.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe } = html_escape(s_('SecurityOrchestration|A security policy project can be used enforce policies for a given project, group, or instance. It allows you to specify security policies that are important to you and enforce them with every commit.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
= link_to _('More information'), help_page_path('user/project/clusters/protect/container_network_security/quick_start_guide'), target: '_blank' = link_to _('More information'), help_page_path('user/project/clusters/protect/container_network_security/quick_start_guide'), target: '_blank'
- if can_update_security_orchestration_policy_project?(@project)
= field.submit _('Save changes'), class: 'btn gl-button btn-success' = field.submit _('Save changes'), class: 'btn gl-button btn-success'
- else
= field.submit _('Save changes'), class: 'btn gl-button btn-success has-tooltip', disabled: true, title: _('Only owners can update Security Policy Project')
...@@ -157,7 +157,7 @@ module EE ...@@ -157,7 +157,7 @@ module EE
def check_if_license_blocks_changes! def check_if_license_blocks_changes!
if ::License.block_changes? if ::License.block_changes?
message = license_message(signed_in: true, is_admin: (user && user.admin?)) message = license_message(signed_in: true, is_admin: (user && user.admin?), force_notification: true)
raise ::Gitlab::GitAccess::ForbiddenError, strip_tags(message) raise ::Gitlab::GitAccess::ForbiddenError, strip_tags(message)
end end
end end
......
...@@ -7,15 +7,16 @@ module Gitlab ...@@ -7,15 +7,16 @@ module Gitlab
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
include ActionView::Helpers::TextHelper include ActionView::Helpers::TextHelper
attr_reader :subscribable, :signed_in, :is_admin, :namespace attr_reader :subscribable, :signed_in, :is_admin, :namespace, :force_notification
delegate :auto_renew, to: :subscribable delegate :auto_renew, to: :subscribable
def initialize(subscribable:, signed_in:, is_admin:, namespace: nil) def initialize(subscribable:, signed_in:, is_admin:, namespace: nil, force_notification: false)
@subscribable = subscribable @subscribable = subscribable
@signed_in = signed_in @signed_in = signed_in
@is_admin = is_admin @is_admin = is_admin
@namespace = namespace @namespace = namespace
@force_notification = force_notification
end end
def message def message
...@@ -39,7 +40,7 @@ module Gitlab ...@@ -39,7 +40,7 @@ module Gitlab
end end
def expired_subject def expired_subject
if show_downgrade_messaging? if show_downgrade_messaging? && namespace
if auto_renew if auto_renew
_('Something went wrong with your automatic subscription renewal.') _('Something went wrong with your automatic subscription renewal.')
else else
...@@ -71,7 +72,7 @@ module Gitlab ...@@ -71,7 +72,7 @@ module Gitlab
def block_changes_message def block_changes_message
return namespace_block_changes_message if namespace return namespace_block_changes_message if namespace
_('You didn\'t renew your subscription so it was downgraded to the GitLab Core Plan.') _('Please delete your current license if you want to downgrade to the free plan.')
end end
def namespace_block_changes_message def namespace_block_changes_message
...@@ -127,6 +128,7 @@ module Gitlab ...@@ -127,6 +128,7 @@ module Gitlab
def require_notification? def require_notification?
return false if expiring_auto_renew? || ::License.future_dated.present? return false if expiring_auto_renew? || ::License.future_dated.present?
return true if force_notification && subscribable.block_changes?
auto_renew_choice_exists? && expired_subscribable_within_notification_window? && !subscription_future_renewal? auto_renew_choice_exists? && expired_subscribable_within_notification_window? && !subscription_future_renewal?
end end
...@@ -154,7 +156,11 @@ module Gitlab ...@@ -154,7 +156,11 @@ module Gitlab
end end
def show_downgrade_messaging? def show_downgrade_messaging?
subscribable.block_changes? && (self_managed? || plan_downgraded?) if self_managed?
subscribable.block_changes?
else
subscribable.block_changes? && plan_downgraded?
end
end end
def strong def strong
......
...@@ -48,7 +48,7 @@ RSpec.describe "Admin views license" do ...@@ -48,7 +48,7 @@ RSpec.describe "Admin views license" do
travel_to(reference_date) do travel_to(reference_date) do
visit(admin_license_path) visit(admin_license_path)
expect(page).to have_content "You have 7 days to renew your subscription." expect(page).to have_content 'You have 7 days to renew your subscription.'
expect(page).to have_link 'Renew subscription', href: "#{EE::SUBSCRIPTIONS_URL}/subscriptions" expect(page).to have_link 'Renew subscription', href: "#{EE::SUBSCRIPTIONS_URL}/subscriptions"
end end
end end
...@@ -61,7 +61,7 @@ RSpec.describe "Admin views license" do ...@@ -61,7 +61,7 @@ RSpec.describe "Admin views license" do
travel_to(reference_date) do travel_to(reference_date) do
visit(admin_license_path) visit(admin_license_path)
expect(page).to have_content "You didn't renew your subscription so it was downgraded to the GitLab Core Plan" expect(page).to have_content 'Please delete your current license if you want to downgrade to the free plan'
expect(page).to have_link 'Upgrade your plan', href: "#{EE::SUBSCRIPTIONS_URL}/subscriptions" expect(page).to have_link 'Upgrade your plan', href: "#{EE::SUBSCRIPTIONS_URL}/subscriptions"
end end
end end
......
...@@ -3,14 +3,17 @@ ...@@ -3,14 +3,17 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
describe '#resolve' do describe '#resolve' do
let_it_be(:owner) { create(:user) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) } let_it_be(:project) { create(:project, namespace: owner.namespace) }
let_it_be(:policy_project) { create(:project) } let_it_be(:policy_project) { create(:project) }
let_it_be(:policy_project_id) { GitlabSchema.id_from_object(policy_project) } let_it_be(:policy_project_id) { GitlabSchema.id_from_object(policy_project) }
let(:current_user) { owner }
subject { mutation.resolve(project_path: project.full_path, security_policy_project_id: policy_project_id) } subject { mutation.resolve(project_path: project.full_path, security_policy_project_id: policy_project_id) }
context 'when feature is enabled and permission is set for user' do context 'when feature is enabled and permission is set for user' do
...@@ -19,6 +22,7 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do ...@@ -19,6 +22,7 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do
stub_feature_flags(security_orchestration_policies_configuration: true) stub_feature_flags(security_orchestration_policies_configuration: true)
end end
context 'when user is an owner of the project' do
it 'assigns the security policy project' do it 'assigns the security policy project' do
result = subject result = subject
...@@ -28,6 +32,19 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do ...@@ -28,6 +32,19 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do
end end
end end
context 'when user is not an owner' do
let(:current_user) { user }
before do
project.add_maintainer(user)
end
it 'raises exception' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
context 'when policy_project_id is invalid' do context 'when policy_project_id is invalid' do
let_it_be(:policy_project_id) { 'invalid' } let_it_be(:policy_project_id) { 'invalid' }
...@@ -47,7 +64,7 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do ...@@ -47,7 +64,7 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do
end end
end end
context 'when permission is not enabled' do context 'when feature is not licensed' do
before do before do
stub_licensed_features(security_orchestration_policies: false) stub_licensed_features(security_orchestration_policies: false)
end end
......
...@@ -2,22 +2,26 @@ ...@@ -2,22 +2,26 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Mutations::SecurityPolicy::CreateSecurityPolicyProject do RSpec.describe Mutations::SecurityPolicy::CreateSecurityPolicyProject do
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
describe '#resolve' do describe '#resolve' do
let_it_be(:owner) { create(:user) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, namespace: user.namespace) } let_it_be(:project) { create(:project, namespace: owner.namespace) }
let(:current_user) { owner }
subject { mutation.resolve(project_path: project.full_path) } subject { mutation.resolve(project_path: project.full_path) }
context 'when feature is enabled and permission is set for user' do context 'when feature is enabled and permission is set for user' do
before do before do
project.add_maintainer(user)
stub_licensed_features(security_orchestration_policies: true) stub_licensed_features(security_orchestration_policies: true)
stub_feature_flags(security_orchestration_policies_configuration: true) stub_feature_flags(security_orchestration_policies_configuration: true)
end end
context 'when user is an owner of the project' do
let(:current_user) { owner }
it 'returns project' do it 'returns project' do
result = subject result = subject
...@@ -26,6 +30,19 @@ RSpec.describe Mutations::SecurityPolicy::CreateSecurityPolicyProject do ...@@ -26,6 +30,19 @@ RSpec.describe Mutations::SecurityPolicy::CreateSecurityPolicyProject do
end end
end end
context 'when user is not an owner' do
let(:current_user) { user }
before do
project.add_maintainer(user)
end
it 'raises exception' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
context 'when feature is disabled' do context 'when feature is disabled' do
before do before do
stub_licensed_features(security_orchestration_policies: true) stub_licensed_features(security_orchestration_policies: true)
...@@ -37,7 +54,7 @@ RSpec.describe Mutations::SecurityPolicy::CreateSecurityPolicyProject do ...@@ -37,7 +54,7 @@ RSpec.describe Mutations::SecurityPolicy::CreateSecurityPolicyProject do
end end
end end
context 'when permission is not enabled' do context 'when feature is not licensed' do
before do before do
stub_licensed_features(security_orchestration_policies: false) stub_licensed_features(security_orchestration_policies: false)
end end
......
...@@ -188,7 +188,8 @@ RSpec.describe EE::SubscribableBannerHelper do ...@@ -188,7 +188,8 @@ RSpec.describe EE::SubscribableBannerHelper do
expect(Gitlab::ExpiringSubscriptionMessage).to receive(:new).with( expect(Gitlab::ExpiringSubscriptionMessage).to receive(:new).with(
subscribable: license, subscribable: license,
signed_in: true, signed_in: true,
is_admin: false is_admin: false,
force_notification: false
).and_return(message_mock) ).and_return(message_mock)
expect(message_mock).to receive(:message) expect(message_mock).to receive(:message)
......
...@@ -20,6 +20,24 @@ RSpec.describe ProjectsHelper do ...@@ -20,6 +20,24 @@ RSpec.describe ProjectsHelper do
end end
end end
describe '#can_update_security_orchestration_policy_project?' do
let(:owner) { project.owner }
before do
allow(helper).to receive(:current_user) { owner }
end
it 'returns false when user cannot update security orchestration policy project' do
allow(helper).to receive(:can?).with(owner, :update_security_orchestration_policy_project, project) { false }
expect(helper.can_update_security_orchestration_policy_project?(project)).to eq false
end
it 'returns true when user can update security orchestration policy project' do
allow(helper).to receive(:can?).with(owner, :update_security_orchestration_policy_project, project) { true }
expect(helper.can_update_security_orchestration_policy_project?(project)).to eq true
end
end
describe '#can_import_members?' do describe '#can_import_members?' do
let(:owner) { project.owner } let(:owner) { project.owner }
......
...@@ -12,12 +12,14 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do ...@@ -12,12 +12,14 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
let(:subscribable) { double(:license) } let(:subscribable) { double(:license) }
let(:namespace) { nil } let(:namespace) { nil }
let(:force_notification) { false }
let(:message) do let(:message) do
described_class.new( described_class.new(
subscribable: subscribable, subscribable: subscribable,
signed_in: true, signed_in: true,
is_admin: true, is_admin: true,
namespace: namespace namespace: namespace,
force_notification: force_notification
).message ).message
end end
...@@ -100,12 +102,12 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do ...@@ -100,12 +102,12 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
end end
it 'has a nice subject' do it 'has a nice subject' do
expect(subject).to include('Your subscription has been downgraded.') expect(subject).to include('Your subscription expired!')
end end
context 'no namespace' do context 'no namespace' do
it 'has an expiration blocking message' do it 'has an expiration blocking message' do
expect(subject).to include("You didn't renew your subscription so it was downgraded to the GitLab Core Plan") expect(subject).to include('Please delete your current license if you want to downgrade to the free plan')
end end
end end
...@@ -316,7 +318,7 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do ...@@ -316,7 +318,7 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
let(:grace_period_effective_from) { today.to_date - 40.days } let(:grace_period_effective_from) { today.to_date - 40.days }
it 'has a nice subject' do it 'has a nice subject' do
expect(subject).to include('Your subscription has been downgraded') expect(subject).to include('Your subscription expired!')
end end
end end
...@@ -326,6 +328,14 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do ...@@ -326,6 +328,14 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
it 'stops displaying' do it 'stops displaying' do
expect(subject).to be nil expect(subject).to be nil
end end
context 'and force_notification is true' do
let(:force_notification) { true }
it 'returns a message' do
expect(subject).to include('Your subscription expired!')
end
end
end end
context 'and not past the cutoff date' do context 'and not past the cutoff date' do
......
...@@ -748,13 +748,25 @@ RSpec.describe ProjectPolicy do ...@@ -748,13 +748,25 @@ RSpec.describe ProjectPolicy do
stub_licensed_features(security_orchestration_policies: true) stub_licensed_features(security_orchestration_policies: true)
end end
context 'with developer or higher role' do context 'with developer or maintainer role' do
where(role: %w[owner maintainer developer]) where(role: %w[maintainer developer])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:security_orchestration_policies) }
it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
end
end
context 'with owner role' do
where(role: %w[owner])
with_them do with_them do
let(:current_user) { public_send(role) } let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:security_orchestration_policies) } it { is_expected.to be_allowed(:security_orchestration_policies) }
it { is_expected.to be_allowed(:update_security_orchestration_policy_project) }
end end
end end
end end
......
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Projects::Security::PoliciesController, type: :request do RSpec.describe Projects::Security::PoliciesController, type: :request do
let_it_be(:project, reload: true) { create(:project) } let_it_be(:owner) { create(:user) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: owner.namespace) }
before do before do
project.add_developer(user) project.add_developer(user)
...@@ -45,6 +46,20 @@ RSpec.describe Projects::Security::PoliciesController, type: :request do ...@@ -45,6 +46,20 @@ RSpec.describe Projects::Security::PoliciesController, type: :request do
stub_licensed_features(security_orchestration_policies: true) stub_licensed_features(security_orchestration_policies: true)
end end
context 'when user is not an owner of the project' do
it 'returns error message' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } }
expect(response).to have_gitlab_http_status(:not_found)
expect(response).not_to render_template('new')
end
end
context 'when user is an owner of the project' do
before do
login_as(owner)
end
it 'assigns policy project to project' do it 'assigns policy project to project' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } } post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } }
...@@ -58,4 +73,5 @@ RSpec.describe Projects::Security::PoliciesController, type: :request do ...@@ -58,4 +73,5 @@ RSpec.describe Projects::Security::PoliciesController, type: :request do
expect(flash[:alert]).to eq 'Policy project doesn\'t exist' expect(flash[:alert]).to eq 'Policy project doesn\'t exist'
end end
end end
end
end end
...@@ -10,9 +10,7 @@ module Gitlab ...@@ -10,9 +10,7 @@ module Gitlab
# TABLES_TO_BE_RENAMED = { # TABLES_TO_BE_RENAMED = {
# 'old_name' => 'new_name' # 'old_name' => 'new_name'
# }.freeze # }.freeze
TABLES_TO_BE_RENAMED = { TABLES_TO_BE_RENAMED = {}.freeze
'services' => 'integrations'
}.freeze
# Minimum PostgreSQL version requirement per documentation: # Minimum PostgreSQL version requirement per documentation:
# https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements # https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements
......
...@@ -15,7 +15,12 @@ module Gitlab ...@@ -15,7 +15,12 @@ module Gitlab
context_for_args = worker_class.context_for_arguments(job['args']) context_for_args = worker_class.context_for_arguments(job['args'])
wrap_in_optional_context(context_for_args, &block) wrap_in_optional_context(context_for_args) do
# This should be inside the context for the arguments so
# that we don't override the feature category on the worker
# with the one from the caller.
Gitlab::ApplicationContext.with_context(feature_category: worker_class.get_feature_category.to_s, &block)
end
end end
end end
end end
......
...@@ -22972,6 +22972,9 @@ msgstr "" ...@@ -22972,6 +22972,9 @@ msgstr ""
msgid "Only include features new to your current subscription tier." msgid "Only include features new to your current subscription tier."
msgstr "" msgstr ""
msgid "Only owners can update Security Policy Project"
msgstr ""
msgid "Only policy:" msgid "Only policy:"
msgstr "" msgstr ""
...@@ -24463,6 +24466,9 @@ msgstr "" ...@@ -24463,6 +24466,9 @@ msgstr ""
msgid "Please create an index before enabling indexing" msgid "Please create an index before enabling indexing"
msgstr "" msgstr ""
msgid "Please delete your current license if you want to downgrade to the free plan."
msgstr ""
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}" msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr "" msgstr ""
...@@ -37421,9 +37427,6 @@ msgstr "" ...@@ -37421,9 +37427,6 @@ msgstr ""
msgid "You didn't renew your subscription for %{strong}%{namespace_name}%{strong_close} so it was downgraded to the free plan." msgid "You didn't renew your subscription for %{strong}%{namespace_name}%{strong_close} so it was downgraded to the free plan."
msgstr "" msgstr ""
msgid "You didn't renew your subscription so it was downgraded to the GitLab Core Plan."
msgstr ""
msgid "You do not have an active license" msgid "You do not have an active license"
msgstr "" msgstr ""
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl do RSpec.describe Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl, schema: 20210421163509 do
let(:services_table) { table(:services) } let(:services_table) { table(:services) }
let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') } let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') }
let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') } let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') }
......
...@@ -9,7 +9,7 @@ RSpec.describe Gitlab::Integrations::StiType do ...@@ -9,7 +9,7 @@ RSpec.describe Gitlab::Integrations::StiType do
context 'SQL SELECT' do context 'SQL SELECT' do
let(:expected_sql) do let(:expected_sql) do
<<~SQL.strip <<~SQL.strip
SELECT "services".* FROM "services" WHERE "services"."type" = 'AsanaService' SELECT "integrations".* FROM "integrations" WHERE "integrations"."type" = 'AsanaService'
SQL SQL
end end
...@@ -25,7 +25,7 @@ RSpec.describe Gitlab::Integrations::StiType do ...@@ -25,7 +25,7 @@ RSpec.describe Gitlab::Integrations::StiType do
context 'SQL CREATE' do context 'SQL CREATE' do
let(:expected_sql) do let(:expected_sql) do
<<~SQL.strip <<~SQL.strip
INSERT INTO "services" ("type") VALUES ('AsanaService') INSERT INTO "integrations" ("type") VALUES ('AsanaService')
SQL SQL
end end
...@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Integrations::StiType do ...@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Integrations::StiType do
context 'SQL UPDATE' do context 'SQL UPDATE' do
let(:expected_sql) do let(:expected_sql) do
<<~SQL.strip <<~SQL.strip
UPDATE "services" SET "type" = 'AsanaService' UPDATE "integrations" SET "type" = 'AsanaService'
SQL SQL
end end
...@@ -61,7 +61,7 @@ RSpec.describe Gitlab::Integrations::StiType do ...@@ -61,7 +61,7 @@ RSpec.describe Gitlab::Integrations::StiType do
context 'SQL DELETE' do context 'SQL DELETE' do
let(:expected_sql) do let(:expected_sql) do
<<~SQL.strip <<~SQL.strip
DELETE FROM "services" WHERE "services"."type" = 'AsanaService' DELETE FROM "integrations" WHERE "integrations"."type" = 'AsanaService'
SQL SQL
end end
......
...@@ -11,6 +11,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do ...@@ -11,6 +11,8 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do
include ApplicationWorker include ApplicationWorker
feature_category :issue_tracking
def self.job_for_args(args) def self.job_for_args(args)
jobs.find { |job| job['args'] == args } jobs.find { |job| job['args'] == args }
end end
...@@ -41,5 +43,39 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do ...@@ -41,5 +43,39 @@ RSpec.describe Gitlab::SidekiqMiddleware::WorkerContext::Client do
expect(job1['meta.user']).to eq(user_per_job['job1'].username) expect(job1['meta.user']).to eq(user_per_job['job1'].username)
expect(job2['meta.user']).to eq(user_per_job['job2'].username) expect(job2['meta.user']).to eq(user_per_job['job2'].username)
end end
context 'when the feature category is set in the context_proc' do
it 'takes the feature category from the worker, not the caller' do
TestWithContextWorker.bulk_perform_async_with_contexts(
%w(job1 job2),
arguments_proc: -> (name) { [name, 1, 2, 3] },
context_proc: -> (_) { { feature_category: 'code_review' } }
)
job1 = TestWithContextWorker.job_for_args(['job1', 1, 2, 3])
job2 = TestWithContextWorker.job_for_args(['job2', 1, 2, 3])
expect(job1['meta.feature_category']).to eq('issue_tracking')
expect(job2['meta.feature_category']).to eq('issue_tracking')
end
end
context 'when the feature category is already set in the surrounding block' do
it 'takes the feature category from the worker, not the caller' do
Gitlab::ApplicationContext.with_context(feature_category: 'authentication_and_authorization') do
TestWithContextWorker.bulk_perform_async_with_contexts(
%w(job1 job2),
arguments_proc: -> (name) { [name, 1, 2, 3] },
context_proc: -> (_) { {} }
)
end
job1 = TestWithContextWorker.job_for_args(['job1', 1, 2, 3])
job2 = TestWithContextWorker.job_for_args(['job2', 1, 2, 3])
expect(job1['meta.feature_category']).to eq('issue_tracking')
expect(job2['meta.feature_category']).to eq('issue_tracking')
end
end
end end
end end
This diff is collapsed.
...@@ -139,61 +139,40 @@ RSpec.describe Integration do ...@@ -139,61 +139,40 @@ RSpec.describe Integration do
end end
end end
describe "Test Button" do
let(:integration) { build(:service, project: project) }
describe '#can_test?' do describe '#can_test?' do
subject { integration.can_test? } subject { integration.can_test? }
context 'when project-level integration' do context 'when integration is project-level' do
let(:project) { create(:project) } let(:integration) { build(:service, project: project) }
it { is_expected.to be true } it { is_expected.to be true }
end end
context 'when instance-level integration' do context 'when integration is not project-level' do
Integration.available_integration_types.each do |type| let(:integration) { build(:service, project: nil) }
let(:integration) do
described_class.send(:integration_type_to_model, type).new(instance: true)
end
it { is_expected.to be false }
end
end
context 'when group-level integration' do
Integration.available_integration_types.each do |type|
let(:integration) do
described_class.send(:integration_type_to_model, type).new(group_id: group.id)
end
it { is_expected.to be false } it { is_expected.to be false }
end end
end end
end
describe '#test' do describe '#test' do
let(:integration) { build(:service, project: project) }
let(:data) { 'test' } let(:data) { 'test' }
context 'when repository is not empty' do it 'calls #execute' do
let(:project) { build(:project, :repository) }
it 'test runs execute' do
expect(integration).to receive(:execute).with(data) expect(integration).to receive(:execute).with(data)
integration.test(data) integration.test(data)
end end
end
context 'when repository is empty' do it 'returns a result' do
let(:project) { build(:project) } result = 'foo'
allow(integration).to receive(:execute).with(data).and_return(result)
it 'test runs execute' do expect(integration.test(data)).to eq(
expect(integration).to receive(:execute).with(data) success: true,
result: result
integration.test(data) )
end
end
end end
end end
......
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