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>
<gl-button
v-if="commit || startVersion"
:href="latestVersionPath"
variant="default"
class="js-latest-version"
:class="{ 'gl-ml-3': commit && !hasNeighborCommits }"
>
{{ __('Show latest version') }}
</gl-button>
<div v-if="hasChanges" class="inline-parallel-buttons d-none d-md-flex ml-auto"> <div v-if="hasChanges" class="inline-parallel-buttons d-none d-md-flex ml-auto">
<diff-stats <diff-stats
:diff-files-count-text="diffFilesCountText" :diff-files-count-text="diffFilesCountText"
:added-lines="addedLines" :added-lines="addedLines"
:removed-lines="removedLines" :removed-lines="removedLines"
/> />
<gl-button
v-if="commit || startVersion"
:href="latestVersionPath"
variant="default"
class="gl-mr-3 js-latest-version"
>
{{ __('Show latest version') }}
</gl-button>
<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
...@@ -18,7 +18,7 @@ UPDATE projects SET has_external_issue_tracker = ( ...@@ -18,7 +18,7 @@ UPDATE projects SET has_external_issue_tracker = (
EXISTS EXISTS
( (
SELECT 1 SELECT 1
FROM services FROM integrations
WHERE project_id = COALESCE(NEW.project_id, OLD.project_id) WHERE project_id = COALESCE(NEW.project_id, OLD.project_id)
AND active = TRUE AND active = TRUE
AND category = 'issue_tracker' AND category = 'issue_tracker'
...@@ -14010,6 +14010,45 @@ CREATE SEQUENCE insights_id_seq ...@@ -14010,6 +14010,45 @@ CREATE SEQUENCE insights_id_seq
ALTER SEQUENCE insights_id_seq OWNED BY insights.id; ALTER SEQUENCE insights_id_seq OWNED BY insights.id;
CREATE TABLE integrations (
id integer NOT NULL,
type character varying,
project_id integer,
created_at timestamp without time zone,
updated_at timestamp without time zone,
active boolean DEFAULT false NOT NULL,
properties text,
push_events boolean DEFAULT true,
issues_events boolean DEFAULT true,
merge_requests_events boolean DEFAULT true,
tag_push_events boolean DEFAULT true,
note_events boolean DEFAULT true NOT NULL,
category character varying DEFAULT 'common'::character varying NOT NULL,
wiki_page_events boolean DEFAULT true,
pipeline_events boolean DEFAULT false NOT NULL,
confidential_issues_events boolean DEFAULT true NOT NULL,
commit_events boolean DEFAULT true NOT NULL,
job_events boolean DEFAULT false NOT NULL,
confidential_note_events boolean DEFAULT true,
deployment_events boolean DEFAULT false NOT NULL,
comment_on_event_enabled boolean DEFAULT true NOT NULL,
template boolean DEFAULT false,
instance boolean DEFAULT false NOT NULL,
comment_detail smallint,
inherit_from_id bigint,
alert_events boolean,
group_id bigint
);
CREATE SEQUENCE integrations_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE integrations_id_seq OWNED BY integrations.id;
CREATE TABLE internal_ids ( CREATE TABLE internal_ids (
id bigint NOT NULL, id bigint NOT NULL,
project_id integer, project_id integer,
...@@ -17980,45 +18019,6 @@ CREATE TABLE service_desk_settings ( ...@@ -17980,45 +18019,6 @@ CREATE TABLE service_desk_settings (
project_key character varying(255) project_key character varying(255)
); );
CREATE TABLE services (
id integer NOT NULL,
type character varying,
project_id integer,
created_at timestamp without time zone,
updated_at timestamp without time zone,
active boolean DEFAULT false NOT NULL,
properties text,
push_events boolean DEFAULT true,
issues_events boolean DEFAULT true,
merge_requests_events boolean DEFAULT true,
tag_push_events boolean DEFAULT true,
note_events boolean DEFAULT true NOT NULL,
category character varying DEFAULT 'common'::character varying NOT NULL,
wiki_page_events boolean DEFAULT true,
pipeline_events boolean DEFAULT false NOT NULL,
confidential_issues_events boolean DEFAULT true NOT NULL,
commit_events boolean DEFAULT true NOT NULL,
job_events boolean DEFAULT false NOT NULL,
confidential_note_events boolean DEFAULT true,
deployment_events boolean DEFAULT false NOT NULL,
comment_on_event_enabled boolean DEFAULT true NOT NULL,
template boolean DEFAULT false,
instance boolean DEFAULT false NOT NULL,
comment_detail smallint,
inherit_from_id bigint,
alert_events boolean,
group_id bigint
);
CREATE SEQUENCE services_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE services_id_seq OWNED BY services.id;
CREATE TABLE shards ( CREATE TABLE shards (
id integer NOT NULL, id integer NOT NULL,
name character varying NOT NULL name character varying NOT NULL
...@@ -20094,6 +20094,8 @@ ALTER TABLE ONLY index_statuses ALTER COLUMN id SET DEFAULT nextval('index_statu ...@@ -20094,6 +20094,8 @@ ALTER TABLE ONLY index_statuses ALTER COLUMN id SET DEFAULT nextval('index_statu
ALTER TABLE ONLY insights ALTER COLUMN id SET DEFAULT nextval('insights_id_seq'::regclass); ALTER TABLE ONLY insights ALTER COLUMN id SET DEFAULT nextval('insights_id_seq'::regclass);
ALTER TABLE ONLY integrations ALTER COLUMN id SET DEFAULT nextval('integrations_id_seq'::regclass);
ALTER TABLE ONLY internal_ids ALTER COLUMN id SET DEFAULT nextval('internal_ids_id_seq'::regclass); ALTER TABLE ONLY internal_ids ALTER COLUMN id SET DEFAULT nextval('internal_ids_id_seq'::regclass);
ALTER TABLE ONLY ip_restrictions ALTER COLUMN id SET DEFAULT nextval('ip_restrictions_id_seq'::regclass); ALTER TABLE ONLY ip_restrictions ALTER COLUMN id SET DEFAULT nextval('ip_restrictions_id_seq'::regclass);
...@@ -20406,8 +20408,6 @@ ALTER TABLE ONLY sent_notifications ALTER COLUMN id SET DEFAULT nextval('sent_no ...@@ -20406,8 +20408,6 @@ ALTER TABLE ONLY sent_notifications ALTER COLUMN id SET DEFAULT nextval('sent_no
ALTER TABLE ONLY sentry_issues ALTER COLUMN id SET DEFAULT nextval('sentry_issues_id_seq'::regclass); ALTER TABLE ONLY sentry_issues ALTER COLUMN id SET DEFAULT nextval('sentry_issues_id_seq'::regclass);
ALTER TABLE ONLY services ALTER COLUMN id SET DEFAULT nextval('services_id_seq'::regclass);
ALTER TABLE ONLY shards ALTER COLUMN id SET DEFAULT nextval('shards_id_seq'::regclass); ALTER TABLE ONLY shards ALTER COLUMN id SET DEFAULT nextval('shards_id_seq'::regclass);
ALTER TABLE ONLY slack_integrations ALTER COLUMN id SET DEFAULT nextval('slack_integrations_id_seq'::regclass); ALTER TABLE ONLY slack_integrations ALTER COLUMN id SET DEFAULT nextval('slack_integrations_id_seq'::regclass);
...@@ -21483,6 +21483,9 @@ ALTER TABLE ONLY index_statuses ...@@ -21483,6 +21483,9 @@ ALTER TABLE ONLY index_statuses
ALTER TABLE ONLY insights ALTER TABLE ONLY insights
ADD CONSTRAINT insights_pkey PRIMARY KEY (id); ADD CONSTRAINT insights_pkey PRIMARY KEY (id);
ALTER TABLE ONLY integrations
ADD CONSTRAINT integrations_pkey PRIMARY KEY (id);
ALTER TABLE ONLY internal_ids ALTER TABLE ONLY internal_ids
ADD CONSTRAINT internal_ids_pkey PRIMARY KEY (id); ADD CONSTRAINT internal_ids_pkey PRIMARY KEY (id);
...@@ -22050,9 +22053,6 @@ ALTER TABLE ONLY serverless_domain_cluster ...@@ -22050,9 +22053,6 @@ ALTER TABLE ONLY serverless_domain_cluster
ALTER TABLE ONLY service_desk_settings ALTER TABLE ONLY service_desk_settings
ADD CONSTRAINT service_desk_settings_pkey PRIMARY KEY (project_id); ADD CONSTRAINT service_desk_settings_pkey PRIMARY KEY (project_id);
ALTER TABLE ONLY services
ADD CONSTRAINT services_pkey PRIMARY KEY (id);
ALTER TABLE ONLY shards ALTER TABLE ONLY shards
ADD CONSTRAINT shards_pkey PRIMARY KEY (id); ADD CONSTRAINT shards_pkey PRIMARY KEY (id);
...@@ -23701,6 +23701,24 @@ CREATE INDEX index_insights_on_namespace_id ON insights USING btree (namespace_i ...@@ -23701,6 +23701,24 @@ CREATE INDEX index_insights_on_namespace_id ON insights USING btree (namespace_i
CREATE INDEX index_insights_on_project_id ON insights USING btree (project_id); CREATE INDEX index_insights_on_project_id ON insights USING btree (project_id);
CREATE INDEX index_integrations_on_inherit_from_id ON integrations USING btree (inherit_from_id);
CREATE INDEX index_integrations_on_project_and_type_where_inherit_null ON integrations USING btree (project_id, type) WHERE (inherit_from_id IS NULL);
CREATE UNIQUE INDEX index_integrations_on_project_id_and_type_unique ON integrations USING btree (project_id, type);
CREATE INDEX index_integrations_on_template ON integrations USING btree (template);
CREATE INDEX index_integrations_on_type ON integrations USING btree (type);
CREATE UNIQUE INDEX index_integrations_on_type_and_instance_partial ON integrations USING btree (type, instance) WHERE (instance = true);
CREATE UNIQUE INDEX index_integrations_on_type_and_template_partial ON integrations USING btree (type, template) WHERE (template = true);
CREATE INDEX index_integrations_on_type_id_when_active_and_project_id_not_nu ON integrations USING btree (type, id) WHERE ((active = true) AND (project_id IS NOT NULL));
CREATE UNIQUE INDEX index_integrations_on_unique_group_id_and_type ON integrations USING btree (group_id, type);
CREATE INDEX index_internal_ids_on_namespace_id ON internal_ids USING btree (namespace_id); CREATE INDEX index_internal_ids_on_namespace_id ON internal_ids USING btree (namespace_id);
CREATE INDEX index_internal_ids_on_project_id ON internal_ids USING btree (project_id); CREATE INDEX index_internal_ids_on_project_id ON internal_ids USING btree (project_id);
...@@ -24741,24 +24759,6 @@ CREATE INDEX index_serverless_domain_cluster_on_pages_domain_id ON serverless_do ...@@ -24741,24 +24759,6 @@ CREATE INDEX index_serverless_domain_cluster_on_pages_domain_id ON serverless_do
CREATE INDEX index_service_desk_enabled_projects_on_id_creator_id_created_at ON projects USING btree (id, creator_id, created_at) WHERE (service_desk_enabled = true); CREATE INDEX index_service_desk_enabled_projects_on_id_creator_id_created_at ON projects USING btree (id, creator_id, created_at) WHERE (service_desk_enabled = true);
CREATE INDEX index_services_on_inherit_from_id ON services USING btree (inherit_from_id);
CREATE INDEX index_services_on_project_and_type_where_inherit_null ON services USING btree (project_id, type) WHERE (inherit_from_id IS NULL);
CREATE UNIQUE INDEX index_services_on_project_id_and_type_unique ON services USING btree (project_id, type);
CREATE INDEX index_services_on_template ON services USING btree (template);
CREATE INDEX index_services_on_type ON services USING btree (type);
CREATE UNIQUE INDEX index_services_on_type_and_instance_partial ON services USING btree (type, instance) WHERE (instance = true);
CREATE UNIQUE INDEX index_services_on_type_and_template_partial ON services USING btree (type, template) WHERE (template = true);
CREATE INDEX index_services_on_type_id_when_active_and_project_id_not_null ON services USING btree (type, id) WHERE ((active = true) AND (project_id IS NOT NULL));
CREATE UNIQUE INDEX index_services_on_unique_group_id_and_type ON services USING btree (group_id, type);
CREATE UNIQUE INDEX index_shards_on_name ON shards USING btree (name); CREATE UNIQUE INDEX index_shards_on_name ON shards USING btree (name);
CREATE UNIQUE INDEX index_site_profile_secret_variables_on_site_profile_id_and_key ON dast_site_profile_secret_variables USING btree (dast_site_profile_id, key); CREATE UNIQUE INDEX index_site_profile_secret_variables_on_site_profile_id_and_key ON dast_site_profile_secret_variables USING btree (dast_site_profile_id, key);
...@@ -25579,20 +25579,20 @@ CREATE TRIGGER trigger_cf2f9e35f002 BEFORE INSERT OR UPDATE ON ci_build_trace_ch ...@@ -25579,20 +25579,20 @@ CREATE TRIGGER trigger_cf2f9e35f002 BEFORE INSERT OR UPDATE ON ci_build_trace_ch
CREATE TRIGGER trigger_f1ca8ec18d78 BEFORE INSERT OR UPDATE ON geo_job_artifact_deleted_events FOR EACH ROW EXECUTE FUNCTION trigger_f1ca8ec18d78(); CREATE TRIGGER trigger_f1ca8ec18d78 BEFORE INSERT OR UPDATE ON geo_job_artifact_deleted_events FOR EACH ROW EXECUTE FUNCTION trigger_f1ca8ec18d78();
CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON services FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker(); CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON integrations FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker();
CREATE TRIGGER trigger_has_external_issue_tracker_on_insert AFTER INSERT ON services FOR EACH ROW WHEN ((((new.category)::text = 'issue_tracker'::text) AND (new.active = true) AND (new.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker(); CREATE TRIGGER trigger_has_external_issue_tracker_on_insert AFTER INSERT ON integrations FOR EACH ROW WHEN ((((new.category)::text = 'issue_tracker'::text) AND (new.active = true) AND (new.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker();
CREATE TRIGGER trigger_has_external_issue_tracker_on_update AFTER UPDATE ON services FOR EACH ROW WHEN ((((new.category)::text = 'issue_tracker'::text) AND (old.active <> new.active) AND (new.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker(); CREATE TRIGGER trigger_has_external_issue_tracker_on_update AFTER UPDATE ON integrations FOR EACH ROW WHEN ((((new.category)::text = 'issue_tracker'::text) AND (old.active <> new.active) AND (new.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker();
CREATE TRIGGER trigger_has_external_wiki_on_delete AFTER DELETE ON services FOR EACH ROW WHEN ((((old.type)::text = 'ExternalWikiService'::text) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_wiki(); CREATE TRIGGER trigger_has_external_wiki_on_delete AFTER DELETE ON integrations FOR EACH ROW WHEN ((((old.type)::text = 'ExternalWikiService'::text) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_wiki();
CREATE TRIGGER trigger_has_external_wiki_on_insert AFTER INSERT ON services FOR EACH ROW WHEN (((new.active = true) AND ((new.type)::text = 'ExternalWikiService'::text) AND (new.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_wiki(); CREATE TRIGGER trigger_has_external_wiki_on_insert AFTER INSERT ON integrations FOR EACH ROW WHEN (((new.active = true) AND ((new.type)::text = 'ExternalWikiService'::text) AND (new.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_wiki();
CREATE TRIGGER trigger_has_external_wiki_on_update AFTER UPDATE ON services FOR EACH ROW WHEN ((((new.type)::text = 'ExternalWikiService'::text) AND (old.active <> new.active) AND (new.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_wiki(); CREATE TRIGGER trigger_has_external_wiki_on_update AFTER UPDATE ON integrations FOR EACH ROW WHEN ((((new.type)::text = 'ExternalWikiService'::text) AND (old.active <> new.active) AND (new.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_wiki();
ALTER TABLE ONLY chat_names ALTER TABLE ONLY chat_names
ADD CONSTRAINT fk_00797a2bf9 FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE; ADD CONSTRAINT fk_00797a2bf9 FOREIGN KEY (service_id) REFERENCES integrations(id) ON DELETE CASCADE;
ALTER TABLE ONLY deployments ALTER TABLE ONLY deployments
ADD CONSTRAINT fk_009fd21147 FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE NOT VALID; ADD CONSTRAINT fk_009fd21147 FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE NOT VALID;
...@@ -25870,7 +25870,7 @@ ALTER TABLE ONLY terraform_state_versions ...@@ -25870,7 +25870,7 @@ ALTER TABLE ONLY terraform_state_versions
ALTER TABLE ONLY protected_branch_push_access_levels ALTER TABLE ONLY protected_branch_push_access_levels
ADD CONSTRAINT fk_7111b68cdb FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; ADD CONSTRAINT fk_7111b68cdb FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY services ALTER TABLE ONLY integrations
ADD CONSTRAINT fk_71cce407f9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; ADD CONSTRAINT fk_71cce407f9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY user_interacted_projects ALTER TABLE ONLY user_interacted_projects
...@@ -26216,7 +26216,7 @@ ALTER TABLE ONLY ci_builds ...@@ -26216,7 +26216,7 @@ ALTER TABLE ONLY ci_builds
ADD CONSTRAINT fk_d3130c9a7f FOREIGN KEY (commit_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE; ADD CONSTRAINT fk_d3130c9a7f FOREIGN KEY (commit_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
ALTER TABLE ONLY web_hooks ALTER TABLE ONLY web_hooks
ADD CONSTRAINT fk_d47999a98a FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE; ADD CONSTRAINT fk_d47999a98a FOREIGN KEY (service_id) REFERENCES integrations(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_sources_pipelines ALTER TABLE ONLY ci_sources_pipelines
ADD CONSTRAINT fk_d4e29af7d7 FOREIGN KEY (source_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE; ADD CONSTRAINT fk_d4e29af7d7 FOREIGN KEY (source_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
...@@ -26299,7 +26299,7 @@ ALTER TABLE ONLY vulnerability_statistics ...@@ -26299,7 +26299,7 @@ ALTER TABLE ONLY vulnerability_statistics
ALTER TABLE ONLY ci_triggers ALTER TABLE ONLY ci_triggers
ADD CONSTRAINT fk_e8e10d1964 FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE; ADD CONSTRAINT fk_e8e10d1964 FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY services ALTER TABLE ONLY integrations
ADD CONSTRAINT fk_e8fe908a34 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; ADD CONSTRAINT fk_e8fe908a34 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY pages_domains ALTER TABLE ONLY pages_domains
...@@ -26543,7 +26543,7 @@ ALTER TABLE ONLY group_wiki_repositories ...@@ -26543,7 +26543,7 @@ ALTER TABLE ONLY group_wiki_repositories
ADD CONSTRAINT fk_rails_19755e374b FOREIGN KEY (shard_id) REFERENCES shards(id) ON DELETE RESTRICT; ADD CONSTRAINT fk_rails_19755e374b FOREIGN KEY (shard_id) REFERENCES shards(id) ON DELETE RESTRICT;
ALTER TABLE ONLY open_project_tracker_data ALTER TABLE ONLY open_project_tracker_data
ADD CONSTRAINT fk_rails_1987546e48 FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_1987546e48 FOREIGN KEY (service_id) REFERENCES integrations(id) ON DELETE CASCADE;
ALTER TABLE ONLY gpg_signatures ALTER TABLE ONLY gpg_signatures
ADD CONSTRAINT fk_rails_19d4f1c6f9 FOREIGN KEY (gpg_key_subkey_id) REFERENCES gpg_key_subkeys(id) ON DELETE SET NULL; ADD CONSTRAINT fk_rails_19d4f1c6f9 FOREIGN KEY (gpg_key_subkey_id) REFERENCES gpg_key_subkeys(id) ON DELETE SET NULL;
...@@ -27125,7 +27125,7 @@ ALTER TABLE ONLY vulnerability_finding_evidence_requests ...@@ -27125,7 +27125,7 @@ ALTER TABLE ONLY vulnerability_finding_evidence_requests
ADD CONSTRAINT fk_rails_72c87c8eb6 FOREIGN KEY (vulnerability_finding_evidence_id) REFERENCES vulnerability_finding_evidences(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_72c87c8eb6 FOREIGN KEY (vulnerability_finding_evidence_id) REFERENCES vulnerability_finding_evidences(id) ON DELETE CASCADE;
ALTER TABLE ONLY slack_integrations ALTER TABLE ONLY slack_integrations
ADD CONSTRAINT fk_rails_73db19721a FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_73db19721a FOREIGN KEY (service_id) REFERENCES integrations(id) ON DELETE CASCADE;
ALTER TABLE ONLY custom_emoji ALTER TABLE ONLY custom_emoji
ADD CONSTRAINT fk_rails_745925b412 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_745925b412 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
...@@ -27392,7 +27392,7 @@ ALTER TABLE ONLY todos ...@@ -27392,7 +27392,7 @@ ALTER TABLE ONLY todos
ADD CONSTRAINT fk_rails_a27c483435 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_a27c483435 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY jira_tracker_data ALTER TABLE ONLY jira_tracker_data
ADD CONSTRAINT fk_rails_a299066916 FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_a299066916 FOREIGN KEY (service_id) REFERENCES integrations(id) ON DELETE CASCADE;
ALTER TABLE ONLY protected_environments ALTER TABLE ONLY protected_environments
ADD CONSTRAINT fk_rails_a354313d11 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_a354313d11 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
...@@ -27647,7 +27647,7 @@ ALTER TABLE ONLY operations_strategies_user_lists ...@@ -27647,7 +27647,7 @@ ALTER TABLE ONLY operations_strategies_user_lists
ADD CONSTRAINT fk_rails_ccb7e4bc0b FOREIGN KEY (user_list_id) REFERENCES operations_user_lists(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_ccb7e4bc0b FOREIGN KEY (user_list_id) REFERENCES operations_user_lists(id) ON DELETE CASCADE;
ALTER TABLE ONLY issue_tracker_data ALTER TABLE ONLY issue_tracker_data
ADD CONSTRAINT fk_rails_ccc0840427 FOREIGN KEY (service_id) REFERENCES services(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_ccc0840427 FOREIGN KEY (service_id) REFERENCES integrations(id) ON DELETE CASCADE;
ALTER TABLE ONLY vulnerability_finding_evidence_headers ALTER TABLE ONLY vulnerability_finding_evidence_headers
ADD CONSTRAINT fk_rails_ce7f121a03 FOREIGN KEY (vulnerability_finding_evidence_request_id) REFERENCES vulnerability_finding_evidence_requests(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_ce7f121a03 FOREIGN KEY (vulnerability_finding_evidence_request_id) REFERENCES vulnerability_finding_evidence_requests(id) ON DELETE CASCADE;
...@@ -27949,8 +27949,8 @@ ALTER TABLE ONLY resource_label_events ...@@ -27949,8 +27949,8 @@ ALTER TABLE ONLY resource_label_events
ALTER TABLE ONLY ci_builds_metadata ALTER TABLE ONLY ci_builds_metadata
ADD CONSTRAINT fk_rails_ffcf702a02 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_ffcf702a02 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY services ALTER TABLE ONLY integrations
ADD CONSTRAINT fk_services_inherit_from_id FOREIGN KEY (inherit_from_id) REFERENCES services(id) ON DELETE CASCADE; ADD CONSTRAINT fk_services_inherit_from_id FOREIGN KEY (inherit_from_id) REFERENCES integrations(id) ON DELETE CASCADE;
ALTER TABLE ONLY merge_requests ALTER TABLE ONLY merge_requests
ADD CONSTRAINT fk_source_project FOREIGN KEY (source_project_id) REFERENCES projects(id) ON DELETE SET NULL; ADD CONSTRAINT fk_source_project FOREIGN KEY (source_project_id) REFERENCES projects(id) ON DELETE SET NULL;
...@@ -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'
= field.submit _('Save changes'), class: 'btn gl-button btn-success' - if can_update_security_orchestration_policy_project?(@project)
= 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,12 +22,26 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do ...@@ -19,12 +22,26 @@ RSpec.describe Mutations::SecurityPolicy::AssignSecurityPolicyProject do
stub_feature_flags(security_orchestration_policies_configuration: true) stub_feature_flags(security_orchestration_policies_configuration: true)
end end
it 'assigns the security policy project' do context 'when user is an owner of the project' do
result = subject it 'assigns the security policy project' do
result = subject
expect(result[:errors]).to be_empty
expect(project.security_orchestration_policy_configuration).not_to be_nil
expect(project.security_orchestration_policy_configuration.security_policy_management_project).to eq(policy_project)
end
end
context 'when user is not an owner' do
let(:current_user) { user }
before do
project.add_maintainer(user)
end
expect(result[:errors]).to be_empty it 'raises exception' do
expect(project.security_orchestration_policy_configuration).not_to be_nil expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
expect(project.security_orchestration_policy_configuration.security_policy_management_project).to eq(policy_project) end
end end
end end
...@@ -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,27 +2,44 @@ ...@@ -2,27 +2,44 @@
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
it 'returns project' do context 'when user is an owner of the project' do
result = subject let(:current_user) { owner }
it 'returns project' do
result = subject
expect(result[:errors]).to be_empty
expect(result[:project]).to eq(Project.last)
end
end
context 'when user is not an owner' do
let(:current_user) { user }
before do
project.add_maintainer(user)
end
expect(result[:errors]).to be_empty it 'raises exception' do
expect(result[:project]).to eq(Project.last) expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end end
end end
...@@ -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 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_disallowed(:update_security_orchestration_policy_project) }
end
end
context 'with owner role' do
where(role: %w[owner])
with_them do
let(:current_user) { public_send(role) }
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,17 +46,32 @@ RSpec.describe Projects::Security::PoliciesController, type: :request do ...@@ -45,17 +46,32 @@ RSpec.describe Projects::Security::PoliciesController, type: :request do
stub_licensed_features(security_orchestration_policies: true) stub_licensed_features(security_orchestration_policies: true)
end end
it 'assigns policy project to project' do context 'when user is not an owner of the project' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } } it 'returns error message' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } }
expect(response).to redirect_to(project_security_policy_url(project)) expect(response).to have_gitlab_http_status(:not_found)
expect(project.security_orchestration_policy_configuration.security_policy_management_project_id).to eq(policy_project.id) expect(response).not_to render_template('new')
end
end end
it 'returns error message for invalid input' do context 'when user is an owner of the project' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: nil } } before do
login_as(owner)
end
it 'assigns policy project to project' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: policy_project.id } }
expect(flash[:alert]).to eq 'Policy project doesn\'t exist' expect(response).to redirect_to(project_security_policy_url(project))
expect(project.security_orchestration_policy_configuration.security_policy_management_project_id).to eq(policy_project.id)
end
it 'returns error message for invalid input' do
post assign_project_security_policy_url(project), params: { orchestration: { policy_project_id: nil } }
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
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe RenameServicesToIntegrations do
let(:migration) { described_class.new }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:integrations) { table(:integrations) }
let(:services) { table(:services) }
before do
@namespace = namespaces.create!(name: 'foo', path: 'foo')
@project = projects.create!(namespace_id: @namespace.id)
end
RSpec.shared_examples 'a table (or view) with triggers' do
describe 'INSERT tracker trigger' do
it 'sets `has_external_issue_tracker` to true when active `issue_tracker` is inserted' do
expect do
subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
end.to change { @project.reload.has_external_issue_tracker }.to(true)
end
it 'does not set `has_external_issue_tracker` to true when integration is for a different project' do
different_project = projects.create!(namespace_id: @namespace.id)
expect do
subject.create!(category: 'issue_tracker', active: true, project_id: different_project.id)
end.not_to change { @project.reload.has_external_issue_tracker }
end
it 'does not set `has_external_issue_tracker` to true when inactive `issue_tracker` is inserted' do
expect do
subject.create!(category: 'issue_tracker', active: false, project_id: @project.id)
end.not_to change { @project.reload.has_external_issue_tracker }
end
it 'does not set `has_external_issue_tracker` to true when a non-`issue tracker` active integration is inserted' do
expect do
subject.create!(category: 'my_type', active: true, project_id: @project.id)
end.not_to change { @project.reload.has_external_issue_tracker }
end
end
describe 'UPDATE tracker trigger' do
it 'sets `has_external_issue_tracker` to true when `issue_tracker` is made active' do
integration = subject.create!(category: 'issue_tracker', active: false, project_id: @project.id)
expect do
integration.update!(active: true)
end.to change { @project.reload.has_external_issue_tracker }.to(true)
end
it 'sets `has_external_issue_tracker` to false when `issue_tracker` is made inactive' do
integration = subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
expect do
integration.update!(active: false)
end.to change { @project.reload.has_external_issue_tracker }.to(false)
end
it 'sets `has_external_issue_tracker` to false when `issue_tracker` is made inactive, and an inactive `issue_tracker` exists' do
subject.create!(category: 'issue_tracker', active: false, project_id: @project.id)
integration = subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
expect do
integration.update!(active: false)
end.to change { @project.reload.has_external_issue_tracker }.to(false)
end
it 'does not change `has_external_issue_tracker` when `issue_tracker` is made inactive, if an active `issue_tracker` exists' do
subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
integration = subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
expect do
integration.update!(active: false)
end.not_to change { @project.reload.has_external_issue_tracker }
end
it 'does not change `has_external_issue_tracker` when integration is for a different project' do
different_project = projects.create!(namespace_id: @namespace.id)
integration = subject.create!(category: 'issue_tracker', active: false, project_id: different_project.id)
expect do
integration.update!(active: true)
end.not_to change { @project.reload.has_external_issue_tracker }
end
end
describe 'DELETE tracker trigger' do
it 'sets `has_external_issue_tracker` to false when `issue_tracker` is deleted' do
integration = subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
expect do
integration.delete
end.to change { @project.reload.has_external_issue_tracker }.to(false)
end
it 'sets `has_external_issue_tracker` to false when `issue_tracker` is deleted, if an inactive `issue_tracker` still exists' do
subject.create!(category: 'issue_tracker', active: false, project_id: @project.id)
integration = subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
expect do
integration.delete
end.to change { @project.reload.has_external_issue_tracker }.to(false)
end
it 'does not change `has_external_issue_tracker` when `issue_tracker` is deleted, if an active `issue_tracker` still exists' do
subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
integration = subject.create!(category: 'issue_tracker', active: true, project_id: @project.id)
expect do
integration.delete
end.not_to change { @project.reload.has_external_issue_tracker }
end
it 'does not change `has_external_issue_tracker` when integration is for a different project' do
different_project = projects.create!(namespace_id: @namespace.id)
integration = subject.create!(category: 'issue_tracker', active: true, project_id: different_project.id)
expect do
integration.delete
end.not_to change { @project.reload.has_external_issue_tracker }
end
end
describe 'INSERT wiki trigger' do
it 'sets `has_external_wiki` to true when active `ExternalWikiService` is inserted' do
expect do
subject.create!(type: 'ExternalWikiService', active: true, project_id: @project.id)
end.to change { @project.reload.has_external_wiki }.to(true)
end
it 'does not set `has_external_wiki` to true when integration is for a different project' do
different_project = projects.create!(namespace_id: @namespace.id)
expect do
subject.create!(type: 'ExternalWikiService', active: true, project_id: different_project.id)
end.not_to change { @project.reload.has_external_wiki }
end
it 'does not set `has_external_wiki` to true when inactive `ExternalWikiService` is inserted' do
expect do
subject.create!(type: 'ExternalWikiService', active: false, project_id: @project.id)
end.not_to change { @project.reload.has_external_wiki }
end
it 'does not set `has_external_wiki` to true when active other integration is inserted' do
expect do
subject.create!(type: 'MyService', active: true, project_id: @project.id)
end.not_to change { @project.reload.has_external_wiki }
end
end
describe 'UPDATE wiki trigger' do
it 'sets `has_external_wiki` to true when `ExternalWikiService` is made active' do
integration = subject.create!(type: 'ExternalWikiService', active: false, project_id: @project.id)
expect do
integration.update!(active: true)
end.to change { @project.reload.has_external_wiki }.to(true)
end
it 'sets `has_external_wiki` to false when `ExternalWikiService` is made inactive' do
integration = subject.create!(type: 'ExternalWikiService', active: true, project_id: @project.id)
expect do
integration.update!(active: false)
end.to change { @project.reload.has_external_wiki }.to(false)
end
it 'does not change `has_external_wiki` when integration is for a different project' do
different_project = projects.create!(namespace_id: @namespace.id)
integration = subject.create!(type: 'ExternalWikiService', active: false, project_id: different_project.id)
expect do
integration.update!(active: true)
end.not_to change { @project.reload.has_external_wiki }
end
end
describe 'DELETE wiki trigger' do
it 'sets `has_external_wiki` to false when `ExternalWikiService` is deleted' do
integration = subject.create!(type: 'ExternalWikiService', active: true, project_id: @project.id)
expect do
integration.delete
end.to change { @project.reload.has_external_wiki }.to(false)
end
it 'does not change `has_external_wiki` when integration is for a different project' do
different_project = projects.create!(namespace_id: @namespace.id)
integration = subject.create!(type: 'ExternalWikiService', active: true, project_id: different_project.id)
expect do
integration.delete
end.not_to change { @project.reload.has_external_wiki }
end
end
end
RSpec.shared_examples 'a table (or view) without triggers' do
specify do
number_of_triggers = ActiveRecord::Base.connection
.execute("SELECT count(*) FROM information_schema.triggers WHERE event_object_table = '#{subject.table_name}'")
.first['count']
expect(number_of_triggers).to eq(0)
end
end
describe '#up' do
before do
# LOCK TABLE statements must be in a transaction
ActiveRecord::Base.transaction { migrate! }
end
context 'the integrations table' do
subject { integrations }
it_behaves_like 'a table (or view) with triggers'
end
context 'the services table' do
subject { services }
it_behaves_like 'a table (or view) without triggers'
end
end
describe '#down' do
before do
# LOCK TABLE statements must be in a transaction
ActiveRecord::Base.transaction do
migration.up
migration.down
end
end
context 'the services table' do
subject { services }
it_behaves_like 'a table (or view) with triggers'
end
context 'the integrations table' do
subject { integrations }
it_behaves_like 'a table (or view) without triggers'
end
end
end
...@@ -139,61 +139,40 @@ RSpec.describe Integration do ...@@ -139,61 +139,40 @@ RSpec.describe Integration do
end end
end end
describe "Test Button" do describe '#can_test?' do
let(:integration) { build(:service, project: project) } subject { integration.can_test? }
describe '#can_test?' do
subject { integration.can_test? }
context 'when project-level integration' do
let(:project) { create(:project) }
it { is_expected.to be true }
end
context 'when instance-level integration' do
Integration.available_integration_types.each do |type|
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 context 'when integration is project-level' do
Integration.available_integration_types.each do |type| let(:integration) { build(:service, project: project) }
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 true }
end
end
end end
describe '#test' do context 'when integration is not project-level' do
let(:data) { 'test' } let(:integration) { build(:service, project: nil) }
context 'when repository is not empty' do it { is_expected.to be false }
let(:project) { build(:project, :repository) } end
end
it 'test runs execute' do describe '#test' do
expect(integration).to receive(:execute).with(data) let(:integration) { build(:service, project: project) }
let(:data) { 'test' }
integration.test(data) it 'calls #execute' do
end expect(integration).to receive(:execute).with(data)
end
context 'when repository is empty' do integration.test(data)
let(:project) { build(:project) } end
it 'test runs execute' do it 'returns a result' do
expect(integration).to receive(:execute).with(data) result = 'foo'
allow(integration).to receive(:execute).with(data).and_return(result)
integration.test(data) expect(integration.test(data)).to eq(
end success: true,
end result: result
)
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