Commit fa43d2e1 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents f59fcd60 795b4598
...@@ -14,7 +14,7 @@ import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees ...@@ -14,7 +14,7 @@ import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue'; import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dropdown.vue'; import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dropdown.vue';
import getIssueParticipants from '~/vue_shared/components/sidebar/queries/getIssueParticipants.query.graphql'; import getIssueParticipants from '~/vue_shared/components/sidebar/queries/getIssueParticipants.query.graphql';
import searchUsers from '~/boards/queries/users_search.query.graphql'; import searchUsers from '~/boards/graphql/users_search.query.graphql';
export default { export default {
noSearchDelay: 0, noSearchDelay: 0,
......
...@@ -12,8 +12,8 @@ import { ...@@ -12,8 +12,8 @@ import {
import httpStatusCodes from '~/lib/utils/http_status'; import httpStatusCodes from '~/lib/utils/http_status';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import projectQuery from '../queries/project_boards.query.graphql'; import projectQuery from '../graphql/project_boards.query.graphql';
import groupQuery from '../queries/group_boards.query.graphql'; import groupQuery from '../graphql/group_boards.query.graphql';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
import BoardForm from './board_form.vue'; import BoardForm from './board_form.vue';
......
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import { fetchPolicies } from '~/lib/graphql'; import { fetchPolicies } from '~/lib/graphql';
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue'; import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import groupMilestones from '../../queries/group_milestones.query.graphql'; import groupMilestones from '../../graphql/group_milestones.query.graphql';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
......
#import "ee_else_ce/boards/queries/board_list.fragment.graphql" #import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
mutation CreateBoardList( mutation CreateBoardList(
$boardId: BoardID! $boardId: BoardID!
......
#import "ee_else_ce/boards/queries/board_list.fragment.graphql" #import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
query ListIssues( query ListIssues(
$fullPath: ID! $fullPath: ID!
......
#import "ee_else_ce/boards/queries/board.fragment.graphql" #import "ee_else_ce/boards/graphql/board.fragment.graphql"
query group_boards($fullPath: ID!) { query group_boards($fullPath: ID!) {
group(fullPath: $fullPath) { group(fullPath: $fullPath) {
......
#import "ee_else_ce/boards/queries/issue.fragment.graphql" #import "ee_else_ce/boards/graphql/issue.fragment.graphql"
mutation CreateIssue($input: CreateIssueInput!) { mutation CreateIssue($input: CreateIssueInput!) {
createIssue(input: $input) { createIssue(input: $input) {
......
#import "ee_else_ce/boards/queries/issue.fragment.graphql" #import "ee_else_ce/boards/graphql/issue.fragment.graphql"
mutation IssueMoveList( mutation IssueMoveList(
$projectPath: ID! $projectPath: ID!
......
#import "ee_else_ce/boards/queries/issue.fragment.graphql" #import "ee_else_ce/boards/graphql/issue.fragment.graphql"
query ListIssues( query ListIssues(
$fullPath: ID! $fullPath: ID!
......
#import "ee_else_ce/boards/queries/board.fragment.graphql" #import "ee_else_ce/boards/graphql/board.fragment.graphql"
query project_boards($fullPath: ID!) { query project_boards($fullPath: ID!) {
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
......
import { pick } from 'lodash'; import { pick } from 'lodash';
import boardListsQuery from 'ee_else_ce/boards/queries/board_lists.query.graphql'; import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
import createGqClient, { fetchPolicies } from '~/lib/graphql'; import createGqClient, { fetchPolicies } from '~/lib/graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { BoardType, ListType, inactiveId, DEFAULT_LABELS } from '~/boards/constants'; import { BoardType, ListType, inactiveId, DEFAULT_LABELS } from '~/boards/constants';
...@@ -14,18 +14,18 @@ import { ...@@ -14,18 +14,18 @@ import {
} from '../boards_util'; } from '../boards_util';
import boardStore from '~/boards/stores/boards_store'; import boardStore from '~/boards/stores/boards_store';
import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql'; import updateAssigneesMutation from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
import listsIssuesQuery from '../queries/lists_issues.query.graphql'; import listsIssuesQuery from '../graphql/lists_issues.query.graphql';
import boardLabelsQuery from '../queries/board_labels.query.graphql'; import boardLabelsQuery from '../graphql/board_labels.query.graphql';
import createBoardListMutation from '../queries/board_list_create.mutation.graphql'; import createBoardListMutation from '../graphql/board_list_create.mutation.graphql';
import updateBoardListMutation from '../queries/board_list_update.mutation.graphql'; import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql';
import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql'; import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
import destroyBoardListMutation from '../queries/board_list_destroy.mutation.graphql'; import destroyBoardListMutation from '../graphql/board_list_destroy.mutation.graphql';
import issueCreateMutation from '../queries/issue_create.mutation.graphql'; import issueCreateMutation from '../graphql/issue_create.mutation.graphql';
import issueSetLabels from '../queries/issue_set_labels.mutation.graphql'; import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql';
import issueSetDueDate from '../queries/issue_set_due_date.mutation.graphql'; import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql';
import issueSetSubscriptionMutation from '../graphql/mutations/issue_set_subscription.mutation.graphql'; import issueSetSubscriptionMutation from '../graphql/issue_set_subscription.mutation.graphql';
import issueSetMilestone from '../queries/issue_set_milestone.mutation.graphql'; import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql';
const notImplemented = () => { const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */ /* eslint-disable-next-line @gitlab/require-i18n-strings */
...@@ -324,7 +324,7 @@ export default { ...@@ -324,7 +324,7 @@ export default {
return gqlClient return gqlClient
.mutate({ .mutate({
mutation: updateAssignees, mutation: updateAssigneesMutation,
variables: { variables: {
iid: getters.activeIssue.iid, iid: getters.activeIssue.iid,
projectPath: getters.activeIssue.referencePath.split('#')[0], projectPath: getters.activeIssue.referencePath.split('#')[0],
...@@ -350,7 +350,7 @@ export default { ...@@ -350,7 +350,7 @@ export default {
setActiveIssueMilestone: async ({ commit, getters }, input) => { setActiveIssueMilestone: async ({ commit, getters }, input) => {
const { activeIssue } = getters; const { activeIssue } = getters;
const { data } = await gqlClient.mutate({ const { data } = await gqlClient.mutate({
mutation: issueSetMilestone, mutation: issueSetMilestoneMutation,
variables: { variables: {
input: { input: {
iid: String(activeIssue.iid), iid: String(activeIssue.iid),
...@@ -416,7 +416,7 @@ export default { ...@@ -416,7 +416,7 @@ export default {
setActiveIssueLabels: async ({ commit, getters }, input) => { setActiveIssueLabels: async ({ commit, getters }, input) => {
const { activeIssue } = getters; const { activeIssue } = getters;
const { data } = await gqlClient.mutate({ const { data } = await gqlClient.mutate({
mutation: issueSetLabels, mutation: issueSetLabelsMutation,
variables: { variables: {
input: { input: {
iid: String(activeIssue.iid), iid: String(activeIssue.iid),
...@@ -441,7 +441,7 @@ export default { ...@@ -441,7 +441,7 @@ export default {
setActiveIssueDueDate: async ({ commit, getters }, input) => { setActiveIssueDueDate: async ({ commit, getters }, input) => {
const { activeIssue } = getters; const { activeIssue } = getters;
const { data } = await gqlClient.mutate({ const { data } = await gqlClient.mutate({
mutation: issueSetDueDate, mutation: issueSetDueDateMutation,
variables: { variables: {
input: { input: {
iid: String(activeIssue.iid), iid: String(activeIssue.iid),
......
...@@ -22,7 +22,7 @@ import ListLabel from '../models/label'; ...@@ -22,7 +22,7 @@ import ListLabel from '../models/label';
import ListAssignee from '../models/assignee'; import ListAssignee from '../models/assignee';
import ListMilestone from '../models/milestone'; import ListMilestone from '../models/milestone';
import createBoardMutation from '../queries/board.mutation.graphql'; import createBoardMutation from '../graphql/board.mutation.graphql';
const PER_PAGE = 20; const PER_PAGE = 20;
export const gqlClient = createDefaultClient(); export const gqlClient = createDefaultClient();
......
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import { camelCase, difference, union } from 'lodash'; import { camelCase, difference, union } from 'lodash';
import updateIssueLabelsMutation from '~/boards/queries/issue_set_labels.mutation.graphql'; import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { IssuableType } from '~/issue_show/constants'; import { IssuableType } from '~/issue_show/constants';
import { __ } from '~/locale'; import { __ } from '~/locale';
......
...@@ -70,6 +70,7 @@ class Service < ApplicationRecord ...@@ -70,6 +70,7 @@ class Service < ApplicationRecord
scope :by_type, -> (type) { where(type: type) } scope :by_type, -> (type) { where(type: type) }
scope :by_active_flag, -> (flag) { where(active: flag) } scope :by_active_flag, -> (flag) { where(active: flag) }
scope :inherit_from_id, -> (id) { where(inherit_from_id: id) } scope :inherit_from_id, -> (id) { where(inherit_from_id: id) }
scope :inherit, -> { where.not(inherit_from_id: nil) }
scope :for_group, -> (group) { where(group_id: group, type: available_services_types(include_project_specific: false)) } scope :for_group, -> (group) { where(group_id: group, type: available_services_types(include_project_specific: false)) }
scope :for_template, -> { where(template: true, type: available_services_types(include_project_specific: false)) } scope :for_template, -> { where(template: true, type: available_services_types(include_project_specific: false)) }
scope :for_instance, -> { where(instance: true, type: available_services_types(include_project_specific: false)) } scope :for_instance, -> { where(instance: true, type: available_services_types(include_project_specific: false)) }
...@@ -278,7 +279,7 @@ class Service < ApplicationRecord ...@@ -278,7 +279,7 @@ class Service < ApplicationRecord
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[], services.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
......
...@@ -28,9 +28,11 @@ module Groups ...@@ -28,9 +28,11 @@ module Groups
Group.transaction do Group.transaction do
update_group_attributes update_group_attributes
ensure_ownership ensure_ownership
update_integrations
end end
post_update_hooks(@updated_project_ids) post_update_hooks(@updated_project_ids)
propagate_integrations
true true
end end
...@@ -196,6 +198,17 @@ module Groups ...@@ -196,6 +198,17 @@ module Groups
raise TransferError, result[:message] unless result[:status] == :success raise TransferError, result[:message] unless result[:status] == :success
end end
end end
def update_integrations
@group.services.inherit.delete_all
Service.create_from_active_default_integrations(@group, :group_id)
end
def propagate_integrations
@group.services.inherit.each do |integration|
PropagateIntegrationWorker.perform_async(integration.id)
end
end
end end
end end
......
...@@ -59,7 +59,7 @@ module Projects ...@@ -59,7 +59,7 @@ module Projects
raise TransferError.new(s_("TransferProject|Root namespace can't be updated if project has NPM packages")) raise TransferError.new(s_("TransferProject|Root namespace can't be updated if project has NPM packages"))
end end
attempt_transfer_transaction proceed_to_transfer
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -67,7 +67,7 @@ module Projects ...@@ -67,7 +67,7 @@ module Projects
new_namespace.root_ancestor == project.namespace.root_ancestor new_namespace.root_ancestor == project.namespace.root_ancestor
end end
def attempt_transfer_transaction def proceed_to_transfer
Project.transaction do Project.transaction do
project.expire_caches_before_rename(@old_path) project.expire_caches_before_rename(@old_path)
...@@ -87,6 +87,8 @@ module Projects ...@@ -87,6 +87,8 @@ module Projects
# Move uploads # Move uploads
move_project_uploads(project) move_project_uploads(project)
update_integrations
project.old_path_with_namespace = @old_path project.old_path_with_namespace = @old_path
update_repository_configuration(@new_path) update_repository_configuration(@new_path)
...@@ -214,6 +216,11 @@ module Projects ...@@ -214,6 +216,11 @@ module Projects
project.shared_runners_enabled = false project.shared_runners_enabled = false
end end
end end
def update_integrations
project.services.inherit.delete_all
Service.create_from_active_default_integrations(project, :project_id)
end
end end
end end
......
---
title: Transfer a project/group to a new namespace inheriting integrations
merge_request: 48621
author:
type: changed
#import "~/boards/queries/board_list_shared.fragment.graphql" #import "~/boards/graphql/board_list_shared.fragment.graphql"
fragment BoardListFragment on BoardList { fragment BoardListFragment on BoardList {
...BoardListShared ...BoardListShared
......
#import "ee_else_ce/boards/queries/board_list.fragment.graphql" #import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
query ListIssues( query ListIssues(
$fullPath: ID! $fullPath: ID!
......
#import "ee_else_ce/boards/queries/board_list.fragment.graphql" #import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
#import "./board_epic.fragment.graphql" #import "./board_epic.fragment.graphql"
query BoardEE( query BoardEE(
......
#import "ee_else_ce/boards/queries/issue.fragment.graphql" #import "ee_else_ce/boards/graphql/issue.fragment.graphql"
mutation IssueMoveList( mutation IssueMoveList(
$projectPath: ID! $projectPath: ID!
......
#import "ee_else_ce/boards/queries/board_list.fragment.graphql" #import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
mutation boardListUpdateLimitMetrics($input: BoardListUpdateLimitMetricsInput!) { mutation boardListUpdateLimitMetrics($input: BoardListUpdateLimitMetricsInput!) {
boardListUpdateLimitMetrics(input: $input) { boardListUpdateLimitMetrics(input: $input) {
......
...@@ -22,13 +22,13 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils'; ...@@ -22,13 +22,13 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import eventHub from '~/boards/eventhub'; import eventHub from '~/boards/eventhub';
import createGqClient, { fetchPolicies } from '~/lib/graphql'; import createGqClient, { fetchPolicies } from '~/lib/graphql';
import epicsSwimlanesQuery from '../queries/epics_swimlanes.query.graphql'; import epicsSwimlanesQuery from '../graphql/epics_swimlanes.query.graphql';
import issueSetEpic from '../queries/issue_set_epic.mutation.graphql'; import issueSetEpicMutation from '../graphql/issue_set_epic.mutation.graphql';
import issueSetWeight from '../queries/issue_set_weight.mutation.graphql'; import issueSetWeightMutation from '../graphql/issue_set_weight.mutation.graphql';
import listsIssuesQuery from '~/boards/queries/lists_issues.query.graphql'; import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql';
import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql'; import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
import listUpdateLimitMetrics from '../queries/list_update_limit_metrics.mutation.graphql'; import listUpdateLimitMetricsMutation from '../graphql/list_update_limit_metrics.mutation.graphql';
import updateBoardEpicUserPreferencesMutation from '../queries/updateBoardEpicUserPreferences.mutation.graphql'; import updateBoardEpicUserPreferencesMutation from '../graphql/updateBoardEpicUserPreferences.mutation.graphql';
const notImplemented = () => { const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */ /* eslint-disable-next-line @gitlab/require-i18n-strings */
...@@ -198,7 +198,7 @@ export default { ...@@ -198,7 +198,7 @@ export default {
if (getters.shouldUseGraphQL) { if (getters.shouldUseGraphQL) {
return gqlClient return gqlClient
.mutate({ .mutate({
mutation: listUpdateLimitMetrics, mutation: listUpdateLimitMetricsMutation,
variables: { variables: {
input: { input: {
listId, listId,
...@@ -339,7 +339,7 @@ export default { ...@@ -339,7 +339,7 @@ export default {
setActiveIssueEpic: async ({ getters }, input) => { setActiveIssueEpic: async ({ getters }, input) => {
const { data } = await gqlClient.mutate({ const { data } = await gqlClient.mutate({
mutation: issueSetEpic, mutation: issueSetEpicMutation,
variables: { variables: {
input: { input: {
iid: String(getters.activeIssue.iid), iid: String(getters.activeIssue.iid),
...@@ -358,7 +358,7 @@ export default { ...@@ -358,7 +358,7 @@ export default {
setActiveIssueWeight: async ({ commit, getters }, input) => { setActiveIssueWeight: async ({ commit, getters }, input) => {
const { data } = await gqlClient.mutate({ const { data } = await gqlClient.mutate({
mutation: issueSetWeight, mutation: issueSetWeightMutation,
variables: { variables: {
input: { input: {
iid: String(getters.activeIssue.iid), iid: String(getters.activeIssue.iid),
......
...@@ -5,7 +5,10 @@ ...@@ -5,7 +5,10 @@
# Utility module for A/B testing experimental features. Define your experiments in the `EXPERIMENTS` constant. # Utility module for A/B testing experimental features. Define your experiments in the `EXPERIMENTS` constant.
# Experiment options: # Experiment options:
# - tracking_category (optional, used to set the category when tracking an experiment event) # - tracking_category (optional, used to set the category when tracking an experiment event)
# - use_backwards_compatible_subject_index (optional, set this to true if you need backwards compatibility) # - use_backwards_compatible_subject_index (optional, set this to true if you need backwards compatibility -- you likely do not need this, see note in the next paragraph.)
#
# Using the backwards-compatible subject index (use_backwards_compatible_subject_index option):
# This option was added when [the calculation of experimentation_subject_index was changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45733/diffs#41af4a6fa5a10c7068559ce21c5188483751d934_157_173). It is not intended to be used by new experiments, it exists merely for the segmentation integrity of in-flight experiments at the time the change was deployed. That is, we want users who were assigned to the "experimental" group or the "control" group before the change to still be in those same groups after the change. See [the original issue](https://gitlab.com/gitlab-org/gitlab/-/issues/270858) and [this related comment](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48110#note_458223745) for more information.
# #
# The experiment is controlled by a Feature Flag (https://docs.gitlab.com/ee/development/feature_flags/controls.html), # The experiment is controlled by a Feature Flag (https://docs.gitlab.com/ee/development/feature_flags/controls.html),
# which is named "#{experiment_key}_experiment_percentage" and *must* be set with a percentage and not be used for other purposes. # which is named "#{experiment_key}_experiment_percentage" and *must* be set with a percentage and not be used for other purposes.
......
...@@ -14,7 +14,7 @@ import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dro ...@@ -14,7 +14,7 @@ import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dro
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue'; import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import store from '~/boards/stores'; import store from '~/boards/stores';
import getIssueParticipants from '~/vue_shared/components/sidebar/queries/getIssueParticipants.query.graphql'; import getIssueParticipants from '~/vue_shared/components/sidebar/queries/getIssueParticipants.query.graphql';
import searchUsers from '~/boards/queries/users_search.query.graphql'; import searchUsers from '~/boards/graphql/users_search.query.graphql';
import { participants } from '../mock_data'; import { participants } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
......
...@@ -14,8 +14,8 @@ import { ...@@ -14,8 +14,8 @@ import {
import actions, { gqlClient } from '~/boards/stores/actions'; import actions, { gqlClient } from '~/boards/stores/actions';
import * as types from '~/boards/stores/mutation_types'; import * as types from '~/boards/stores/mutation_types';
import { inactiveId } from '~/boards/constants'; import { inactiveId } from '~/boards/constants';
import issueMoveListMutation from '~/boards/queries/issue_move_list.mutation.graphql'; import issueMoveListMutation from '~/boards/graphql/issue_move_list.mutation.graphql';
import destroyBoardListMutation from '~/boards/queries/board_list_destroy.mutation.graphql'; import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql'; import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
import { fullBoardId, formatListIssues, formatBoardLists } from '~/boards/boards_util'; import { fullBoardId, formatListIssues, formatBoardLists } from '~/boards/boards_util';
......
...@@ -3,7 +3,7 @@ import { ...@@ -3,7 +3,7 @@ import {
mockLabels, mockLabels,
mockRegularLabel, mockRegularLabel,
} from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data'; } from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
import updateIssueLabelsMutation from '~/boards/queries/issue_set_labels.mutation.graphql'; import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
import { MutationOperationMode } from '~/graphql_shared/utils'; import { MutationOperationMode } from '~/graphql_shared/utils';
import { IssuableType } from '~/issue_show/constants'; import { IssuableType } from '~/issue_show/constants';
import SidebarLabels from '~/sidebar/components/labels/sidebar_labels.vue'; import SidebarLabels from '~/sidebar/components/labels/sidebar_labels.vue';
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Groups::TransferService do RSpec.describe Groups::TransferService do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:new_parent_group) { create(:group, :public) } let_it_be(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) } let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let(:transfer_service) { described_class.new(group, user) } let(:transfer_service) { described_class.new(group, user) }
context 'handling packages' do context 'handling packages' do
let_it_be(:group) { create(:group, :public) } let_it_be(:group) { create(:group, :public) }
let_it_be(:new_group) { create(:group, :public) }
let(:project) { create(:project, :public, namespace: group) } let(:project) { create(:project, :public, namespace: group) }
let(:new_group) { create(:group, :public) }
before do before do
group.add_owner(user) group.add_owner(user)
...@@ -35,8 +35,8 @@ RSpec.describe Groups::TransferService do ...@@ -35,8 +35,8 @@ RSpec.describe Groups::TransferService do
it_behaves_like 'transfer not allowed' it_behaves_like 'transfer not allowed'
context 'with a project within subgroup' do context 'with a project within subgroup' do
let(:root_group) { create(:group) } let_it_be(:root_group) { create(:group) }
let(:group) { create(:group, parent: root_group) } let_it_be(:group) { create(:group, parent: root_group) }
before do before do
root_group.add_owner(user) root_group.add_owner(user)
...@@ -79,8 +79,6 @@ RSpec.describe Groups::TransferService do ...@@ -79,8 +79,6 @@ RSpec.describe Groups::TransferService do
shared_examples 'ensuring allowed transfer for a group' do shared_examples 'ensuring allowed transfer for a group' do
context "when there's an exception on GitLab shell directories" do context "when there's an exception on GitLab shell directories" do
let(:new_parent_group) { create(:group, :public) }
before do before do
allow_next_instance_of(described_class) do |instance| allow_next_instance_of(described_class) do |instance|
allow(instance).to receive(:update_group_attributes).and_raise(Gitlab::UpdatePathError, 'namespace directory cannot be moved') allow(instance).to receive(:update_group_attributes).and_raise(Gitlab::UpdatePathError, 'namespace directory cannot be moved')
...@@ -101,7 +99,7 @@ RSpec.describe Groups::TransferService do ...@@ -101,7 +99,7 @@ RSpec.describe Groups::TransferService do
describe '#execute' do describe '#execute' do
context 'when transforming a group into a root group' do context 'when transforming a group into a root group' do
let!(:group) { create(:group, :public, :nested) } let_it_be_with_reload(:group) { create(:group, :public, :nested) }
it_behaves_like 'ensuring allowed transfer for a group' it_behaves_like 'ensuring allowed transfer for a group'
...@@ -115,7 +113,7 @@ RSpec.describe Groups::TransferService do ...@@ -115,7 +113,7 @@ RSpec.describe Groups::TransferService do
end end
context 'when the user does not have the right policies' do context 'when the user does not have the right policies' do
let!(:group_member) { create(:group_member, :guest, group: group, user: user) } let_it_be(:group_member) { create(:group_member, :guest, group: group, user: user) }
it "returns false" do it "returns false" do
expect(transfer_service.execute(nil)).to be_falsy expect(transfer_service.execute(nil)).to be_falsy
...@@ -128,7 +126,7 @@ RSpec.describe Groups::TransferService do ...@@ -128,7 +126,7 @@ RSpec.describe Groups::TransferService do
end end
context 'when there is a group with the same path' do context 'when there is a group with the same path' do
let!(:group) { create(:group, :public, :nested, path: 'not-unique') } let_it_be(:group) { create(:group, :public, :nested, path: 'not-unique') }
before do before do
create(:group, path: 'not-unique') create(:group, path: 'not-unique')
...@@ -145,9 +143,9 @@ RSpec.describe Groups::TransferService do ...@@ -145,9 +143,9 @@ RSpec.describe Groups::TransferService do
end end
context 'when the group is a subgroup and the transfer is valid' do context 'when the group is a subgroup and the transfer is valid' do
let!(:subgroup1) { create(:group, :private, parent: group) } let_it_be(:subgroup1) { create(:group, :private, parent: group) }
let!(:subgroup2) { create(:group, :internal, parent: group) } let_it_be(:subgroup2) { create(:group, :internal, parent: group) }
let!(:project1) { create(:project, :repository, :private, namespace: group) } let_it_be(:project1) { create(:project, :repository, :private, namespace: group) }
before do before do
transfer_service.execute(nil) transfer_service.execute(nil)
...@@ -173,12 +171,12 @@ RSpec.describe Groups::TransferService do ...@@ -173,12 +171,12 @@ RSpec.describe Groups::TransferService do
end end
context 'when transferring a subgroup into another group' do context 'when transferring a subgroup into another group' do
let(:group) { create(:group, :public, :nested) } let_it_be_with_reload(:group) { create(:group, :public, :nested) }
it_behaves_like 'ensuring allowed transfer for a group' it_behaves_like 'ensuring allowed transfer for a group'
context 'when the new parent group is the same as the previous parent group' do context 'when the new parent group is the same as the previous parent group' do
let(:group) { create(:group, :public, :nested, parent: new_parent_group) } let_it_be(:group) { create(:group, :public, :nested, parent: new_parent_group) }
it 'returns false' do it 'returns false' do
expect(transfer_service.execute(new_parent_group)).to be_falsy expect(transfer_service.execute(new_parent_group)).to be_falsy
...@@ -191,7 +189,7 @@ RSpec.describe Groups::TransferService do ...@@ -191,7 +189,7 @@ RSpec.describe Groups::TransferService do
end end
context 'when the user does not have the right policies' do context 'when the user does not have the right policies' do
let!(:group_member) { create(:group_member, :guest, group: group, user: user) } let_it_be(:group_member) { create(:group_member, :guest, group: group, user: user) }
it "returns false" do it "returns false" do
expect(transfer_service.execute(new_parent_group)).to be_falsy expect(transfer_service.execute(new_parent_group)).to be_falsy
...@@ -221,7 +219,7 @@ RSpec.describe Groups::TransferService do ...@@ -221,7 +219,7 @@ RSpec.describe Groups::TransferService do
end end
context 'when the parent group has a project with the same path' do context 'when the parent group has a project with the same path' do
let!(:group) { create(:group, :public, :nested, path: 'foo') } let_it_be_with_reload(:group) { create(:group, :public, :nested, path: 'foo') }
before do before do
create(:group_member, :owner, group: new_parent_group, user: user) create(:group_member, :owner, group: new_parent_group, user: user)
...@@ -240,8 +238,13 @@ RSpec.describe Groups::TransferService do ...@@ -240,8 +238,13 @@ RSpec.describe Groups::TransferService do
end end
context 'when the group is allowed to be transferred' do context 'when the group is allowed to be transferred' do
let_it_be(:new_parent_group_integration) { create(:slack_service, group: new_parent_group, project: nil, webhook: 'http://new-group.slack.com') }
before do before do
allow(PropagateIntegrationWorker).to receive(:perform_async)
create(:group_member, :owner, group: new_parent_group, user: user) create(:group_member, :owner, group: new_parent_group, user: user)
transfer_service.execute(new_parent_group) transfer_service.execute(new_parent_group)
end end
...@@ -267,6 +270,30 @@ RSpec.describe Groups::TransferService do ...@@ -267,6 +270,30 @@ RSpec.describe Groups::TransferService do
end end
end end
context 'with a group integration' do
let_it_be(:instance_integration) { create(:slack_service, :instance, webhook: 'http://project.slack.com') }
let(:new_created_integration) { Service.find_by(group: group) }
context 'with an inherited integration' do
let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com', inherit_from_id: instance_integration.id) }
it 'replaces inherited integrations', :aggregate_failures do
expect(new_created_integration.webhook).to eq(new_parent_group_integration.webhook)
expect(PropagateIntegrationWorker).to have_received(:perform_async).with(new_created_integration.id)
expect(Service.count).to eq(3)
end
end
context 'with a custom integration' do
let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com') }
it 'does not updates the integrations', :aggregate_failures do
expect { transfer_service.execute(new_parent_group) }.not_to change { group_integration.webhook }
expect(PropagateIntegrationWorker).not_to have_received(:perform_async)
end
end
end
it 'updates visibility for the group based on the parent group' do it 'updates visibility for the group based on the parent group' do
expect(group.visibility_level).to eq(new_parent_group.visibility_level) expect(group.visibility_level).to eq(new_parent_group.visibility_level)
end end
...@@ -464,7 +491,7 @@ RSpec.describe Groups::TransferService do ...@@ -464,7 +491,7 @@ RSpec.describe Groups::TransferService do
end end
context 'updated paths' do context 'updated paths' do
let(:group) { create(:group, :public) } let_it_be_with_reload(:group) { create(:group, :public) }
before do before do
transfer_service.execute(new_parent_group) transfer_service.execute(new_parent_group)
...@@ -500,10 +527,10 @@ RSpec.describe Groups::TransferService do ...@@ -500,10 +527,10 @@ RSpec.describe Groups::TransferService do
end end
context 'resets project authorizations' do context 'resets project authorizations' do
let(:old_parent_group) { create(:group) } let_it_be(:old_parent_group) { create(:group) }
let(:group) { create(:group, :private, parent: old_parent_group) } let_it_be_with_reload(:group) { create(:group, :private, parent: old_parent_group) }
let(:new_group_member) { create(:user) } let_it_be(:new_group_member) { create(:user) }
let(:old_group_member) { create(:user) } let_it_be(:old_group_member) { create(:user) }
before do before do
new_parent_group.add_maintainer(new_group_member) new_parent_group.add_maintainer(new_group_member)
......
...@@ -7,6 +7,7 @@ RSpec.describe Projects::TransferService do ...@@ -7,6 +7,7 @@ RSpec.describe Projects::TransferService do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com') }
let(:project) { create(:project, :repository, :legacy_storage, namespace: user.namespace) } let(:project) { create(:project, :repository, :legacy_storage, namespace: user.namespace) }
subject(:execute_transfer) { described_class.new(project, user).execute(group).tap { project.reload } } subject(:execute_transfer) { described_class.new(project, user).execute(group).tap { project.reload } }
...@@ -117,6 +118,30 @@ RSpec.describe Projects::TransferService do ...@@ -117,6 +118,30 @@ RSpec.describe Projects::TransferService do
shard_name: project.repository_storage shard_name: project.repository_storage
) )
end end
context 'with a project integration' do
let_it_be_with_reload(:project) { create(:project, namespace: user.namespace) }
let_it_be(:instance_integration) { create(:slack_service, :instance, webhook: 'http://project.slack.com') }
context 'with an inherited integration' do
let_it_be(:project_integration) { create(:slack_service, project: project, webhook: 'http://project.slack.com', inherit_from_id: instance_integration.id) }
it 'replaces inherited integrations', :aggregate_failures do
execute_transfer
expect(project.slack_service.webhook).to eq(group_integration.webhook)
expect(Service.count).to eq(3)
end
end
context 'with a custom integration' do
let_it_be(:project_integration) { create(:slack_service, project: project, webhook: 'http://project.slack.com') }
it 'does not updates the integrations' do
expect { execute_transfer }.not_to change { project.slack_service.webhook }
end
end
end
end end
context 'when transfer fails' do context 'when transfer fails' do
...@@ -527,7 +552,7 @@ RSpec.describe Projects::TransferService do ...@@ -527,7 +552,7 @@ RSpec.describe Projects::TransferService do
group.add_owner(user) group.add_owner(user)
end end
it 'schedules a job when pages are deployed' do it 'schedules a job when pages are deployed' do
project.mark_pages_as_deployed project.mark_pages_as_deployed
expect(PagesTransferWorker).to receive(:perform_async) expect(PagesTransferWorker).to receive(:perform_async)
......
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