summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-04 09:09:36 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-04 09:09:36 +0000
commitf9fe7cda4bdbc477d8d76e5def4023f7d03bab46 (patch)
tree293285f6f438a22e7beb35ef8cf5e1ffae131094
parenta2f16969fa9bb982d5ec01f18efff5eabfc89a67 (diff)
downloadgitlab-ce-f9fe7cda4bdbc477d8d76e5def4023f7d03bab46.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/boards/components/board_assignee_dropdown.vue2
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue4
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue2
-rw-r--r--app/assets/javascripts/boards/graphql/board.fragment.graphql (renamed from app/assets/javascripts/boards/queries/board.fragment.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board.mutation.graphql (renamed from app/assets/javascripts/boards/queries/board.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_labels.query.graphql (renamed from app/assets/javascripts/boards/queries/board_labels.query.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_list.fragment.graphql (renamed from app/assets/javascripts/boards/queries/board_list.fragment.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql (renamed from app/assets/javascripts/boards/queries/board_list_create.mutation.graphql)2
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_destroy.mutation.graphql (renamed from app/assets/javascripts/boards/queries/board_list_destroy.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql (renamed from app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_list_update.mutation.graphql (renamed from app/assets/javascripts/boards/queries/board_list_update.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/board_lists.query.graphql (renamed from app/assets/javascripts/boards/queries/board_lists.query.graphql)2
-rw-r--r--app/assets/javascripts/boards/graphql/group_boards.query.graphql (renamed from app/assets/javascripts/boards/queries/group_boards.query.graphql)2
-rw-r--r--app/assets/javascripts/boards/graphql/group_milestones.query.graphql (renamed from app/assets/javascripts/boards/queries/group_milestones.query.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/issue.fragment.graphql (renamed from app/assets/javascripts/boards/queries/issue.fragment.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/issue_create.mutation.graphql (renamed from app/assets/javascripts/boards/queries/issue_create.mutation.graphql)2
-rw-r--r--app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql (renamed from app/assets/javascripts/boards/queries/issue_move_list.mutation.graphql)2
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_due_date.mutation.graphql (renamed from app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_labels.mutation.graphql (renamed from app/assets/javascripts/boards/queries/issue_set_labels.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql (renamed from app/assets/javascripts/boards/queries/issue_set_milestone.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_subscription.mutation.graphql (renamed from app/assets/javascripts/boards/graphql/mutations/issue_set_subscription.mutation.graphql)0
-rw-r--r--app/assets/javascripts/boards/graphql/lists_issues.query.graphql (renamed from app/assets/javascripts/boards/queries/lists_issues.query.graphql)2
-rw-r--r--app/assets/javascripts/boards/graphql/project_boards.query.graphql (renamed from app/assets/javascripts/boards/queries/project_boards.query.graphql)2
-rw-r--r--app/assets/javascripts/boards/graphql/users_search.query.graphql (renamed from app/assets/javascripts/boards/queries/users_search.query.graphql)0
-rw-r--r--app/assets/javascripts/boards/stores/actions.js34
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js2
-rw-r--r--app/assets/javascripts/boards/stores/getters.js1
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue2
-rw-r--r--app/assets/javascripts/diffs/store/actions.js2
-rw-r--r--app/assets/javascripts/diffs/store/utils.js2
-rw-r--r--app/assets/javascripts/diffs/utils/diff_file.js (renamed from app/assets/javascripts/diffs/diff_file.js)2
-rw-r--r--app/assets/javascripts/incidents_settings/components/alerts_form.vue4
-rw-r--r--app/assets/javascripts/incidents_settings/components/pagerduty_form.vue31
-rw-r--r--app/assets/javascripts/incidents_settings/constants.js16
-rw-r--r--app/assets/javascripts/integrations/edit/components/integration_form.vue5
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue2
-rw-r--r--app/assets/javascripts/whats_new/components/app.vue130
-rw-r--r--app/assets/javascripts/whats_new/components/feature.vue64
-rw-r--r--app/assets/javascripts/whats_new/index.js8
-rw-r--r--app/assets/javascripts/whats_new/store/actions.js3
-rw-r--r--app/assets/stylesheets/components/whats_new.scss26
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss112
-rw-r--r--app/controllers/whats_new_controller.rb27
-rw-r--r--app/helpers/whats_new_helper.rb6
-rw-r--r--app/models/release_highlight.rb38
-rw-r--r--app/models/service.rb3
-rw-r--r--app/services/groups/transfer_service.rb13
-rw-r--r--app/services/projects/transfer_service.rb11
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--changelogs/unreleased/255923-transfer-project-group-with-integrations.yml5
-rw-r--r--changelogs/unreleased/273270-save-button-should-have-a-different-color-on-press.yml5
-rw-r--r--changelogs/unreleased/tr-update-issue-to-incident.yml5
-rw-r--r--doc/ci/quick_start/img/runners_activated.pngbin104545 -> 0 bytes
-rw-r--r--lib/gitlab/experimentation.rb5
-rw-r--r--locale/gitlab.pot33
-rw-r--r--spec/features/projects/settings/operations_settings_spec.rb2
-rw-r--r--spec/frontend/boards/components/board_assignee_dropdown_spec.js2
-rw-r--r--spec/frontend/boards/stores/actions_spec.js4
-rw-r--r--spec/frontend/boards/stores/getters_spec.js18
-rw-r--r--spec/frontend/diffs/utils/diff_file_spec.js (renamed from spec/frontend/diffs/diff_file_spec.js)2
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap6
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap14
-rw-r--r--spec/frontend/sidebar/sidebar_labels_spec.js2
-rw-r--r--spec/frontend/whats_new/components/app_spec.js201
-rw-r--r--spec/frontend/whats_new/store/actions_spec.js17
-rw-r--r--spec/helpers/whats_new_helper_spec.rb14
-rw-r--r--spec/models/release_highlight_spec.rb96
-rw-r--r--spec/requests/whats_new_controller_spec.rb22
-rw-r--r--spec/services/groups/transfer_service_spec.rb71
-rw-r--r--spec/services/projects/transfer_service_spec.rb27
73 files changed, 785 insertions, 340 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 43c5a59ef08..f14b86c836c 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-add5f3dd182c99b4d9e1cf93e45fec1214c00659
+9fd57cbd0b63d448f9a9555b53f065ee1c110199
diff --git a/app/assets/javascripts/boards/components/board_assignee_dropdown.vue b/app/assets/javascripts/boards/components/board_assignee_dropdown.vue
index b4a9d34e738..76b468a3402 100644
--- a/app/assets/javascripts/boards/components/board_assignee_dropdown.vue
+++ b/app/assets/javascripts/boards/components/board_assignee_dropdown.vue
@@ -14,7 +14,7 @@ import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dropdown.vue';
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 {
noSearchDelay: 0,
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index 2daec46e64b..435ea3f8b7a 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -12,8 +12,8 @@ import {
import httpStatusCodes from '~/lib/utils/http_status';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import projectQuery from '../queries/project_boards.query.graphql';
-import groupQuery from '../queries/group_boards.query.graphql';
+import projectQuery from '../graphql/project_boards.query.graphql';
+import groupQuery from '../graphql/group_boards.query.graphql';
import boardsStore from '../stores/boards_store';
import BoardForm from './board_form.vue';
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
index f7b7fd3f61f..78c3f8acc62 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_milestone_select.vue
@@ -10,7 +10,7 @@ import {
} from '@gitlab/ui';
import { fetchPolicies } from '~/lib/graphql';
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 { __, s__ } from '~/locale';
diff --git a/app/assets/javascripts/boards/queries/board.fragment.graphql b/app/assets/javascripts/boards/graphql/board.fragment.graphql
index 872a4c4afbc..872a4c4afbc 100644
--- a/app/assets/javascripts/boards/queries/board.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/board.fragment.graphql
diff --git a/app/assets/javascripts/boards/queries/board.mutation.graphql b/app/assets/javascripts/boards/graphql/board.mutation.graphql
index ef2b81a7939..ef2b81a7939 100644
--- a/app/assets/javascripts/boards/queries/board.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/board.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/board_labels.query.graphql b/app/assets/javascripts/boards/graphql/board_labels.query.graphql
index 42a94419a97..42a94419a97 100644
--- a/app/assets/javascripts/boards/queries/board_labels.query.graphql
+++ b/app/assets/javascripts/boards/graphql/board_labels.query.graphql
diff --git a/app/assets/javascripts/boards/queries/board_list.fragment.graphql b/app/assets/javascripts/boards/graphql/board_list.fragment.graphql
index bbf3314377e..bbf3314377e 100644
--- a/app/assets/javascripts/boards/queries/board_list.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list.fragment.graphql
diff --git a/app/assets/javascripts/boards/queries/board_list_create.mutation.graphql b/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql
index 48420b349ae..f78a21baa7f 100644
--- a/app/assets/javascripts/boards/queries/board_list_create.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_create.mutation.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/boards/queries/board_list.fragment.graphql"
+#import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
mutation CreateBoardList(
$boardId: BoardID!
diff --git a/app/assets/javascripts/boards/queries/board_list_destroy.mutation.graphql b/app/assets/javascripts/boards/graphql/board_list_destroy.mutation.graphql
index ef3fd36e980..ef3fd36e980 100644
--- a/app/assets/javascripts/boards/queries/board_list_destroy.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_destroy.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql b/app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql
index d85b736720b..d85b736720b 100644
--- a/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_shared.fragment.graphql
diff --git a/app/assets/javascripts/boards/queries/board_list_update.mutation.graphql b/app/assets/javascripts/boards/graphql/board_list_update.mutation.graphql
index b474c9acb93..b474c9acb93 100644
--- a/app/assets/javascripts/boards/queries/board_list_update.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/board_list_update.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/board_lists.query.graphql b/app/assets/javascripts/boards/graphql/board_lists.query.graphql
index 88425e9a9c1..eb922f162f8 100644
--- a/app/assets/javascripts/boards/queries/board_lists.query.graphql
+++ b/app/assets/javascripts/boards/graphql/board_lists.query.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/boards/queries/board_list.fragment.graphql"
+#import "ee_else_ce/boards/graphql/board_list.fragment.graphql"
query ListIssues(
$fullPath: ID!
diff --git a/app/assets/javascripts/boards/queries/group_boards.query.graphql b/app/assets/javascripts/boards/graphql/group_boards.query.graphql
index 74c224add7d..feafd6ae10d 100644
--- a/app/assets/javascripts/boards/queries/group_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/group_boards.query.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/boards/queries/board.fragment.graphql"
+#import "ee_else_ce/boards/graphql/board.fragment.graphql"
query group_boards($fullPath: ID!) {
group(fullPath: $fullPath) {
diff --git a/app/assets/javascripts/boards/queries/group_milestones.query.graphql b/app/assets/javascripts/boards/graphql/group_milestones.query.graphql
index f2ab12ef4a7..f2ab12ef4a7 100644
--- a/app/assets/javascripts/boards/queries/group_milestones.query.graphql
+++ b/app/assets/javascripts/boards/graphql/group_milestones.query.graphql
diff --git a/app/assets/javascripts/boards/queries/issue.fragment.graphql b/app/assets/javascripts/boards/graphql/issue.fragment.graphql
index 1395bef39ed..1395bef39ed 100644
--- a/app/assets/javascripts/boards/queries/issue.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/issue.fragment.graphql
diff --git a/app/assets/javascripts/boards/queries/issue_create.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql
index 65be147be07..c1a2361a4e8 100644
--- a/app/assets/javascripts/boards/queries/issue_create.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_create.mutation.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/boards/queries/issue.fragment.graphql"
+#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
mutation CreateIssue($input: CreateIssueInput!) {
createIssue(input: $input) {
diff --git a/app/assets/javascripts/boards/queries/issue_move_list.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
index ff6aa597f48..3c574fd8c87 100644
--- a/app/assets/javascripts/boards/queries/issue_move_list.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/boards/queries/issue.fragment.graphql"
+#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
mutation IssueMoveList(
$projectPath: ID!
diff --git a/app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_due_date.mutation.graphql
index bbea248cf85..bbea248cf85 100644
--- a/app/assets/javascripts/boards/queries/issue_set_due_date.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_set_due_date.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/issue_set_labels.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_labels.mutation.graphql
index 3c5f4b3e3bd..3c5f4b3e3bd 100644
--- a/app/assets/javascripts/boards/queries/issue_set_labels.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_set_labels.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/issue_set_milestone.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql
index 5dc78a03a06..5dc78a03a06 100644
--- a/app/assets/javascripts/boards/queries/issue_set_milestone.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_set_milestone.mutation.graphql
diff --git a/app/assets/javascripts/boards/graphql/mutations/issue_set_subscription.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_subscription.mutation.graphql
index 1f383245ac2..1f383245ac2 100644
--- a/app/assets/javascripts/boards/graphql/mutations/issue_set_subscription.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_set_subscription.mutation.graphql
diff --git a/app/assets/javascripts/boards/queries/lists_issues.query.graphql b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
index 5dbfe4675c6..43af7d2b2f1 100644
--- a/app/assets/javascripts/boards/queries/lists_issues.query.graphql
+++ b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/boards/queries/issue.fragment.graphql"
+#import "ee_else_ce/boards/graphql/issue.fragment.graphql"
query ListIssues(
$fullPath: ID!
diff --git a/app/assets/javascripts/boards/queries/project_boards.query.graphql b/app/assets/javascripts/boards/graphql/project_boards.query.graphql
index a1326bd5eff..f98d25ba671 100644
--- a/app/assets/javascripts/boards/queries/project_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/project_boards.query.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/boards/queries/board.fragment.graphql"
+#import "ee_else_ce/boards/graphql/board.fragment.graphql"
query project_boards($fullPath: ID!) {
project(fullPath: $fullPath) {
diff --git a/app/assets/javascripts/boards/queries/users_search.query.graphql b/app/assets/javascripts/boards/graphql/users_search.query.graphql
index ca016495d79..ca016495d79 100644
--- a/app/assets/javascripts/boards/queries/users_search.query.graphql
+++ b/app/assets/javascripts/boards/graphql/users_search.query.graphql
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index fd5049ef9bc..a7d8144f8af 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -1,6 +1,6 @@
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 { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { BoardType, ListType, inactiveId, DEFAULT_LABELS } from '~/boards/constants';
@@ -14,18 +14,18 @@ import {
} from '../boards_util';
import boardStore from '~/boards/stores/boards_store';
-import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
-import listsIssuesQuery from '../queries/lists_issues.query.graphql';
-import boardLabelsQuery from '../queries/board_labels.query.graphql';
-import createBoardListMutation from '../queries/board_list_create.mutation.graphql';
-import updateBoardListMutation from '../queries/board_list_update.mutation.graphql';
-import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql';
-import destroyBoardListMutation from '../queries/board_list_destroy.mutation.graphql';
-import issueCreateMutation from '../queries/issue_create.mutation.graphql';
-import issueSetLabels from '../queries/issue_set_labels.mutation.graphql';
-import issueSetDueDate from '../queries/issue_set_due_date.mutation.graphql';
-import issueSetSubscriptionMutation from '../graphql/mutations/issue_set_subscription.mutation.graphql';
-import issueSetMilestone from '../queries/issue_set_milestone.mutation.graphql';
+import updateAssigneesMutation from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
+import listsIssuesQuery from '../graphql/lists_issues.query.graphql';
+import boardLabelsQuery from '../graphql/board_labels.query.graphql';
+import createBoardListMutation from '../graphql/board_list_create.mutation.graphql';
+import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql';
+import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
+import destroyBoardListMutation from '../graphql/board_list_destroy.mutation.graphql';
+import issueCreateMutation from '../graphql/issue_create.mutation.graphql';
+import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql';
+import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql';
+import issueSetSubscriptionMutation from '../graphql/issue_set_subscription.mutation.graphql';
+import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@@ -324,7 +324,7 @@ export default {
return gqlClient
.mutate({
- mutation: updateAssignees,
+ mutation: updateAssigneesMutation,
variables: {
iid: getters.activeIssue.iid,
projectPath: getters.activeIssue.referencePath.split('#')[0],
@@ -350,7 +350,7 @@ export default {
setActiveIssueMilestone: async ({ commit, getters }, input) => {
const { activeIssue } = getters;
const { data } = await gqlClient.mutate({
- mutation: issueSetMilestone,
+ mutation: issueSetMilestoneMutation,
variables: {
input: {
iid: String(activeIssue.iid),
@@ -416,7 +416,7 @@ export default {
setActiveIssueLabels: async ({ commit, getters }, input) => {
const { activeIssue } = getters;
const { data } = await gqlClient.mutate({
- mutation: issueSetLabels,
+ mutation: issueSetLabelsMutation,
variables: {
input: {
iid: String(activeIssue.iid),
@@ -441,7 +441,7 @@ export default {
setActiveIssueDueDate: async ({ commit, getters }, input) => {
const { activeIssue } = getters;
const { data } = await gqlClient.mutate({
- mutation: issueSetDueDate,
+ mutation: issueSetDueDateMutation,
variables: {
input: {
iid: String(activeIssue.iid),
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 2a4bf7c7288..fa1ffdfa26a 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -22,7 +22,7 @@ import ListLabel from '../models/label';
import ListAssignee from '../models/assignee';
import ListMilestone from '../models/milestone';
-import createBoardMutation from '../queries/board.mutation.graphql';
+import createBoardMutation from '../graphql/board.mutation.graphql';
const PER_PAGE = 20;
export const gqlClient = createDefaultClient();
diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js
index d2517fd2cfc..ca6887b6f45 100644
--- a/app/assets/javascripts/boards/stores/getters.js
+++ b/app/assets/javascripts/boards/stores/getters.js
@@ -2,7 +2,6 @@ import { find } from 'lodash';
import { inactiveId } from '../constants';
export default {
- labelToggleState: state => (state.isShowingLabels ? 'on' : 'off'),
isSidebarOpen: state => state.activeId !== inactiveId,
isSwimlanesOn: () => false,
getIssueById: state => id => {
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 32191d7e309..ed94cabe124 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -10,7 +10,7 @@ import notesEventHub from '../../notes/event_hub';
import DiffFileHeader from './diff_file_header.vue';
import DiffContent from './diff_content.vue';
import { diffViewerErrors } from '~/ide/constants';
-import { collapsedType, isCollapsed } from '../diff_file';
+import { collapsedType, isCollapsed } from '../utils/diff_file';
import {
DIFF_FILE_AUTOMATIC_COLLAPSE,
DIFF_FILE_MANUAL_COLLAPSE,
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 6dc9c71e1cd..53d1383b82e 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -19,7 +19,7 @@ import { __, s__, sprintf } from '~/locale';
import { diffViewerModes } from '~/ide/constants';
import DiffStats from './diff_stats.vue';
import { scrollToElement } from '~/lib/utils/common_utils';
-import { isCollapsed } from '../diff_file';
+import { isCollapsed } from '../utils/diff_file';
import { DIFF_FILE_HEADER } from '../i18n';
export default {
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index c41828a4221..e7f950f15c1 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -49,7 +49,7 @@ import {
DIFF_FILE_BY_FILE_COOKIE_NAME,
} from '../constants';
import { diffViewerModes } from '~/ide/constants';
-import { isCollapsed } from '../diff_file';
+import { isCollapsed } from '../utils/diff_file';
export const setBaseConfig = ({ commit }, options) => {
const {
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 8f32952676a..8949c8cd23e 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -16,7 +16,7 @@ import {
SHOW_WHITESPACE,
NO_SHOW_WHITESPACE,
} from '../constants';
-import { prepareRawDiffFile } from '../diff_file';
+import { prepareRawDiffFile } from '../utils/diff_file';
export const isAdded = line => ['new', 'new-nonewline'].includes(line.type);
export const isRemoved = line => ['old', 'old-nonewline'].includes(line.type);
diff --git a/app/assets/javascripts/diffs/diff_file.js b/app/assets/javascripts/diffs/utils/diff_file.js
index a14a30b41a9..aa801783c57 100644
--- a/app/assets/javascripts/diffs/diff_file.js
+++ b/app/assets/javascripts/diffs/utils/diff_file.js
@@ -3,7 +3,7 @@ import {
DIFF_FILE_DELETED_MODE,
DIFF_FILE_MANUAL_COLLAPSE,
DIFF_FILE_AUTOMATIC_COLLAPSE,
-} from './constants';
+} from '../constants';
function fileSymlinkInformation(file, fileList) {
const duplicates = fileList.filter(iteratedFile => iteratedFile.file_hash === file.file_hash);
diff --git a/app/assets/javascripts/incidents_settings/components/alerts_form.vue b/app/assets/javascripts/incidents_settings/components/alerts_form.vue
index 5fe0badc56e..e8daad8811e 100644
--- a/app/assets/javascripts/incidents_settings/components/alerts_form.vue
+++ b/app/assets/javascripts/incidents_settings/components/alerts_form.vue
@@ -86,7 +86,7 @@ export default {
<form ref="settingsForm" @submit.prevent="updateAlertsIntegrationSettings">
<gl-form-group class="gl-pl-0">
<gl-form-checkbox v-model="createIssueEnabled" data-qa-selector="create_issue_checkbox">
- <span>{{ $options.i18n.createIssue.label }}</span>
+ <span>{{ $options.i18n.createIncident.label }}</span>
</gl-form-checkbox>
</gl-form-group>
@@ -96,7 +96,7 @@ export default {
class="col-8 col-md-9 gl-px-6"
>
<label class="gl-display-inline-flex" for="alert-integration-settings-issue-template">
- {{ $options.i18n.issueTemplate.label }}
+ {{ $options.i18n.incidentTemplate.label }}
<gl-link :href="$options.ISSUE_TEMPLATES_DOCS_LINK" target="_blank">
<gl-icon name="question" :size="12" />
</gl-link>
diff --git a/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue b/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue
index 9a8c4bc5af9..b56dd66342a 100644
--- a/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue
+++ b/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue
@@ -109,7 +109,20 @@ export default {
{{ webhookUpdateAlertMsg }}
</gl-alert>
- <p>{{ $options.i18n.introText }}</p>
+ <p>
+ <gl-sprintf :message="$options.i18n.introText">
+ <template #link="{ content }">
+ <gl-link
+ :href="$options.CONFIGURE_PAGERDUTY_WEBHOOK_DOCS_LINK"
+ target="_blank"
+ class="gl-display-inline-flex"
+ >
+ <span>{{ content }}</span>
+ <gl-icon name="external-link" />
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
<form ref="settingsForm" @submit.prevent="updatePagerDutyIntegrationSettings">
<gl-form-group class="col-8 col-md-9 gl-p-0">
<gl-toggle
@@ -134,23 +147,9 @@ export default {
</template>
</gl-form-input-group>
- <div class="gl-text-gray-200 gl-pt-2">
- <gl-sprintf :message="$options.i18n.webhookUrl.helpText">
- <template #docsLink>
- <gl-link
- :href="$options.CONFIGURE_PAGERDUTY_WEBHOOK_DOCS_LINK"
- target="_blank"
- class="gl-display-inline-flex"
- >
- <span>{{ $options.i18n.webhookUrl.helpDocsLink }}</span>
- <gl-icon name="external-link" />
- </gl-link>
- </template>
- </gl-sprintf>
- </div>
<gl-button
v-gl-modal.resetWebhookModal
- class="gl-mt-3"
+ class="gl-mt-5"
:disabled="loading"
:loading="resettingWebhook"
data-testid="webhook-reset-btn"
diff --git a/app/assets/javascripts/incidents_settings/constants.js b/app/assets/javascripts/incidents_settings/constants.js
index 42f1f645d16..fcac9c519c2 100644
--- a/app/assets/javascripts/incidents_settings/constants.js
+++ b/app/assets/javascripts/incidents_settings/constants.js
@@ -33,17 +33,17 @@ export const I18N_ALERT_SETTINGS_FORM = {
saveBtnLabel: __('Save changes'),
introText: __('Action to take when receiving an alert. %{docsLink}'),
introLinkText: __('More information.'),
- createIssue: {
- label: __('Create an issue. Issues are created for each alert triggered.'),
+ createIncident: {
+ label: __('Create an incident. Incidents are created for each alert triggered.'),
},
- issueTemplate: {
- label: __('Issue template (optional)'),
+ incidentTemplate: {
+ label: __('Incident template (optional)'),
},
sendEmail: {
label: __('Send a separate email notification to Developers.'),
},
autoCloseIncidents: {
- label: __('Automatically close incident issues when the associated Prometheus alert resolves.'),
+ label: __('Automatically close incidents when the associated Prometheus alert resolves.'),
},
};
@@ -57,17 +57,13 @@ export const ISSUE_TEMPLATES_DOCS_LINK =
export const I18N_PAGERDUTY_SETTINGS_FORM = {
introText: s__(
- 'PagerDutySettings|Setting up a webhook with PagerDuty will automatically create a GitLab issue for each PagerDuty incident.',
+ 'PagerDutySettings|Create a GitLab incident for each PagerDuty incident by %{linkStart}configuring a webhook in PagerDuty%{linkEnd}',
),
activeToggle: {
label: s__('PagerDutySettings|Active'),
},
webhookUrl: {
label: s__('PagerDutySettings|Webhook URL'),
- helpText: s__(
- 'PagerDutySettings|Create a GitLab issue for each PagerDuty incident by %{docsLink}',
- ),
- helpDocsLink: s__('PagerDutySettings|configuring a webhook in PagerDuty'),
resetWebhookUrl: s__('PagerDutySettings|Reset webhook URL'),
copyToClipboard: __('Copy'),
updateErrMsg: s__('PagerDutySettings|Failed to update Webhook URL'),
diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue
index ac8a64d5f3b..c6f8ba8dcb2 100644
--- a/app/assets/javascripts/integrations/edit/components/integration_form.vue
+++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue
@@ -59,6 +59,9 @@ export default {
showReset() {
return this.isInstanceOrGroupLevel && this.propsSource.resetPath;
},
+ saveButtonKey() {
+ return `save-button-${this.isDisabled}`;
+ },
},
methods: {
...mapActions([
@@ -117,6 +120,7 @@ export default {
<div v-if="isEditable" class="footer-block row-content-block">
<template v-if="isInstanceOrGroupLevel">
<gl-button
+ :key="saveButtonKey"
v-gl-modal.confirmSaveIntegration
category="primary"
variant="success"
@@ -130,6 +134,7 @@ export default {
</template>
<gl-button
v-else
+ :key="saveButtonKey"
category="primary"
variant="success"
type="submit"
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 98e3ae5179c..1580c94658a 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -7,7 +7,7 @@ import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import { getDiffMode } from '~/diffs/store/utils';
import { diffViewerModes } from '~/ide/constants';
-import { isCollapsed } from '../../diffs/diff_file';
+import { isCollapsed } from '../../diffs/utils/diff_file';
const FIRST_CHAR_REGEX = /^(\+|-| )/;
diff --git a/app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue b/app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue
index 1785174e8d7..07abfa8d103 100644
--- a/app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue
+++ b/app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue
@@ -1,7 +1,7 @@
<script>
import $ from 'jquery';
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 { IssuableType } from '~/issue_show/constants';
import { __ } from '~/locale';
diff --git a/app/assets/javascripts/whats_new/components/app.vue b/app/assets/javascripts/whats_new/components/app.vue
index 3c1de57252a..560cabd3bba 100644
--- a/app/assets/javascripts/whats_new/components/app.vue
+++ b/app/assets/javascripts/whats_new/components/app.vue
@@ -2,13 +2,15 @@
import { mapState, mapActions } from 'vuex';
import {
GlDrawer,
- GlBadge,
- GlIcon,
- GlLink,
GlInfiniteScroll,
GlResizeObserverDirective,
+ GlTabs,
+ GlTab,
+ GlBadge,
+ GlLoadingIcon,
} from '@gitlab/ui';
import SkeletonLoader from './skeleton_loader.vue';
+import Feature from './feature.vue';
import Tracking from '~/tracking';
import { getDrawerBodyHeight } from '../utils/get_drawer_body_height';
@@ -17,11 +19,13 @@ const trackingMixin = Tracking.mixin();
export default {
components: {
GlDrawer,
- GlBadge,
- GlIcon,
- GlLink,
GlInfiniteScroll,
+ GlTabs,
+ GlTab,
SkeletonLoader,
+ Feature,
+ GlBadge,
+ GlLoadingIcon,
},
directives: {
GlResizeObserver: GlResizeObserverDirective,
@@ -31,11 +35,19 @@ export default {
storageKey: {
type: String,
required: true,
- default: null,
+ },
+ versions: {
+ type: Array,
+ required: true,
+ },
+ gitlabDotCom: {
+ type: Boolean,
+ required: false,
+ default: false,
},
},
computed: {
- ...mapState(['open', 'features', 'pageInfo', 'drawerBodyHeight']),
+ ...mapState(['open', 'features', 'pageInfo', 'drawerBodyHeight', 'fetching']),
},
mounted() {
this.openDrawer(this.storageKey);
@@ -49,14 +61,25 @@ export default {
methods: {
...mapActions(['openDrawer', 'closeDrawer', 'fetchItems', 'setDrawerBodyHeight']),
bottomReached() {
- if (this.pageInfo.nextPage) {
- this.fetchItems(this.pageInfo.nextPage);
+ const page = this.pageInfo.nextPage;
+ if (page) {
+ this.fetchItems({ page });
}
},
handleResize() {
const height = getDrawerBodyHeight(this.$refs.drawer.$el);
this.setDrawerBodyHeight(height);
},
+ featuresForVersion(version) {
+ return this.features.filter(feature => {
+ return feature.release === parseFloat(version);
+ });
+ },
+ fetchVersion(version) {
+ if (this.featuresForVersion(version).length === 0) {
+ this.fetchItems({ version });
+ }
+ },
},
};
</script>
@@ -73,64 +96,39 @@ export default {
<template #header>
<h4 class="page-title gl-my-2">{{ __("What's new at GitLab") }}</h4>
</template>
- <gl-infinite-scroll
- v-if="features.length"
- :fetched-items="features.length"
- :max-list-height="drawerBodyHeight"
- class="gl-p-0"
- @bottomReached="bottomReached"
- >
- <template #items>
- <div
- v-for="feature in features"
- :key="feature.title"
- class="gl-pb-7 gl-pt-5 gl-px-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
+ <template v-if="features.length">
+ <gl-infinite-scroll
+ v-if="gitlabDotCom"
+ :fetched-items="features.length"
+ :max-list-height="drawerBodyHeight"
+ class="gl-p-0"
+ @bottomReached="bottomReached"
+ >
+ <template #items>
+ <feature v-for="feature in features" :key="feature.title" :feature="feature" />
+ </template>
+ </gl-infinite-scroll>
+ <gl-tabs v-else :style="{ height: `${drawerBodyHeight}px` }" class="gl-p-0">
+ <gl-tab
+ v-for="(version, index) in versions"
+ :key="version"
+ @click="fetchVersion(version)"
>
- <gl-link
- :href="feature.url"
- target="_blank"
- class="whats-new-item-title-link"
- data-track-event="click_whats_new_item"
- :data-track-label="feature.title"
- :data-track-property="feature.url"
- >
- <h5 class="gl-font-lg">{{ feature.title }}</h5>
- </gl-link>
- <div v-if="feature.packages" class="gl-mb-3">
- <gl-badge
- v-for="package_name in feature.packages"
- :key="package_name"
- size="sm"
- class="whats-new-item-badge gl-mr-2"
- >
- <gl-icon name="license" />{{ package_name }}
- </gl-badge>
- </div>
- <gl-link
- :href="feature.url"
- target="_blank"
- data-track-event="click_whats_new_item"
- :data-track-label="feature.title"
- :data-track-property="feature.url"
- >
- <img
- :alt="feature.title"
- :src="feature.image_url"
- class="img-thumbnail gl-px-8 gl-py-3 whats-new-item-image"
+ <template #title>
+ <span>{{ version }}</span>
+ <gl-badge v-if="index === 0">{{ __('Your Version') }}</gl-badge>
+ </template>
+ <gl-loading-icon v-if="fetching" size="lg" class="text-center" />
+ <template v-else>
+ <feature
+ v-for="feature in featuresForVersion(version)"
+ :key="feature.title"
+ :feature="feature"
/>
- </gl-link>
- <p class="gl-pt-3">{{ feature.body }}</p>
- <gl-link
- :href="feature.url"
- target="_blank"
- data-track-event="click_whats_new_item"
- :data-track-label="feature.title"
- :data-track-property="feature.url"
- >{{ __('Learn more') }}</gl-link
- >
- </div>
- </template>
- </gl-infinite-scroll>
+ </template>
+ </gl-tab>
+ </gl-tabs>
+ </template>
<div v-else class="gl-mt-5">
<skeleton-loader />
<skeleton-loader />
diff --git a/app/assets/javascripts/whats_new/components/feature.vue b/app/assets/javascripts/whats_new/components/feature.vue
new file mode 100644
index 00000000000..32fb2bd34a5
--- /dev/null
+++ b/app/assets/javascripts/whats_new/components/feature.vue
@@ -0,0 +1,64 @@
+<script>
+import { GlBadge, GlIcon, GlLink } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlBadge,
+ GlIcon,
+ GlLink,
+ },
+ props: {
+ feature: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-pb-7 gl-pt-5 gl-px-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100">
+ <gl-link
+ :href="feature.url"
+ target="_blank"
+ class="whats-new-item-title-link"
+ data-track-event="click_whats_new_item"
+ :data-track-label="feature.title"
+ :data-track-property="feature.url"
+ >
+ <h5 class="gl-font-lg" data-test-id="feature-title">{{ feature.title }}</h5>
+ </gl-link>
+ <div v-if="feature.packages" class="gl-mb-3">
+ <gl-badge
+ v-for="packageName in feature.packages"
+ :key="packageName"
+ size="sm"
+ class="whats-new-item-badge gl-mr-2"
+ >
+ <gl-icon name="license" />{{ packageName }}
+ </gl-badge>
+ </div>
+ <gl-link
+ :href="feature.url"
+ target="_blank"
+ data-track-event="click_whats_new_item"
+ :data-track-label="feature.title"
+ :data-track-property="feature.url"
+ >
+ <img
+ :alt="feature.title"
+ :src="feature.image_url"
+ class="img-thumbnail gl-px-8 gl-py-3 whats-new-item-image"
+ />
+ </gl-link>
+ <p class="gl-pt-3">{{ feature.body }}</p>
+ <gl-link
+ :href="feature.url"
+ target="_blank"
+ data-track-event="click_whats_new_item"
+ :data-track-label="feature.title"
+ :data-track-property="feature.url"
+ >{{ __('Learn more') }}</gl-link
+ >
+ </div>
+</template>
diff --git a/app/assets/javascripts/whats_new/index.js b/app/assets/javascripts/whats_new/index.js
index 2b9e7a2815e..ed0258c3992 100644
--- a/app/assets/javascripts/whats_new/index.js
+++ b/app/assets/javascripts/whats_new/index.js
@@ -10,8 +10,6 @@ export default el => {
if (whatsNewApp) {
store.dispatch('openDrawer');
} else {
- const storageKey = getStorageKey(el);
-
whatsNewApp = new Vue({
el,
store,
@@ -28,7 +26,11 @@ export default el => {
},
render(createElement) {
return createElement('app', {
- props: { storageKey },
+ props: {
+ storageKey: getStorageKey(el),
+ versions: JSON.parse(el.getAttribute('data-versions')),
+ gitlabDotCom: el.getAttribute('data-gitlab-dot-com'),
+ },
});
},
});
diff --git a/app/assets/javascripts/whats_new/store/actions.js b/app/assets/javascripts/whats_new/store/actions.js
index 532febd61cb..0e5eeda742a 100644
--- a/app/assets/javascripts/whats_new/store/actions.js
+++ b/app/assets/javascripts/whats_new/store/actions.js
@@ -13,7 +13,7 @@ export default {
localStorage.setItem(storageKey, JSON.stringify(false));
}
},
- fetchItems({ commit, state }, page) {
+ fetchItems({ commit, state }, { page, version } = { page: null, version: null }) {
if (state.fetching) {
return false;
}
@@ -24,6 +24,7 @@ export default {
.get('/-/whats_new', {
params: {
page,
+ version,
},
})
.then(({ data, headers }) => {
diff --git a/app/assets/stylesheets/components/whats_new.scss b/app/assets/stylesheets/components/whats_new.scss
index 64e82531c30..51bf2686be2 100644
--- a/app/assets/stylesheets/components/whats_new.scss
+++ b/app/assets/stylesheets/components/whats_new.scss
@@ -6,6 +6,32 @@
.gl-infinite-scroll-legend {
@include gl-display-none;
}
+
+ .gl-tabs {
+ @include gl-overflow-y-auto;
+ }
+
+ .gl-tabs-nav {
+ flex-wrap: nowrap;
+ overflow-x: scroll;
+ align-items: stretch;
+
+ .nav-item {
+ @include gl-flex-shrink-0;
+
+ a {
+ @include gl-h-full;
+ line-height: 1.5;
+ }
+ }
+ }
+
+ .gl-spinner-container {
+ @include gl-w-full;
+ @include gl-absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ }
}
.with-performance-bar .whats-new-drawer {
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
index 4a96d4fa612..9624c5095cc 100644
--- a/app/assets/stylesheets/page_bundles/oncall_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -28,3 +28,115 @@
}
}
}
+
+//// Copied from roadmaps.scss - adapted for on-call schedules
+$header-item-height: 60px;
+$details-cell-width: px-to-rem(150px);
+$timeline-cell-height: 32px;
+$timeline-cell-width: 180px;
+$border-style: 1px solid var(--gray-100, $gray-100);
+$gradient-dark-gray: rgba(0, 0, 0, 0.15);
+$gradient-gray: rgba(255, 255, 255, 0.001);
+$scroll-top-gradient: linear-gradient(to bottom, $gradient-dark-gray 0%, $gradient-gray 100%);
+$scroll-bottom-gradient: linear-gradient(to bottom, $gradient-gray 0%, $gradient-dark-gray 100%);
+$column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradient-gray 100%);
+$epic-details-cell-width: 150px;
+
+.schedule-shell {
+ @include gl-relative;
+ @include gl-h-full;
+ @include gl-w-full;
+ @include gl-overflow-x-auto;
+ @include gl-border-gray-100;
+ @include gl-border-1;
+ @include gl-border-solid;
+ @include gl-rounded-base;
+}
+
+.timeline-section {
+ @include gl-sticky;
+ position: -webkit-sticky;
+ @include gl-top-0;
+ z-index: 20;
+
+ .timeline-header-blank,
+ .timeline-header-item {
+ @include float-left;
+ height: $header-item-height;
+ border-bottom: $border-style;
+ background-color: var(--white, $white);
+ }
+
+ .timeline-header-blank {
+ @include gl-sticky;
+ position: -webkit-sticky;
+ @include gl-top-0;
+ @include gl-left-0;
+ width: $details-cell-width;
+ z-index: 2;
+
+ &::after {
+ height: $header-item-height;
+ @include gl-content-empty;
+ @include gl-absolute;
+ @include gl-top-0;
+ right: -$grid-size;
+ width: $grid-size;
+ @include gl-pointer-events-none;
+ background: $column-right-gradient;
+ }
+ }
+
+ .timeline-header-item {
+ // container size minus left panel width divided by 2 week timeframes
+ width: calc((100% - #{$epic-details-cell-width}) / 2);
+
+ &:last-of-type .item-label {
+ @include gl-border-r-0;
+ }
+
+ .item-label,
+ .item-sublabel .sublabel-value {
+ color: var(--gray-400, $gray-400);
+ @include gl-font-weight-normal;
+
+ &.label-dark {
+ @include gl-text-gray-900;
+ }
+
+ &.label-bold {
+ @include gl-font-weight-bold;
+ }
+ }
+
+ .item-label {
+ padding: $gl-padding-8 $gl-padding;
+ border-right: $border-style;
+ border-bottom: $border-style;
+ }
+
+ .item-sublabel {
+ @include gl-relative;
+ @include gl-display-flex;
+
+ .sublabel-value {
+ @include gl-flex-grow-1;
+ @include gl-flex-basis-0;
+
+ text-align: center;
+ font-size: $code-font-size;
+ line-height: 1.5;
+ padding: 2px 0;
+ }
+ }
+
+ .current-day-indicator-header {
+ @include gl-bottom-0;
+ height: $gl-vert-padding;
+ width: $gl-vert-padding;
+ background-color: var(--red-500, $red-500);
+ border-radius: 50%;
+ transform: translateX(-3px);
+ }
+ }
+}
diff --git a/app/controllers/whats_new_controller.rb b/app/controllers/whats_new_controller.rb
index 6ed15d9b127..cba86c65848 100644
--- a/app/controllers/whats_new_controller.rb
+++ b/app/controllers/whats_new_controller.rb
@@ -1,16 +1,19 @@
# frozen_string_literal: true
class WhatsNewController < ApplicationController
+ include Gitlab::Utils::StrongMemoize
+
skip_before_action :authenticate_user!
- before_action :check_feature_flag, :check_valid_page_param, :set_pagination_headers
+ before_action :check_feature_flag
+ before_action :check_valid_page_param, :set_pagination_headers, unless: -> { has_version_param? }
feature_category :navigation
def index
respond_to do |format|
format.js do
- render json: most_recent_items
+ render json: highlight_items
end
end
end
@@ -29,15 +32,25 @@ class WhatsNewController < ApplicationController
params[:page]&.to_i || 1
end
- def most_recent
- @most_recent ||= ReleaseHighlight.paginated(page: current_page)
+ def highlights
+ strong_memoize(:highlights) do
+ if has_version_param?
+ ReleaseHighlight.for_version(version: params[:version])
+ else
+ ReleaseHighlight.paginated(page: current_page)
+ end
+ end
end
- def most_recent_items
- most_recent[:items].map {|item| Gitlab::WhatsNew::ItemPresenter.present(item) }
+ def highlight_items
+ highlights.map {|item| Gitlab::WhatsNew::ItemPresenter.present(item) }
end
def set_pagination_headers
- response.set_header('X-Next-Page', most_recent[:next_page])
+ response.set_header('X-Next-Page', highlights.next_page)
+ end
+
+ def has_version_param?
+ params[:version].present?
end
end
diff --git a/app/helpers/whats_new_helper.rb b/app/helpers/whats_new_helper.rb
index f267ede3153..bbf5bde5904 100644
--- a/app/helpers/whats_new_helper.rb
+++ b/app/helpers/whats_new_helper.rb
@@ -6,10 +6,14 @@ module WhatsNewHelper
end
def whats_new_storage_key
- most_recent_version = ReleaseHighlight.most_recent_version
+ most_recent_version = ReleaseHighlight.versions&.first
return unless most_recent_version
['display-whats-new-notification', most_recent_version].join('-')
end
+
+ def whats_new_versions
+ ReleaseHighlight.versions
+ end
end
diff --git a/app/models/release_highlight.rb b/app/models/release_highlight.rb
index 436314de3a3..53545ad8933 100644
--- a/app/models/release_highlight.rb
+++ b/app/models/release_highlight.rb
@@ -3,6 +3,17 @@
class ReleaseHighlight
CACHE_DURATION = 1.hour
FILES_PATH = Rails.root.join('data', 'whats_new', '*.yml')
+ RELEASE_VERSIONS_IN_A_YEAR = 12
+
+ def self.for_version(version:)
+ index = self.versions.index(version)
+
+ return if index.nil?
+
+ page = index + 1
+
+ self.paginated(page: page)
+ end
def self.paginated(page: 1)
Rails.cache.fetch(cache_key(page), expires_in: CACHE_DURATION) do
@@ -10,10 +21,7 @@ class ReleaseHighlight
next if items.nil?
- {
- items: items,
- next_page: next_page(current_page: page)
- }
+ QueryResult.new(items: items, next_page: next_page(current_page: page))
end
end
@@ -53,15 +61,25 @@ class ReleaseHighlight
next_page if self.file_paths[next_index]
end
- def self.most_recent_version
- Gitlab::ProcessMemoryCache.cache_backend.fetch('release_highlight:release_version', expires_in: CACHE_DURATION) do
- self.paginated&.[](:items)&.first&.[]('release')
+ def self.most_recent_item_count
+ Gitlab::ProcessMemoryCache.cache_backend.fetch('release_highlight:recent_item_count', expires_in: CACHE_DURATION) do
+ self.paginated&.items&.count
end
end
- def self.most_recent_item_count
- Gitlab::ProcessMemoryCache.cache_backend.fetch('release_highlight:recent_item_count', expires_in: CACHE_DURATION) do
- self.paginated&.[](:items)&.count
+ def self.versions
+ Gitlab::ProcessMemoryCache.cache_backend.fetch('release_highlight:versions', expires_in: CACHE_DURATION) do
+ versions = self.file_paths.first(RELEASE_VERSIONS_IN_A_YEAR).map do |path|
+ /\d*\_(\d*\_\d*)\.yml$/.match(path).captures[0].gsub(/0(?=\d)/, "").tr("_", ".")
+ end
+
+ versions.uniq
end
end
+
+ QueryResult = Struct.new(:items, :next_page, keyword_init: true) do
+ include Enumerable
+
+ delegate :each, to: :items
+ end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index f9ef7230661..c94b9c3b1cc 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -70,6 +70,7 @@ class Service < ApplicationRecord
scope :by_type, -> (type) { where(type: type) }
scope :by_active_flag, -> (flag) { where(active: flag) }
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_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)) }
@@ -278,7 +279,7 @@ class Service < ApplicationRecord
active.where(instance: true),
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|
- build_from_integration(records.first, association => scope.id).save!
+ build_from_integration(records.first, association => scope.id).save
end
end
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index aad574aeaf5..e800e546a45 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -28,9 +28,11 @@ module Groups
Group.transaction do
update_group_attributes
ensure_ownership
+ update_integrations
end
post_update_hooks(@updated_project_ids)
+ propagate_integrations
true
end
@@ -196,6 +198,17 @@ module Groups
raise TransferError, result[:message] unless result[:status] == :success
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
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 5178c76f0fc..1574c90d2ac 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -59,7 +59,7 @@ module Projects
raise TransferError.new(s_("TransferProject|Root namespace can't be updated if project has NPM packages"))
end
- attempt_transfer_transaction
+ proceed_to_transfer
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -67,7 +67,7 @@ module Projects
new_namespace.root_ancestor == project.namespace.root_ancestor
end
- def attempt_transfer_transaction
+ def proceed_to_transfer
Project.transaction do
project.expire_caches_before_rename(@old_path)
@@ -87,6 +87,8 @@ module Projects
# Move uploads
move_project_uploads(project)
+ update_integrations
+
project.old_path_with_namespace = @old_path
update_repository_configuration(@new_path)
@@ -214,6 +216,11 @@ module Projects
project.shared_runners_enabled = false
end
end
+
+ def update_integrations
+ project.services.inherit.delete_all
+ Service.create_from_active_default_integrations(project, :project_id)
+ end
end
end
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 8aba9426ec0..70ab0a56581 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -102,7 +102,7 @@
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
- if ::Feature.enabled?(:whats_new_drawer, current_user)
- #whats-new-app{ data: { storage_key: whats_new_storage_key } }
+ #whats-new-app{ data: { storage_key: whats_new_storage_key, versions: whats_new_versions, gitlab_dot_com: Gitlab.dev_env_org_or_com? } }
- if can?(current_user, :update_user_status, current_user)
.js-set-status-modal-wrapper{ data: user_status_data }
diff --git a/changelogs/unreleased/255923-transfer-project-group-with-integrations.yml b/changelogs/unreleased/255923-transfer-project-group-with-integrations.yml
new file mode 100644
index 00000000000..10177cbb6e7
--- /dev/null
+++ b/changelogs/unreleased/255923-transfer-project-group-with-integrations.yml
@@ -0,0 +1,5 @@
+---
+title: Transfer a project/group to a new namespace inheriting integrations
+merge_request: 48621
+author:
+type: changed
diff --git a/changelogs/unreleased/273270-save-button-should-have-a-different-color-on-press.yml b/changelogs/unreleased/273270-save-button-should-have-a-different-color-on-press.yml
new file mode 100644
index 00000000000..023289cc572
--- /dev/null
+++ b/changelogs/unreleased/273270-save-button-should-have-a-different-color-on-press.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Save button should have a different color on press
+merge_request: 48975
+author:
+type: fixed
diff --git a/changelogs/unreleased/tr-update-issue-to-incident.yml b/changelogs/unreleased/tr-update-issue-to-incident.yml
new file mode 100644
index 00000000000..09fa0a6cbeb
--- /dev/null
+++ b/changelogs/unreleased/tr-update-issue-to-incident.yml
@@ -0,0 +1,5 @@
+---
+title: Use incident instead of issue for operation settings
+merge_request: 48406
+author:
+type: fixed
diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png
deleted file mode 100644
index ac09e1d0137..00000000000
--- a/doc/ci/quick_start/img/runners_activated.png
+++ /dev/null
Binary files differ
diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb
index 03dd9507a9b..0cdff9c735a 100644
--- a/lib/gitlab/experimentation.rb
+++ b/lib/gitlab/experimentation.rb
@@ -5,7 +5,10 @@
# Utility module for A/B testing experimental features. Define your experiments in the `EXPERIMENTS` constant.
# Experiment options:
# - 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),
# which is named "#{experiment_key}_experiment_percentage" and *must* be set with a percentage and not be used for other purposes.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0cbe42faeee..9efeaff1628 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -941,6 +941,9 @@ msgstr ""
msgid "(No changes)"
msgstr ""
+msgid "(UTC%{offset}) %{timezone}"
+msgstr ""
+
msgid "(check progress)"
msgstr ""
@@ -4169,7 +4172,7 @@ msgstr ""
msgid "Automatic deployment rollbacks"
msgstr ""
-msgid "Automatically close incident issues when the associated Prometheus alert resolves."
+msgid "Automatically close incidents when the associated Prometheus alert resolves."
msgstr ""
msgid "Automatically create merge requests for vulnerabilities that have fixes available."
@@ -7919,7 +7922,7 @@ msgstr ""
msgid "Create an account using:"
msgstr ""
-msgid "Create an issue. Issues are created for each alert triggered."
+msgid "Create an incident. Incidents are created for each alert triggered."
msgstr ""
msgid "Create and provide your GitHub %{link_start}Personal Access Token%{link_end}. You will need to select the %{code_open}repo%{code_close} scope, so we can display a list of your public and private repositories which are available to import."
@@ -14377,6 +14380,9 @@ msgstr ""
msgid "Incident Management Limits"
msgstr ""
+msgid "Incident template (optional)"
+msgstr ""
+
msgid "IncidentManagement|%{hours} hours, %{minutes} minutes remaining"
msgstr ""
@@ -15207,9 +15213,6 @@ msgstr ""
msgid "Issue published on status page."
msgstr ""
-msgid "Issue template (optional)"
-msgstr ""
-
msgid "Issue update failed"
msgstr ""
@@ -19112,6 +19115,12 @@ msgstr ""
msgid "OnCallSchedules|Failed to add schedule"
msgstr ""
+msgid "OnCallSchedules|On-call schedule"
+msgstr ""
+
+msgid "OnCallSchedules|On-call schedule for the %{tzShort}"
+msgstr ""
+
msgid "OnCallSchedules|Rotation length"
msgstr ""
@@ -19729,7 +19738,7 @@ msgstr ""
msgid "PagerDutySettings|Active"
msgstr ""
-msgid "PagerDutySettings|Create a GitLab issue for each PagerDuty incident by %{docsLink}"
+msgid "PagerDutySettings|Create a GitLab incident for each PagerDuty incident by %{linkStart}configuring a webhook in PagerDuty%{linkEnd}"
msgstr ""
msgid "PagerDutySettings|Failed to update Webhook URL"
@@ -19741,18 +19750,12 @@ msgstr ""
msgid "PagerDutySettings|Resetting the webhook URL for this project will require updating this integration's settings in PagerDuty."
msgstr ""
-msgid "PagerDutySettings|Setting up a webhook with PagerDuty will automatically create a GitLab issue for each PagerDuty incident."
-msgstr ""
-
msgid "PagerDutySettings|Webhook URL"
msgstr ""
msgid "PagerDutySettings|Webhook URL update was successful"
msgstr ""
-msgid "PagerDutySettings|configuring a webhook in PagerDuty"
-msgstr ""
-
msgid "Pages"
msgstr ""
@@ -25174,6 +25177,9 @@ msgstr ""
msgid "Show file contents"
msgstr ""
+msgid "Show labels"
+msgstr ""
+
msgid "Show latest version"
msgstr ""
@@ -31711,6 +31717,9 @@ msgstr ""
msgid "Your U2F device was registered!"
msgstr ""
+msgid "Your Version"
+msgstr ""
+
msgid "Your WebAuthn device did not send a valid JSON response."
msgstr ""
diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb
index de7251db5c9..1d9f256a819 100644
--- a/spec/features/projects/settings/operations_settings_spec.rb
+++ b/spec/features/projects/settings/operations_settings_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
describe 'Settings > Operations' do
describe 'Incidents' do
- let(:create_issue) { 'Create an issue. Issues are created for each alert triggered.' }
+ let(:create_issue) { 'Create an incident. Incidents are created for each alert triggered.' }
let(:send_email) { 'Send a separate email notification to Developers.' }
before do
diff --git a/spec/frontend/boards/components/board_assignee_dropdown_spec.js b/spec/frontend/boards/components/board_assignee_dropdown_spec.js
index 499e6b5882d..bbdcc707f09 100644
--- a/spec/frontend/boards/components/board_assignee_dropdown_spec.js
+++ b/spec/frontend/boards/components/board_assignee_dropdown_spec.js
@@ -14,7 +14,7 @@ import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dro
import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
import store from '~/boards/stores';
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';
const localVue = createLocalVue();
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 679a83bf55f..360665c77be 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -14,8 +14,8 @@ import {
import actions, { gqlClient } from '~/boards/stores/actions';
import * as types from '~/boards/stores/mutation_types';
import { inactiveId } from '~/boards/constants';
-import issueMoveListMutation from '~/boards/queries/issue_move_list.mutation.graphql';
-import destroyBoardListMutation from '~/boards/queries/board_list_destroy.mutation.graphql';
+import issueMoveListMutation from '~/boards/graphql/issue_move_list.mutation.graphql';
+import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import updateAssignees from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
import { fullBoardId, formatListIssues, formatBoardLists } from '~/boards/boards_util';
diff --git a/spec/frontend/boards/stores/getters_spec.js b/spec/frontend/boards/stores/getters_spec.js
index fd63d6e9c82..3d5ba8ddc5b 100644
--- a/spec/frontend/boards/stores/getters_spec.js
+++ b/spec/frontend/boards/stores/getters_spec.js
@@ -10,24 +10,6 @@ import {
} from '../mock_data';
describe('Boards - Getters', () => {
- describe('labelToggleState', () => {
- it('should return "on" when isShowingLabels is true', () => {
- const state = {
- isShowingLabels: true,
- };
-
- expect(getters.labelToggleState(state)).toBe('on');
- });
-
- it('should return "off" when isShowingLabels is false', () => {
- const state = {
- isShowingLabels: false,
- };
-
- expect(getters.labelToggleState(state)).toBe('off');
- });
- });
-
describe('isSidebarOpen', () => {
it('returns true when activeId is not equal to 0', () => {
const state = {
diff --git a/spec/frontend/diffs/diff_file_spec.js b/spec/frontend/diffs/utils/diff_file_spec.js
index 5d74760ef66..dfa63e51778 100644
--- a/spec/frontend/diffs/diff_file_spec.js
+++ b/spec/frontend/diffs/utils/diff_file_spec.js
@@ -1,4 +1,4 @@
-import { prepareRawDiffFile } from '~/diffs/diff_file';
+import { prepareRawDiffFile } from '~/diffs/utils/diff_file';
const DIFF_FILES = [
{
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
index 26ff4aa657b..c3fd4a9bab2 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
@@ -17,7 +17,7 @@ exports[`Alert integration settings form default state should match the default
data-qa-selector="create_issue_checkbox"
>
<span>
- Create an issue. Issues are created for each alert triggered.
+ Create an incident. Incidents are created for each alert triggered.
</span>
</gl-form-checkbox-stub>
</gl-form-group-stub>
@@ -32,7 +32,7 @@ exports[`Alert integration settings form default state should match the default
for="alert-integration-settings-issue-template"
>
- Issue template (optional)
+ Incident template (optional)
<gl-link-stub
href="/help/user/project/description_templates#creating-issue-templates"
@@ -89,7 +89,7 @@ exports[`Alert integration settings form default state should match the default
checked="true"
>
<span>
- Automatically close incident issues when the associated Prometheus alert resolves.
+ Automatically close incidents when the associated Prometheus alert resolves.
</span>
</gl-form-checkbox-stub>
</gl-form-group-stub>
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
index 273356151fc..f0eb54c1b3a 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
@@ -5,7 +5,9 @@ exports[`Alert integration settings form should match the default snapshot 1`] =
<!---->
<p>
- Setting up a webhook with PagerDuty will automatically create a GitLab issue for each PagerDuty incident.
+ <gl-sprintf-stub
+ message="Create a GitLab incident for each PagerDuty incident by %{linkStart}configuring a webhook in PagerDuty%{linkEnd}"
+ />
</p>
<form>
@@ -33,18 +35,10 @@ exports[`Alert integration settings form should match the default snapshot 1`] =
value="pagerduty.webhook.com"
/>
- <div
- class="gl-text-gray-200 gl-pt-2"
- >
- <gl-sprintf-stub
- message="Create a GitLab issue for each PagerDuty incident by %{docsLink}"
- />
- </div>
-
<gl-button-stub
buttontextclasses=""
category="primary"
- class="gl-mt-3"
+ class="gl-mt-5"
data-testid="webhook-reset-btn"
icon=""
role="button"
diff --git a/spec/frontend/sidebar/sidebar_labels_spec.js b/spec/frontend/sidebar/sidebar_labels_spec.js
index 36d1e129b6a..ab08a1e65e2 100644
--- a/spec/frontend/sidebar/sidebar_labels_spec.js
+++ b/spec/frontend/sidebar/sidebar_labels_spec.js
@@ -3,7 +3,7 @@ import {
mockLabels,
mockRegularLabel,
} 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 { IssuableType } from '~/issue_show/constants';
import SidebarLabels from '~/sidebar/components/labels/sidebar_labels.vue';
diff --git a/spec/frontend/whats_new/components/app_spec.js b/spec/frontend/whats_new/components/app_spec.js
index cba550b19db..7a9340da87a 100644
--- a/spec/frontend/whats_new/components/app_spec.js
+++ b/spec/frontend/whats_new/components/app_spec.js
@@ -1,6 +1,6 @@
import { createLocalVue, mount } from '@vue/test-utils';
import Vuex from 'vuex';
-import { GlDrawer, GlInfiniteScroll } from '@gitlab/ui';
+import { GlDrawer, GlInfiniteScroll, GlTabs } from '@gitlab/ui';
import { mockTracking, unmockTracking, triggerEvent } from 'helpers/tracking_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import App from '~/whats_new/components/app.vue';
@@ -16,12 +16,18 @@ const localVue = createLocalVue();
localVue.use(Vuex);
describe('App', () => {
- const propsData = { storageKey: 'storage-key' };
let wrapper;
let store;
let actions;
let state;
let trackingSpy;
+ let gitlabDotCom = true;
+
+ const buildProps = () => ({
+ storageKey: 'storage-key',
+ versions: ['3.11', '3.10'],
+ gitlabDotCom,
+ });
const buildWrapper = () => {
actions = {
@@ -45,7 +51,7 @@ describe('App', () => {
wrapper = mount(App, {
localVue,
store,
- propsData,
+ propsData: buildProps(),
directives: {
GlResizeObserver: createMockDirective(),
},
@@ -53,112 +59,171 @@ describe('App', () => {
};
const findInfiniteScroll = () => wrapper.find(GlInfiniteScroll);
- const emitBottomReached = () => findInfiniteScroll().vm.$emit('bottomReached');
- beforeEach(async () => {
+ const setup = async () => {
document.body.dataset.page = 'test-page';
document.body.dataset.namespaceId = 'namespace-840';
trackingSpy = mockTracking('_category_', null, jest.spyOn);
buildWrapper();
- wrapper.vm.$store.state.features = [{ title: 'Whats New Drawer', url: 'www.url.com' }];
+ wrapper.vm.$store.state.features = [
+ { title: 'Whats New Drawer', url: 'www.url.com', release: 3.11 },
+ ];
wrapper.vm.$store.state.drawerBodyHeight = MOCK_DRAWER_BODY_HEIGHT;
await wrapper.vm.$nextTick();
- });
+ };
afterEach(() => {
wrapper.destroy();
unmockTracking();
});
- const getDrawer = () => wrapper.find(GlDrawer);
+ describe('gitlab.com', () => {
+ beforeEach(() => {
+ setup();
+ });
- it('contains a drawer', () => {
- expect(getDrawer().exists()).toBe(true);
- });
+ const getDrawer = () => wrapper.find(GlDrawer);
- it('dispatches openDrawer and tracking calls when mounted', () => {
- expect(actions.openDrawer).toHaveBeenCalledWith(expect.any(Object), 'storage-key');
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_whats_new_drawer', {
- label: 'namespace_id',
- value: 'namespace-840',
+ it('contains a drawer', () => {
+ expect(getDrawer().exists()).toBe(true);
});
- });
- it('dispatches closeDrawer when clicking close', () => {
- getDrawer().vm.$emit('close');
- expect(actions.closeDrawer).toHaveBeenCalled();
- });
+ it('dispatches openDrawer and tracking calls when mounted', () => {
+ expect(actions.openDrawer).toHaveBeenCalledWith(expect.any(Object), 'storage-key');
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_whats_new_drawer', {
+ label: 'namespace_id',
+ value: 'namespace-840',
+ });
+ });
- it.each([true, false])('passes open property', async openState => {
- wrapper.vm.$store.state.open = openState;
+ it('dispatches closeDrawer when clicking close', () => {
+ getDrawer().vm.$emit('close');
+ expect(actions.closeDrawer).toHaveBeenCalled();
+ });
- await wrapper.vm.$nextTick();
+ it.each([true, false])('passes open property', async openState => {
+ wrapper.vm.$store.state.open = openState;
- expect(getDrawer().props('open')).toBe(openState);
- });
+ await wrapper.vm.$nextTick();
- it('renders features when provided via ajax', () => {
- expect(actions.fetchItems).toHaveBeenCalled();
- expect(wrapper.find('h5').text()).toBe('Whats New Drawer');
- });
+ expect(getDrawer().props('open')).toBe(openState);
+ });
- it('send an event when feature item is clicked', () => {
- trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
+ it('renders features when provided via ajax', () => {
+ expect(actions.fetchItems).toHaveBeenCalled();
+ expect(wrapper.find('[data-test-id="feature-title"]').text()).toBe('Whats New Drawer');
+ });
- const link = wrapper.find('.whats-new-item-title-link');
- triggerEvent(link.element);
+ it('send an event when feature item is clicked', () => {
+ trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
- expect(trackingSpy.mock.calls[1]).toMatchObject([
- '_category_',
- 'click_whats_new_item',
- {
- label: 'Whats New Drawer',
- property: 'www.url.com',
- },
- ]);
- });
+ const link = wrapper.find('.whats-new-item-title-link');
+ triggerEvent(link.element);
+
+ expect(trackingSpy.mock.calls[1]).toMatchObject([
+ '_category_',
+ 'click_whats_new_item',
+ {
+ label: 'Whats New Drawer',
+ property: 'www.url.com',
+ },
+ ]);
+ });
+
+ it('renders infinite scroll', () => {
+ const scroll = findInfiniteScroll();
+
+ expect(scroll.props()).toMatchObject({
+ fetchedItems: wrapper.vm.$store.state.features.length,
+ maxListHeight: MOCK_DRAWER_BODY_HEIGHT,
+ });
+ });
+
+ describe('bottomReached', () => {
+ const emitBottomReached = () => findInfiniteScroll().vm.$emit('bottomReached');
- it('renders infinite scroll', () => {
- const scroll = findInfiniteScroll();
+ beforeEach(() => {
+ actions.fetchItems.mockClear();
+ });
- expect(scroll.props()).toMatchObject({
- fetchedItems: wrapper.vm.$store.state.features.length,
- maxListHeight: MOCK_DRAWER_BODY_HEIGHT,
+ it('when nextPage exists it calls fetchItems', () => {
+ wrapper.vm.$store.state.pageInfo = { nextPage: 840 };
+ emitBottomReached();
+
+ expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), { page: 840 });
+ });
+
+ it('when nextPage does not exist it does not call fetchItems', () => {
+ wrapper.vm.$store.state.pageInfo = { nextPage: null };
+ emitBottomReached();
+
+ expect(actions.fetchItems).not.toHaveBeenCalled();
+ });
+ });
+
+ it('calls getDrawerBodyHeight and setDrawerBodyHeight when resize directive is triggered', () => {
+ const { value } = getBinding(getDrawer().element, 'gl-resize-observer');
+
+ value();
+
+ expect(getDrawerBodyHeight).toHaveBeenCalledWith(wrapper.find(GlDrawer).element);
+
+ expect(actions.setDrawerBodyHeight).toHaveBeenCalledWith(
+ expect.any(Object),
+ MOCK_DRAWER_BODY_HEIGHT,
+ );
});
});
- describe('bottomReached', () => {
+ describe('self managed', () => {
+ const findTabs = () => wrapper.find(GlTabs);
+
+ const clickSecondTab = async () => {
+ const secondTab = wrapper.findAll('.nav-link').at(1);
+ await secondTab.trigger('click');
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ };
+
beforeEach(() => {
- actions.fetchItems.mockClear();
+ gitlabDotCom = false;
+ setup();
});
- it('when nextPage exists it calls fetchItems', () => {
- wrapper.vm.$store.state.pageInfo = { nextPage: 840 };
- emitBottomReached();
+ it('renders tabs with drawer body height and content', () => {
+ const scroll = findInfiniteScroll();
+ const tabs = findTabs();
- expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), 840);
+ expect(scroll.exists()).toBe(false);
+ expect(tabs.attributes().style).toBe(`height: ${MOCK_DRAWER_BODY_HEIGHT}px;`);
+ expect(wrapper.find('h5').text()).toBe('Whats New Drawer');
});
- it('when nextPage does not exist it does not call fetchItems', () => {
- wrapper.vm.$store.state.pageInfo = { nextPage: null };
- emitBottomReached();
+ describe('fetchVersion', () => {
+ beforeEach(() => {
+ actions.fetchItems.mockClear();
+ });
- expect(actions.fetchItems).not.toHaveBeenCalled();
- });
- });
+ it('when version isnt fetched, clicking a tab calls fetchItems', async () => {
+ const fetchVersionSpy = jest.spyOn(wrapper.vm, 'fetchVersion');
+ await clickSecondTab();
- it('calls getDrawerBodyHeight and setDrawerBodyHeight when resize directive is triggered', () => {
- const { value } = getBinding(getDrawer().element, 'gl-resize-observer');
+ expect(fetchVersionSpy).toHaveBeenCalledWith('3.10');
+ expect(actions.fetchItems).toHaveBeenCalledWith(expect.anything(), { version: '3.10' });
+ });
- value();
+ it('when version has been fetched, clicking a tab calls fetchItems', async () => {
+ wrapper.vm.$store.state.features.push({ title: 'GitLab Stories', release: 3.1 });
+ await wrapper.vm.$nextTick();
- expect(getDrawerBodyHeight).toHaveBeenCalledWith(wrapper.find(GlDrawer).element);
+ const fetchVersionSpy = jest.spyOn(wrapper.vm, 'fetchVersion');
+ await clickSecondTab();
- expect(actions.setDrawerBodyHeight).toHaveBeenCalledWith(
- expect.any(Object),
- MOCK_DRAWER_BODY_HEIGHT,
- );
+ expect(fetchVersionSpy).toHaveBeenCalledWith('3.10');
+ expect(actions.fetchItems).not.toHaveBeenCalled();
+ expect(wrapper.find('.tab-pane.active h5').text()).toBe('GitLab Stories');
+ });
+ });
});
});
diff --git a/spec/frontend/whats_new/store/actions_spec.js b/spec/frontend/whats_new/store/actions_spec.js
index 12722b1b3b1..82f17a2726f 100644
--- a/spec/frontend/whats_new/store/actions_spec.js
+++ b/spec/frontend/whats_new/store/actions_spec.js
@@ -41,6 +41,23 @@ describe('whats new actions', () => {
axiosMock.restore();
});
+ it('passes arguments', () => {
+ axiosMock.reset();
+
+ axiosMock
+ .onGet('/-/whats_new', { params: { page: 8, version: 40 } })
+ .replyOnce(200, [{ title: 'GitLab Stories' }]);
+
+ testAction(
+ actions.fetchItems,
+ { page: 8, version: 40 },
+ {},
+ expect.arrayContaining([
+ { type: types.ADD_FEATURES, payload: [{ title: 'GitLab Stories' }] },
+ ]),
+ );
+ });
+
it('if already fetching, does not fetch', () => {
testAction(actions.fetchItems, {}, { fetching: true }, []);
});
diff --git a/spec/helpers/whats_new_helper_spec.rb b/spec/helpers/whats_new_helper_spec.rb
index cdb4fc60629..017826921ff 100644
--- a/spec/helpers/whats_new_helper_spec.rb
+++ b/spec/helpers/whats_new_helper_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe WhatsNewHelper do
let(:release_item) { double(:item) }
before do
- allow(ReleaseHighlight).to receive(:most_recent_version).and_return(84.0)
+ allow(ReleaseHighlight).to receive(:versions).and_return([84.0])
end
it { is_expected.to eq('display-whats-new-notification-84.0') }
@@ -18,7 +18,7 @@ RSpec.describe WhatsNewHelper do
context 'when most recent release highlights do NOT exist' do
before do
- allow(ReleaseHighlight).to receive(:most_recent_version).and_return(nil)
+ allow(ReleaseHighlight).to receive(:versions).and_return(nil)
end
it { is_expected.to be_nil }
@@ -44,4 +44,14 @@ RSpec.describe WhatsNewHelper do
end
end
end
+
+ describe '#whats_new_versions' do
+ let(:versions) { [84.0] }
+
+ it 'returns ReleaseHighlight.versions' do
+ expect(ReleaseHighlight).to receive(:versions).and_return(versions)
+
+ expect(helper.whats_new_versions).to eq(versions)
+ end
+ end
end
diff --git a/spec/models/release_highlight_spec.rb b/spec/models/release_highlight_spec.rb
index b7817a04134..15477144894 100644
--- a/spec/models/release_highlight_spec.rb
+++ b/spec/models/release_highlight_spec.rb
@@ -3,21 +3,44 @@
require 'spec_helper'
RSpec.describe ReleaseHighlight do
- describe '#paginated' do
- let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) }
- let(:cache_mock) { double(:cache_mock) }
+ let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) }
+ let(:cache_mock) { double(:cache_mock) }
+
+ before do
+ allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
+ allow(cache_mock).to receive(:fetch).with('release_highlight:file_paths', expires_in: 1.hour).and_yield
+ end
+
+ after do
+ ReleaseHighlight.instance_variable_set(:@file_paths, nil)
+ end
+
+ describe '.for_version' do
+ subject { ReleaseHighlight.for_version(version: version) }
+
+ let(:version) { '1.1' }
+
+ context 'with version param that exists' do
+ it 'returns items from that version' do
+ expect(subject.items.first['title']).to eq("It's gonna be a bright")
+ end
+ end
+
+ context 'with version param that does NOT exist' do
+ let(:version) { '84.0' }
+
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+ end
+
+ describe '.paginated' do
let(:dot_com) { false }
before do
allow(Gitlab).to receive(:com?).and_return(dot_com)
- allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
-
expect(Rails).to receive(:cache).twice.and_return(cache_mock)
- expect(cache_mock).to receive(:fetch).with('release_highlight:file_paths', expires_in: 1.hour).and_yield
- end
-
- after do
- ReleaseHighlight.instance_variable_set(:@file_paths, nil)
end
context 'with page param' do
@@ -90,46 +113,51 @@ RSpec.describe ReleaseHighlight do
end
end
- describe '.most_recent_version' do
- subject { ReleaseHighlight.most_recent_version }
+ describe '.most_recent_item_count' do
+ subject { ReleaseHighlight.most_recent_item_count }
- context 'when version exist' do
- let(:release_item) { double(:item) }
+ context 'when recent release items exist' do
+ it 'returns the count from the most recent file' do
+ allow(ReleaseHighlight).to receive(:paginated).and_return(double(:paginated, items: [double(:item)]))
- before do
- allow(ReleaseHighlight).to receive(:paginated).and_return({ items: [release_item] })
- allow(release_item).to receive(:[]).with('release').and_return(84.0)
+ expect(subject).to eq(1)
end
-
- it { is_expected.to eq(84.0) }
end
- context 'when most recent release highlights do NOT exist' do
- before do
+ context 'when recent release items do NOT exist' do
+ it 'returns nil' do
allow(ReleaseHighlight).to receive(:paginated).and_return(nil)
- end
- it { is_expected.to be_nil }
+ expect(subject).to be_nil
+ end
end
end
- describe '#most_recent_item_count' do
- subject { ReleaseHighlight.most_recent_item_count }
+ describe '.versions' do
+ it 'returns versions from the file paths' do
+ expect(ReleaseHighlight.versions).to eq(['1.5', '1.2', '1.1'])
+ end
- context 'when recent release items exist' do
- it 'returns the count from the most recent file' do
- allow(ReleaseHighlight).to receive(:paginated).and_return({ items: [double(:item)] })
+ context 'when there are more than 12 versions' do
+ let(:file_paths) do
+ i = 0
+ Array.new(20) { "20201225_01_#{i += 1}.yml" }
+ end
- expect(subject).to eq(1)
+ it 'limits to 12 versions' do
+ allow(ReleaseHighlight).to receive(:file_paths).and_return(file_paths)
+ expect(ReleaseHighlight.versions.count).to eq(12)
end
end
+ end
- context 'when recent release items do NOT exist' do
- it 'returns nil' do
- allow(ReleaseHighlight).to receive(:paginated).and_return(nil)
+ describe 'QueryResult' do
+ subject { ReleaseHighlight::QueryResult.new(items: items, next_page: 2) }
- expect(subject).to be_nil
- end
+ let(:items) { [:item] }
+
+ it 'responds to map' do
+ expect(subject.map(&:to_s)).to eq(items.map(&:to_s))
end
end
end
diff --git a/spec/requests/whats_new_controller_spec.rb b/spec/requests/whats_new_controller_spec.rb
index 30d741ee0f0..8005d38dbb0 100644
--- a/spec/requests/whats_new_controller_spec.rb
+++ b/spec/requests/whats_new_controller_spec.rb
@@ -4,22 +4,22 @@ require 'spec_helper'
RSpec.describe WhatsNewController do
describe 'whats_new_path' do
+ let(:item) { double(:item) }
+ let(:highlights) { double(:highlight, items: [item], map: [item].map, next_page: 2) }
+
context 'with whats_new_drawer feature enabled' do
before do
stub_feature_flags(whats_new_drawer: true)
end
context 'with no page param' do
- let(:most_recent) { { items: [item], next_page: 2 } }
- let(:item) { double(:item) }
-
it 'responds with paginated data and headers' do
- allow(ReleaseHighlight).to receive(:paginated).with(page: 1).and_return(most_recent)
+ allow(ReleaseHighlight).to receive(:paginated).with(page: 1).and_return(highlights)
allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item)
get whats_new_path, xhr: true
- expect(response.body).to eq(most_recent[:items].to_json)
+ expect(response.body).to eq(highlights.items.to_json)
expect(response.headers['X-Next-Page']).to eq(2)
end
end
@@ -37,6 +37,18 @@ RSpec.describe WhatsNewController do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ context 'with version param' do
+ it 'returns items without pagination headers' do
+ allow(ReleaseHighlight).to receive(:for_version).with(version: '42').and_return(highlights)
+ allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item)
+
+ get whats_new_path(version: 42), xhr: true
+
+ expect(response.body).to eq(highlights.items.to_json)
+ expect(response.headers['X-Next-Page']).to be_nil
+ end
+ end
end
context 'with whats_new_drawer feature disabled' do
diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb
index ae04eca3a9f..19b746ade34 100644
--- a/spec/services/groups/transfer_service_spec.rb
+++ b/spec/services/groups/transfer_service_spec.rb
@@ -3,15 +3,15 @@
require 'spec_helper'
RSpec.describe Groups::TransferService do
- let(:user) { create(:user) }
- let(:new_parent_group) { create(:group, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:new_parent_group) { create(:group, :public) }
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
let(:transfer_service) { described_class.new(group, user) }
context 'handling packages' do
let_it_be(:group) { create(:group, :public) }
+ let_it_be(:new_group) { create(:group, :public) }
let(:project) { create(:project, :public, namespace: group) }
- let(:new_group) { create(:group, :public) }
before do
group.add_owner(user)
@@ -35,8 +35,8 @@ RSpec.describe Groups::TransferService do
it_behaves_like 'transfer not allowed'
context 'with a project within subgroup' do
- let(:root_group) { create(:group) }
- let(:group) { create(:group, parent: root_group) }
+ let_it_be(:root_group) { create(:group) }
+ let_it_be(:group) { create(:group, parent: root_group) }
before do
root_group.add_owner(user)
@@ -79,8 +79,6 @@ RSpec.describe Groups::TransferService do
shared_examples 'ensuring allowed transfer for a group' do
context "when there's an exception on GitLab shell directories" do
- let(:new_parent_group) { create(:group, :public) }
-
before do
allow_next_instance_of(described_class) do |instance|
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
describe '#execute' 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'
@@ -115,7 +113,7 @@ RSpec.describe Groups::TransferService do
end
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
expect(transfer_service.execute(nil)).to be_falsy
@@ -128,7 +126,7 @@ RSpec.describe Groups::TransferService do
end
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
create(:group, path: 'not-unique')
@@ -145,9 +143,9 @@ RSpec.describe Groups::TransferService do
end
context 'when the group is a subgroup and the transfer is valid' do
- let!(:subgroup1) { create(:group, :private, parent: group) }
- let!(:subgroup2) { create(:group, :internal, parent: group) }
- let!(:project1) { create(:project, :repository, :private, namespace: group) }
+ let_it_be(:subgroup1) { create(:group, :private, parent: group) }
+ let_it_be(:subgroup2) { create(:group, :internal, parent: group) }
+ let_it_be(:project1) { create(:project, :repository, :private, namespace: group) }
before do
transfer_service.execute(nil)
@@ -173,12 +171,12 @@ RSpec.describe Groups::TransferService do
end
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'
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
expect(transfer_service.execute(new_parent_group)).to be_falsy
@@ -191,7 +189,7 @@ RSpec.describe Groups::TransferService do
end
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
expect(transfer_service.execute(new_parent_group)).to be_falsy
@@ -221,7 +219,7 @@ RSpec.describe Groups::TransferService do
end
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
create(:group_member, :owner, group: new_parent_group, user: user)
@@ -240,8 +238,13 @@ RSpec.describe Groups::TransferService do
end
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
+ allow(PropagateIntegrationWorker).to receive(:perform_async)
+
create(:group_member, :owner, group: new_parent_group, user: user)
+
transfer_service.execute(new_parent_group)
end
@@ -267,6 +270,30 @@ RSpec.describe Groups::TransferService do
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
expect(group.visibility_level).to eq(new_parent_group.visibility_level)
end
@@ -464,7 +491,7 @@ RSpec.describe Groups::TransferService do
end
context 'updated paths' do
- let(:group) { create(:group, :public) }
+ let_it_be_with_reload(:group) { create(:group, :public) }
before do
transfer_service.execute(new_parent_group)
@@ -500,10 +527,10 @@ RSpec.describe Groups::TransferService do
end
context 'resets project authorizations' do
- let(:old_parent_group) { create(:group) }
- let(:group) { create(:group, :private, parent: old_parent_group) }
- let(:new_group_member) { create(:user) }
- let(:old_group_member) { create(:user) }
+ let_it_be(:old_parent_group) { create(:group) }
+ let_it_be_with_reload(:group) { create(:group, :private, parent: old_parent_group) }
+ let_it_be(:new_group_member) { create(:user) }
+ let_it_be(:old_group_member) { create(:user) }
before do
new_parent_group.add_maintainer(new_group_member)
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 8e6147e7a3c..5f41ec1d610 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Projects::TransferService do
let_it_be(:user) { create(:user) }
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) }
subject(:execute_transfer) { described_class.new(project, user).execute(group).tap { project.reload } }
@@ -117,6 +118,30 @@ RSpec.describe Projects::TransferService do
shard_name: project.repository_storage
)
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
context 'when transfer fails' do
@@ -527,7 +552,7 @@ RSpec.describe Projects::TransferService do
group.add_owner(user)
end
- it 'schedules a job when pages are deployed' do
+ it 'schedules a job when pages are deployed' do
project.mark_pages_as_deployed
expect(PagesTransferWorker).to receive(:perform_async)