Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-04-28 18:09:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-04-28 18:09:35 +0300
commit42d13aebd3c47671337d871e8b349385dade5252 (patch)
treec15b971738677229f079feab81821611f92ad6c9
parent0805030d634b48c8a44308330fe0d99ba8434f46 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--CHANGELOG.md35
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/batch_comments/components/drafts_count.vue2
-rw-r--r--app/assets/javascripts/batch_comments/components/publish_button.vue2
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_mermaid.js19
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue16
-rw-r--r--app/assets/javascripts/diffs/components/app.vue38
-rw-r--r--app/assets/javascripts/diffs/constants.js17
-rw-r--r--app/assets/javascripts/diffs/store/actions.js53
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue12
-rw-r--r--app/assets/stylesheets/pages/note_form.scss4
-rw-r--r--app/assets/stylesheets/utilities.scss10
-rw-r--r--app/controllers/concerns/sessionless_authentication.rb6
-rw-r--r--app/controllers/graphql_controller.rb8
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/finders/group_members_finder.rb6
-rw-r--r--app/graphql/mutations/base_mutation.rb13
-rw-r--r--app/graphql/types/group_member_relation_enum.rb2
-rw-r--r--app/helpers/preferences_helper.rb18
-rw-r--r--app/models/user.rb2
-rw-r--r--app/policies/global_policy.rb4
-rw-r--r--app/services/auth/dependency_proxy_authentication_service.rb5
-rw-r--r--app/services/issues/base_service.rb4
-rw-r--r--app/services/projects/branches_by_mode_service.rb2
-rw-r--r--app/views/import/bitbucket_server/new.html.haml6
-rw-r--r--app/views/import/fogbugz/new.html.haml6
-rw-r--r--app/views/import/gitea/new.html.haml4
-rw-r--r--app/views/layouts/nav/sidebar/_project_menus.html.haml19
-rw-r--r--app/views/projects/mirrors/_authentication_method.html.haml2
-rw-r--r--app/views/shared/_import_form.html.haml6
-rw-r--r--changelogs/unreleased/gl-form-bitbucket-import.yml5
-rw-r--r--changelogs/unreleased/gl-form-fogbugz-import.yml5
-rw-r--r--changelogs/unreleased/gl-form-gitea-import.yml5
-rw-r--r--changelogs/unreleased/gl-form-import.yml5
-rw-r--r--changelogs/unreleased/justin_ho-update-button-variant-to-confirm-on-merge-request-page.yml5
-rw-r--r--changelogs/unreleased/mermaid-chaining.yml5
-rw-r--r--changelogs/unreleased/ph-327054-mrSettingsTracking.yml5
-rw-r--r--changelogs/unreleased/trans-levels-master-patch-26507.yml5
-rw-r--r--config/feature_flags/development/branch_list_keyset_pagination.yml2
-rw-r--r--config/feature_flags/development/diff_settings_usage_data.yml8
-rw-r--r--config/metrics/aggregates/code_review.yml22
-rw-r--r--config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml19
-rw-r--r--config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml19
-rw-r--r--config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml19
-rw-r--r--config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml19
-rw-r--r--doc/api/graphql/reference/index.md6
-rw-r--r--doc/development/i18n/merging_translations.md7
-rw-r--r--doc/development/testing_guide/best_practices.md4
-rw-r--r--doc/development/testing_guide/flaky_tests.md27
-rw-r--r--doc/development/usage_ping/dictionary.md264
-rw-r--r--doc/user/clusters/agent/repository.md38
-rw-r--r--lib/api/issues.rb3
-rw-r--r--lib/gitlab/auth/scope_validator.rb24
-rw-r--r--lib/gitlab/graphql/authorize/object_authorization.rb19
-rw-r--r--lib/gitlab/i18n.rb56
-rw-r--r--lib/gitlab/pagination/gitaly_keyset_pager.rb4
-rw-r--r--lib/gitlab/usage_data_counters/known_events/code_review_events.yml56
-rw-r--r--lib/sidebars/projects/menus/merge_requests_menu.rb70
-rw-r--r--lib/sidebars/projects/panel.rb1
-rw-r--r--locale/gitlab.pot6
-rw-r--r--qa/qa/page/project/menu.rb3
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb4
-rw-r--r--spec/features/profiles/user_edit_preferences_spec.rb14
-rw-r--r--spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap4
-rw-r--r--spec/helpers/preferences_helper_spec.rb17
-rw-r--r--spec/lib/gitlab/i18n_spec.rb18
-rw-r--r--spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb63
-rw-r--r--spec/requests/api/graphql/mutations/notes/create/note_spec.rb2
-rw-r--r--spec/requests/api/issues/issues_spec.rb28
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb14
-rw-r--r--spec/requests/jwt_controller_spec.rb10
-rw-r--r--spec/services/auth/dependency_proxy_authentication_service_spec.rb25
-rw-r--r--spec/services/projects/download_service_spec.rb5
-rw-r--r--spec/support/helpers/graphql_helpers.rb18
-rw-r--r--spec/support/shared_examples/requests/graphql_shared_examples.rb46
-rw-r--r--spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb14
96 files changed, 1544 insertions, 146 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 096a598100e..f272dd674ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 13.11.2 (2021-04-27)
+
+### Security (5 changes)
+
+- Prevent tokens with only read_api scope from executing mutations.
+- Do not allow deploy tokens in the dependency proxy authentication service.
+- Disable keyset pagination for branches by default.
+- Bump Carrierwave gem to v1.3.2.
+- Restrict setting system_note_timestamp to owners.
+
+
## 13.11.1 (2021-04-22)
### Changed (1 change)
@@ -819,6 +830,18 @@ entry.
- Externalize strings in labels/new.html.haml. (nuwe1)
+## 13.10.4 (2021-04-27)
+
+### Security (6 changes)
+
+- Prevent tokens with only read_api scope from executing mutations.
+- Update mermaid to version 8.9.2.
+- Do not allow deploy tokens in the dependency proxy authentication service.
+- Disable keyset pagination for branches by default.
+- Bump Carrierwave gem to v1.3.2.
+- Restrict setting system_note_timestamp to owners.
+
+
## 13.10.3 (2021-04-13)
### Security (3 changes)
@@ -1388,6 +1411,18 @@ entry.
- Convert mattermost alert to pajamas. !56556
+## 13.9.7 (2021-04-27)
+
+### Security (6 changes)
+
+- Prevent tokens with only read_api scope from executing mutations.
+- Update mermaid to version 8.9.2.
+- Do not allow deploy tokens in the dependency proxy authentication service.
+- Disable keyset pagination for branches by default.
+- Bump Carrierwave gem to v1.3.2.
+- Restrict setting system_note_timestamp to owners.
+
+
## 13.9.6 (2021-04-13)
### Security (2 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index a68349bf139..2d885695fcf 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-67520d7b374eea77f7412600271a90193bdd2e8a
+34b2ac334944beb72e5000d9e90e5b7bdd4778fc
diff --git a/Gemfile.lock b/Gemfile.lock
index 989d812228f..0c2dc260970 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -173,10 +173,11 @@ GEM
capybara-screenshot (1.0.22)
capybara (>= 1.0, < 4)
launchy
- carrierwave (1.3.1)
+ carrierwave (1.3.2)
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
+ ssrf_filter (~> 1.0)
cbor (0.5.9.6)
character_set (1.4.0)
charlock_holmes (0.7.7)
@@ -1222,6 +1223,7 @@ GEM
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
sshkey (2.0.0)
+ ssrf_filter (1.0.7)
stackprof (0.2.15)
state_machines (0.5.0)
state_machines-activemodel (0.8.0)
diff --git a/app/assets/javascripts/batch_comments/components/drafts_count.vue b/app/assets/javascripts/batch_comments/components/drafts_count.vue
index 5e110b101eb..61718b766d8 100644
--- a/app/assets/javascripts/batch_comments/components/drafts_count.vue
+++ b/app/assets/javascripts/batch_comments/components/drafts_count.vue
@@ -12,7 +12,7 @@ export default {
};
</script>
<template>
- <gl-badge size="sm" variant="success">
+ <gl-badge size="sm" variant="info" class="gl-ml-2">
{{ draftsCount }}
<span class="sr-only"> {{ n__('draft', 'drafts', draftsCount) }} </span>
</gl-badge>
diff --git a/app/assets/javascripts/batch_comments/components/publish_button.vue b/app/assets/javascripts/batch_comments/components/publish_button.vue
index 2a7be605003..d4fc4ad744a 100644
--- a/app/assets/javascripts/batch_comments/components/publish_button.vue
+++ b/app/assets/javascripts/batch_comments/components/publish_button.vue
@@ -22,7 +22,7 @@ export default {
variant: {
type: String,
required: false,
- default: 'success',
+ default: 'confirm',
},
},
computed: {
diff --git a/app/assets/javascripts/behaviors/markdown/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
index 5b5148a850b..f5b2d266c18 100644
--- a/app/assets/javascripts/behaviors/markdown/render_mermaid.js
+++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import { once } from 'lodash';
+import { once, countBy } from 'lodash';
import { deprecatedCreateFlash as flash } from '~/flash';
import { darkModeEnabled } from '~/lib/utils/color_utils';
import { __, sprintf } from '~/locale';
@@ -22,6 +22,8 @@ import { __, sprintf } from '~/locale';
const MAX_CHAR_LIMIT = 2000;
// Max # of mermaid blocks that can be rendered in a page.
const MAX_MERMAID_BLOCK_LIMIT = 50;
+// Max # of `&` allowed in Chaining of links syntax
+const MAX_CHAINING_OF_LINKS_LIMIT = 30;
// Keep a map of mermaid blocks we've already rendered.
const elsProcessingMap = new WeakMap();
let renderedMermaidBlocks = 0;
@@ -64,6 +66,18 @@ function importMermaidModule() {
});
}
+function shouldLazyLoadMermaidBlock(source) {
+ /**
+ * If source contains `&`, which means that it might
+ * contain Chaining of links a new syntax in Mermaid.
+ */
+ if (countBy(source)['&'] > MAX_CHAINING_OF_LINKS_LIMIT) {
+ return true;
+ }
+
+ return false;
+}
+
function fixElementSource(el) {
// Mermaid doesn't like `<br />` tags, so collapse all like tags into `<br>`, which is parsed correctly.
const source = el.textContent.replace(/<br\s*\/>/g, '<br>');
@@ -128,7 +142,8 @@ function renderMermaids($els) {
if (
(source && source.length > MAX_CHAR_LIMIT) ||
renderedChars > MAX_CHAR_LIMIT ||
- renderedMermaidBlocks >= MAX_MERMAID_BLOCK_LIMIT
+ renderedMermaidBlocks >= MAX_MERMAID_BLOCK_LIMIT ||
+ shouldLazyLoadMermaidBlock(source)
) {
const html = `
<div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert">
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue b/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue
index fb25d3618ab..336ce714a05 100644
--- a/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue
+++ b/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue
@@ -115,12 +115,13 @@ export default {
</template>
</markdown-field>
<slot name="resolve-checkbox"></slot>
- <div class="note-form-actions gl-display-flex gl-justify-content-space-between">
+ <div class="note-form-actions gl-display-flex">
<gl-button
ref="submitButton"
:disabled="!hasValue || isSaving"
+ class="gl-mr-3 gl-w-auto!"
category="primary"
- variant="success"
+ variant="confirm"
type="submit"
data-track-event="click_button"
data-qa-selector="save_comment_button"
@@ -128,9 +129,14 @@ export default {
>
{{ buttonText }}
</gl-button>
- <gl-button ref="cancelButton" variant="default" category="primary" @click="cancelComment">{{
- __('Cancel')
- }}</gl-button>
+ <gl-button
+ ref="cancelButton"
+ class="gl-w-auto!"
+ variant="default"
+ category="primary"
+ @click="cancelComment"
+ >{{ __('Cancel') }}</gl-button
+ >
</div>
<gl-modal
ref="cancelCommentModal"
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 7c610968209..66157dfeaba 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -3,6 +3,7 @@ import { GlLoadingIcon, GlPagination, GlSprintf } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import Mousetrap from 'mousetrap';
import { mapState, mapGetters, mapActions } from 'vuex';
+import api from '~/api';
import {
keysFor,
MR_PREVIOUS_FILE_IN_DIFF,
@@ -30,6 +31,15 @@ import {
ALERT_OVERFLOW_HIDDEN,
ALERT_MERGE_CONFLICT,
ALERT_COLLAPSED_FILES,
+ INLINE_DIFF_VIEW_TYPE,
+ TRACKING_DIFF_VIEW_INLINE,
+ TRACKING_DIFF_VIEW_PARALLEL,
+ TRACKING_FILE_BROWSER_TREE,
+ TRACKING_FILE_BROWSER_LIST,
+ TRACKING_WHITESPACE_SHOW,
+ TRACKING_WHITESPACE_HIDE,
+ TRACKING_SINGLE_FILE_MODE,
+ TRACKING_MULTIPLE_FILES_MODE,
} from '../constants';
import { reviewStatuses } from '../utils/file_reviews';
@@ -183,6 +193,8 @@ export default {
'hasConflicts',
'viewDiffsFileByFile',
'mrReviews',
+ 'renderTreeList',
+ 'showWhitespace',
]),
...mapGetters('diffs', ['whichCollapsedTypes', 'isParallelView', 'currentDiffIndex']),
...mapGetters('batchComments', ['draftsCount']),
@@ -305,6 +317,32 @@ export default {
if (id && id.indexOf('#note') !== 0) {
this.setHighlightedRow(id.split('diff-content').pop().slice(1));
}
+
+ if (window.gon?.features?.diffSettingsUsageData) {
+ if (this.renderTreeList) {
+ api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_TREE);
+ } else {
+ api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_LIST);
+ }
+
+ if (this.diffViewType === INLINE_DIFF_VIEW_TYPE) {
+ api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_INLINE);
+ } else {
+ api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_PARALLEL);
+ }
+
+ if (this.showWhitespace) {
+ api.trackRedisHllUserEvent(TRACKING_WHITESPACE_SHOW);
+ } else {
+ api.trackRedisHllUserEvent(TRACKING_WHITESPACE_HIDE);
+ }
+
+ if (this.viewDiffsFileByFile) {
+ api.trackRedisHllUserEvent(TRACKING_SINGLE_FILE_MODE);
+ } else {
+ api.trackRedisHllUserEvent(TRACKING_MULTIPLE_FILES_MODE);
+ }
+ }
},
beforeCreate() {
diffsApp.instrument();
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 0163f508fea..f0e15983336 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -114,3 +114,20 @@ export const CONFLICT_THEIR = 'conflict_their';
export const CONFLICT_MARKER = 'conflict_marker';
export const CONFLICT_MARKER_OUR = 'conflict_marker_our';
export const CONFLICT_MARKER_THEIR = 'conflict_marker_their';
+
+// Tracking events
+export const TRACKING_CLICK_DIFF_VIEW_SETTING = 'i_code_review_click_diff_view_setting';
+export const TRACKING_DIFF_VIEW_INLINE = 'i_code_review_diff_view_inline';
+export const TRACKING_DIFF_VIEW_PARALLEL = 'i_code_review_diff_view_parallel';
+
+export const TRACKING_CLICK_FILE_BROWSER_SETTING = 'i_code_review_click_file_browser_setting';
+export const TRACKING_FILE_BROWSER_TREE = 'i_code_review_file_browser_tree_view';
+export const TRACKING_FILE_BROWSER_LIST = 'i_code_review_file_browser_list_view';
+
+export const TRACKING_CLICK_WHITESPACE_SETTING = 'i_code_review_click_whitespace_setting';
+export const TRACKING_WHITESPACE_SHOW = 'i_code_review_diff_show_whitespace';
+export const TRACKING_WHITESPACE_HIDE = 'i_code_review_diff_hide_whitespace';
+
+export const TRACKING_CLICK_SINGLE_FILE_SETTING = 'i_code_review_click_single_file_mode_setting';
+export const TRACKING_SINGLE_FILE_MODE = 'i_code_review_diff_single_file';
+export const TRACKING_MULTIPLE_FILES_MODE = 'i_code_review_diff_multiple_files';
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 428faf693b0..d0730e18228 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -1,5 +1,6 @@
import Cookies from 'js-cookie';
import Vue from 'vue';
+import api from '~/api';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { diffViewerModes } from '~/ide/constants';
import axios from '~/lib/utils/axios_utils';
@@ -36,6 +37,18 @@ import {
DIFF_VIEW_FILE_BY_FILE,
DIFF_VIEW_ALL_FILES,
DIFF_FILE_BY_FILE_COOKIE_NAME,
+ TRACKING_CLICK_DIFF_VIEW_SETTING,
+ TRACKING_DIFF_VIEW_INLINE,
+ TRACKING_DIFF_VIEW_PARALLEL,
+ TRACKING_CLICK_FILE_BROWSER_SETTING,
+ TRACKING_FILE_BROWSER_TREE,
+ TRACKING_FILE_BROWSER_LIST,
+ TRACKING_CLICK_WHITESPACE_SETTING,
+ TRACKING_WHITESPACE_SHOW,
+ TRACKING_WHITESPACE_HIDE,
+ TRACKING_CLICK_SINGLE_FILE_SETTING,
+ TRACKING_SINGLE_FILE_MODE,
+ TRACKING_MULTIPLE_FILES_MODE,
} from '../constants';
import eventHub from '../event_hub';
import { isCollapsed } from '../utils/diff_file';
@@ -352,6 +365,11 @@ export const setInlineDiffViewType = ({ commit }) => {
Cookies.set(DIFF_VIEW_COOKIE_NAME, INLINE_DIFF_VIEW_TYPE);
const url = mergeUrlParams({ view: INLINE_DIFF_VIEW_TYPE }, window.location.href);
historyPushState(url);
+
+ if (window.gon?.features?.diffSettingsUsageData) {
+ api.trackRedisHllUserEvent(TRACKING_CLICK_DIFF_VIEW_SETTING);
+ api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_INLINE);
+ }
};
export const setParallelDiffViewType = ({ commit }) => {
@@ -360,6 +378,11 @@ export const setParallelDiffViewType = ({ commit }) => {
Cookies.set(DIFF_VIEW_COOKIE_NAME, PARALLEL_DIFF_VIEW_TYPE);
const url = mergeUrlParams({ view: PARALLEL_DIFF_VIEW_TYPE }, window.location.href);
historyPushState(url);
+
+ if (window.gon?.features?.diffSettingsUsageData) {
+ api.trackRedisHllUserEvent(TRACKING_CLICK_DIFF_VIEW_SETTING);
+ api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_PARALLEL);
+ }
};
export const showCommentForm = ({ commit }, { lineCode, fileHash }) => {
@@ -527,6 +550,16 @@ export const setRenderTreeList = ({ commit }, renderTreeList) => {
commit(types.SET_RENDER_TREE_LIST, renderTreeList);
localStorage.setItem(TREE_LIST_STORAGE_KEY, renderTreeList);
+
+ if (window.gon?.features?.diffSettingsUsageData) {
+ api.trackRedisHllUserEvent(TRACKING_CLICK_FILE_BROWSER_SETTING);
+
+ if (renderTreeList) {
+ api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_TREE);
+ } else {
+ api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_LIST);
+ }
+ }
};
export const setShowWhitespace = ({ commit }, { showWhitespace, pushState = false }) => {
@@ -540,6 +573,16 @@ export const setShowWhitespace = ({ commit }, { showWhitespace, pushState = fals
}
notesEventHub.$emit('refetchDiffData');
+
+ if (window.gon?.features?.diffSettingsUsageData) {
+ api.trackRedisHllUserEvent(TRACKING_CLICK_WHITESPACE_SETTING);
+
+ if (showWhitespace) {
+ api.trackRedisHllUserEvent(TRACKING_WHITESPACE_SHOW);
+ } else {
+ api.trackRedisHllUserEvent(TRACKING_WHITESPACE_HIDE);
+ }
+ }
};
export const toggleFileFinder = ({ commit }, visible) => {
@@ -754,6 +797,16 @@ export const setFileByFile = ({ state, commit }, { fileByFile }) => {
commit(types.SET_FILE_BY_FILE, fileByFile);
Cookies.set(DIFF_FILE_BY_FILE_COOKIE_NAME, fileViewMode);
+ if (window.gon?.features?.diffSettingsUsageData) {
+ api.trackRedisHllUserEvent(TRACKING_CLICK_SINGLE_FILE_SETTING);
+
+ if (fileByFile) {
+ api.trackRedisHllUserEvent(TRACKING_SINGLE_FILE_MODE);
+ } else {
+ api.trackRedisHllUserEvent(TRACKING_MULTIPLE_FILES_MODE);
+ }
+ }
+
return axios
.put(state.endpointUpdateUser, {
view_diffs_file_by_file: fileByFile,
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index a70bac94b71..5b40f4f86f5 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -362,7 +362,7 @@ export default {
</template>
</markdown-field>
</comment-field-layout>
- <div class="note-form-actions clearfix">
+ <div class="note-form-actions">
<template v-if="showBatchCommentsActions">
<p v-if="showResolveDiscussionToggle">
<label>
@@ -386,12 +386,12 @@ export default {
</template>
</label>
</p>
- <div class="gl-display-sm-flex gl-flex-wrap">
+ <div class="gl-display-flex gl-flex-wrap gl-mb-n3">
<gl-button
:disabled="isDisabled"
category="primary"
variant="confirm"
- class="gl-mr-3"
+ class="gl-sm-mr-3 gl-mb-3"
data-qa-selector="start_review_button"
@click="handleAddToReview"
>
@@ -401,15 +401,15 @@ export default {
<gl-button
:disabled="isDisabled"
category="secondary"
- variant="default"
+ variant="confirm"
data-qa-selector="comment_now_button"
- class="gl-mr-3 js-comment-button"
+ class="gl-sm-mr-3 gl-mb-3 js-comment-button"
@click="handleUpdate()"
>
{{ __('Add comment now') }}
</gl-button>
<gl-button
- class="note-edit-cancel js-close-discussion-note-form"
+ class="note-edit-cancel gl-mb-3 js-close-discussion-note-form"
category="secondary"
variant="default"
data-testid="cancelBatchCommentsEnabled"
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 59768f4cda8..c025d8569a7 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -369,10 +369,6 @@ table {
.btn {
float: none;
width: 100%;
-
- &:not(:last-child) {
- margin-bottom: 10px;
- }
}
}
}
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 024162eba3e..f9e9a7a99b7 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -172,3 +172,13 @@
width: 50%;
}
}
+
+.gl-sm-mr-3 {
+ @include media-breakpoint-up(sm) {
+ margin-right: $gl-spacing-scale-3;
+ }
+}
+
+.gl-mb-n3 {
+ margin-bottom: -$gl-spacing-scale-3;
+}
diff --git a/app/controllers/concerns/sessionless_authentication.rb b/app/controllers/concerns/sessionless_authentication.rb
index 882fef7a342..3c8a683439a 100644
--- a/app/controllers/concerns/sessionless_authentication.rb
+++ b/app/controllers/concerns/sessionless_authentication.rb
@@ -7,11 +7,15 @@
module SessionlessAuthentication
# This filter handles personal access tokens, atom requests with rss tokens, and static object tokens
def authenticate_sessionless_user!(request_format)
- user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format)
+ user = request_authenticator.find_sessionless_user(request_format)
sessionless_sign_in(user) if user
end
+ def request_authenticator
+ @request_authenticator ||= Gitlab::Auth::RequestAuthenticator.new(request)
+ end
+
def sessionless_user?
current_user && !session.key?('warden.user.user.key')
end
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index b7daff04373..725d8b62c77 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -112,7 +112,13 @@ class GraphqlController < ApplicationController
# When modifying the context, also update GraphqlChannel#context if needed
# so that we have similar context when executing queries, mutations, and subscriptions
def context
- @context ||= { current_user: current_user, is_sessionless_user: !!sessionless_user?, request: request }
+ api_user = !!sessionless_user?
+ @context ||= {
+ current_user: current_user,
+ is_sessionless_user: api_user,
+ request: request,
+ scope_validator: ::Gitlab::Auth::ScopeValidator.new(api_user, request_authenticator)
+ }
end
def build_variables(variable_info)
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index f522dffdf3e..5006aa75ce5 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -185,7 +185,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Here we get one more branch to indicate if there are more data we're not showing
limit = @overview_max_branches + 1
- if Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true)
+ if Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: :yaml)
@active_branches =
BranchesFinder.new(@repository, { per_page: limit, sort: sort_value_recently_updated })
.execute(gitaly_pagination: true).select(&:active?)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 4e409b5f28f..a31437288b9 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -45,6 +45,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
# Usage data feature flags
push_frontend_feature_flag(:users_expanding_widgets_usage_data, @project, default_enabled: :yaml)
+ push_frontend_feature_flag(:diff_settings_usage_data, default_enabled: :yaml)
record_experiment_user(:invite_members_version_b)
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index a6ecd835527..430ff212977 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -4,6 +4,12 @@ class GroupMembersFinder < UnionFinder
RELATIONS = %i(direct inherited descendants).freeze
DEFAULT_RELATIONS = %i(direct inherited).freeze
+ RELATIONS_DESCRIPTIONS = {
+ direct: 'Members in the group itself',
+ inherited: "Members in the group's ancestor groups",
+ descendants: "Members in the group's subgroups"
+ }.freeze
+
include CreatedAtFilter
# Params can be any of the following:
diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb
index 1f18a37fcb9..da658e1f108 100644
--- a/app/graphql/mutations/base_mutation.rb
+++ b/app/graphql/mutations/base_mutation.rb
@@ -44,9 +44,18 @@ module Mutations
end
end
+ def self.authorizes_object?
+ true
+ end
+
def self.authorized?(object, context)
- # we never provide an object to mutations, but we do need to have a user.
- context[:current_user].present? && !context[:current_user].blocked?
+ auth = ::Gitlab::Graphql::Authorize::ObjectAuthorization.new(:execute_graphql_mutation, :api)
+
+ return true if auth.ok?(:global, context[:current_user],
+ scope_validator: context[:scope_validator])
+
+ # in our mutations we raise, rather than returning a null value.
+ raise_resource_not_available_error!
end
# See: AuthorizeResource#authorized_resource?
diff --git a/app/graphql/types/group_member_relation_enum.rb b/app/graphql/types/group_member_relation_enum.rb
index aa2e73d4944..ce22410a249 100644
--- a/app/graphql/types/group_member_relation_enum.rb
+++ b/app/graphql/types/group_member_relation_enum.rb
@@ -6,7 +6,7 @@ module Types
description 'Group member relation'
::GroupMembersFinder::RELATIONS.each do |member_relation|
- value member_relation.to_s.upcase, value: member_relation, description: "#{member_relation.to_s.titleize} members"
+ value member_relation.to_s.upcase, value: member_relation, description: ::GroupMembersFinder::RELATIONS_DESCRIPTIONS[member_relation]
end
end
end
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index add6e1eaf6f..6fc2dfb597b 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -4,8 +4,8 @@
module PreferencesHelper
def layout_choices
[
- ['Fixed', :fixed],
- ['Fluid', :fluid]
+ ['Fixed', :fixed],
+ ['Fluid', :fluid]
]
end
@@ -76,7 +76,7 @@ module PreferencesHelper
def language_choices
options_for_select(
- Gitlab::I18n.selectable_locales.map(&:reverse).sort,
+ selectable_locales_with_translation_level.sort,
current_user.preferred_language
)
end
@@ -107,6 +107,18 @@ module PreferencesHelper
def default_first_day_of_week
first_day_of_week_choices.rassoc(Gitlab::CurrentSettings.first_day_of_week).first
end
+
+ def selectable_locales_with_translation_level
+ Gitlab::I18n.selectable_locales.map do |code, language|
+ [
+ s_("i18n|%{language} (%{percent_translated}%% translated)") % {
+ language: language,
+ percent_translated: Gitlab::I18n.percentage_translated_for(code)
+ },
+ code
+ ]
+ end
+ end
end
PreferencesHelper.prepend_if_ee('EE::PreferencesHelper')
diff --git a/app/models/user.rb b/app/models/user.rb
index 92cbf863d63..2a1a910effd 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -431,7 +431,7 @@ class User < ApplicationRecord
def preferred_language
read_attribute('preferred_language') ||
- I18n.default_locale.to_s.presence_in(Gitlab::I18n::AVAILABLE_LANGUAGES.keys) ||
+ I18n.default_locale.to_s.presence_in(Gitlab::I18n.available_locales) ||
'en'
end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 5ee34ebbb2f..d16c4734b2c 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -23,6 +23,7 @@ class GlobalPolicy < BasePolicy
prevent :receive_notifications
prevent :use_quick_actions
prevent :create_group
+ prevent :execute_graphql_mutation
end
rule { default }.policy do
@@ -32,6 +33,7 @@ class GlobalPolicy < BasePolicy
enable :receive_notifications
enable :use_quick_actions
enable :use_slash_commands
+ enable :execute_graphql_mutation
end
rule { inactive }.policy do
@@ -48,6 +50,8 @@ class GlobalPolicy < BasePolicy
prevent :use_slash_commands
end
+ rule { ~can?(:access_api) }.prevent :execute_graphql_mutation
+
rule { blocked | (internal & ~migration_bot & ~security_bot) }.policy do
prevent :access_git
end
diff --git a/app/services/auth/dependency_proxy_authentication_service.rb b/app/services/auth/dependency_proxy_authentication_service.rb
index 1b8c16b7c79..fab42e0ebb6 100644
--- a/app/services/auth/dependency_proxy_authentication_service.rb
+++ b/app/services/auth/dependency_proxy_authentication_service.rb
@@ -8,7 +8,10 @@ module Auth
def execute(authentication_abilities:)
return error('dependency proxy not enabled', 404) unless ::Gitlab.config.dependency_proxy.enabled
- return error('access forbidden', 403) unless current_user
+
+ # Because app/controllers/concerns/dependency_proxy/auth.rb consumes this
+ # JWT only as `User.find`, we currently only allow User (not DeployToken, etc)
+ return error('access forbidden', 403) unless current_user.is_a?(User)
{ token: authorized_token.encoded }
end
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 87615d1b4f2..07e4a10708e 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -34,7 +34,7 @@ module Issues
private
- def filter_params(merge_request)
+ def filter_params(issue)
super
moved_issue = params.delete(:moved_issue)
@@ -44,6 +44,8 @@ module Issues
params.delete(:iid) unless current_user.can?(:set_issue_iid, project)
params.delete(:created_at) unless moved_issue || current_user.can?(:set_issue_created_at, project)
params.delete(:updated_at) unless moved_issue || current_user.can?(:set_issue_updated_at, project)
+
+ issue.system_note_timestamp = params[:created_at] || params[:updated_at]
end
def create_assignee_note(issue, old_assignees)
diff --git a/app/services/projects/branches_by_mode_service.rb b/app/services/projects/branches_by_mode_service.rb
index dbdcef066f4..090671cc79a 100644
--- a/app/services/projects/branches_by_mode_service.rb
+++ b/app/services/projects/branches_by_mode_service.rb
@@ -37,7 +37,7 @@ class Projects::BranchesByModeService
def use_gitaly_pagination?
return false if params[:page].present? || params[:search].present?
- Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true)
+ Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: :yaml)
end
def fetch_branches_via_offset_pagination
diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml
index 308065da90a..8a3fe1a816c 100644
--- a/app/views/import/bitbucket_server/new.html.haml
+++ b/app/views/import/bitbucket_server/new.html.haml
@@ -15,14 +15,14 @@
.form-group.row
= label_tag :bitbucket_server_url, 'Bitbucket Server URL', class: 'col-form-label col-md-2'
.col-md-4
- = text_field_tag :bitbucket_server_url, '', class: 'form-control gl-mr-3', placeholder: _('https://your-bitbucket-server'), size: 40
+ = text_field_tag :bitbucket_server_url, '', class: 'form-control gl-form-input gl-mr-3', placeholder: _('https://your-bitbucket-server'), size: 40
.form-group.row
= label_tag :bitbucket_server_url, 'Username', class: 'col-form-label col-md-2'
.col-md-4
- = text_field_tag :bitbucket_server_username, '', class: 'form-control gl-mr-3', placeholder: _('username'), size: 40
+ = text_field_tag :bitbucket_server_username, '', class: 'form-control gl-form-input gl-mr-3', placeholder: _('username'), size: 40
.form-group.row
= label_tag :personal_access_token, 'Password/Personal Access Token', class: 'col-form-label col-md-2'
.col-md-4
- = password_field_tag :personal_access_token, '', class: 'form-control gl-mr-3', placeholder: _('Personal Access Token'), size: 40
+ = password_field_tag :personal_access_token, '', class: 'form-control gl-form-input gl-mr-3', placeholder: _('Personal Access Token'), size: 40
.form-actions
= submit_tag _('List your Bitbucket Server repositories'), class: 'gl-button btn btn-confirm'
diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml
index c0abac0a633..ab836174024 100644
--- a/app/views/import/fogbugz/new.html.haml
+++ b/app/views/import/fogbugz/new.html.haml
@@ -12,14 +12,14 @@
.form-group.row
= label_tag :uri, _('FogBugz URL'), class: 'col-form-label col-md-2'
.col-md-4
- = text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control'
+ = text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control gl-form-input'
.form-group.row
= label_tag :email, _('FogBugz Email'), class: 'col-form-label col-md-2'
.col-md-4
- = text_field_tag :email, nil, class: 'form-control'
+ = text_field_tag :email, nil, class: 'form-control gl-form-input'
.form-group.row
= label_tag :password, _('FogBugz Password'), class: 'col-form-label col-md-2'
.col-md-4
- = password_field_tag :password, nil, class: 'form-control'
+ = password_field_tag :password, nil, class: 'form-control gl-form-input'
.form-actions
= submit_tag _('Continue to the next step'), class: 'gl-button btn btn-confirm'
diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml
index 285d2fb23a3..27786806d17 100644
--- a/app/views/import/gitea/new.html.haml
+++ b/app/views/import/gitea/new.html.haml
@@ -13,10 +13,10 @@
.form-group.row
= label_tag :gitea_host_url, _('Gitea Host URL'), class: 'col-form-label col-sm-2'
.col-sm-4
- = text_field_tag :gitea_host_url, nil, placeholder: 'https://gitea.com', class: 'form-control'
+ = text_field_tag :gitea_host_url, nil, placeholder: 'https://gitea.com', class: 'form-control gl-form-input'
.form-group.row
= label_tag :personal_access_token, _('Personal Access Token'), class: 'col-form-label col-sm-2'
.col-sm-4
- = text_field_tag :personal_access_token, nil, class: 'form-control'
+ = text_field_tag :personal_access_token, nil, class: 'form-control gl-form-input'
.form-actions
= submit_tag _('List Your Gitea Repositories'), class: 'gl-button btn btn-confirm'
diff --git a/app/views/layouts/nav/sidebar/_project_menus.html.haml b/app/views/layouts/nav/sidebar/_project_menus.html.haml
index 0507f6d4b16..631fc3f095d 100644
--- a/app/views/layouts/nav/sidebar/_project_menus.html.haml
+++ b/app/views/layouts/nav/sidebar/_project_menus.html.haml
@@ -1,22 +1,3 @@
-- if project_nav_tab? :merge_requests
- = nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :milestones]) do
- = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests', data: { qa_selector: 'merge_requests_link' } do
- .nav-icon-container
- = sprite_icon('git-merge')
- %span.nav-item-name#js-onboarding-mr-link
- = _('Merge requests')
- %span.badge.badge-pill.count.merge_counter.js-merge-counter
- = number_with_delimiter(@project.open_merge_requests_count)
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(controller: :merge_requests, html_options: { class: "fly-out-top-item" } ) do
- = link_to project_merge_requests_path(@project) do
- %strong.fly-out-top-item-name
- = _('Merge requests')
- %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge
- = number_with_delimiter(@project.open_merge_requests_count)
-
-= render_if_exists "layouts/nav/requirements_link", project: @project
-
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts, :test_cases, :pipeline_editor], unless: -> { current_path?('projects/pipelines#charts') }) do
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines rspec-link-pipelines', data: { qa_selector: 'ci_cd_link' } do
diff --git a/app/views/projects/mirrors/_authentication_method.html.haml b/app/views/projects/mirrors/_authentication_method.html.haml
index 94f8703657b..5f31ec4087e 100644
--- a/app/views/projects/mirrors/_authentication_method.html.haml
+++ b/app/views/projects/mirrors/_authentication_method.html.haml
@@ -13,4 +13,4 @@
.form-group
.well-password-auth.collapse.js-well-password-auth
= f.label :password, _("Password"), class: "label-bold"
- = f.password_field :password, value: mirror.password, class: 'form-control gl-form-input qa-password', autocomplete: 'new-password'
+ = f.password_field :password, class: 'form-control gl-form-input qa-password', autocomplete: 'new-password'
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index 36d8aab6d53..65e02341936 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -7,20 +7,20 @@
%span
= _('Git repository URL')
= f.text_field :import_url, value: import_url.sanitized_url,
- autocomplete: 'off', class: 'form-control', placeholder: 'https://gitlab.company.com/group/project.git', required: true
+ autocomplete: 'off', class: 'form-control gl-form-input', placeholder: 'https://gitlab.company.com/group/project.git', required: true
.row
.form-group.col-md-6
= f.label :import_url_user, class: 'label-bold' do
%span
= _('Username (optional)')
- = f.text_field :import_url_user, value: import_url.user, class: 'form-control', required: false, autocomplete: 'new-password'
+ = f.text_field :import_url_user, value: import_url.user, class: 'form-control gl-form-input', required: false, autocomplete: 'new-password'
.form-group.col-md-6
= f.label :import_url_password, class: 'label-bold' do
%span
= _('Password (optional)')
- = f.password_field :import_url_password, class: 'form-control', required: false, autocomplete: 'new-password'
+ = f.password_field :import_url_password, class: 'form-control gl-form-input', required: false, autocomplete: 'new-password'
.info-well.prepend-top-20
.well-segment
diff --git a/changelogs/unreleased/gl-form-bitbucket-import.yml b/changelogs/unreleased/gl-form-bitbucket-import.yml
new file mode 100644
index 00000000000..e0058c058f8
--- /dev/null
+++ b/changelogs/unreleased/gl-form-bitbucket-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add gl-form-input class for fields in bitbucket import page
+merge_request: 58309
+author: Yogi (@yo)
+type: changed
diff --git a/changelogs/unreleased/gl-form-fogbugz-import.yml b/changelogs/unreleased/gl-form-fogbugz-import.yml
new file mode 100644
index 00000000000..69d0f277a24
--- /dev/null
+++ b/changelogs/unreleased/gl-form-fogbugz-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add gl-form-input class for fields in fogbugz import page
+merge_request: 58312
+author: Yogi (@yo)
+type: changed
diff --git a/changelogs/unreleased/gl-form-gitea-import.yml b/changelogs/unreleased/gl-form-gitea-import.yml
new file mode 100644
index 00000000000..b66aa41a8f9
--- /dev/null
+++ b/changelogs/unreleased/gl-form-gitea-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add gl-form-input class for fields in gitea import page
+merge_request: 58313
+author: Yogi (@yo)
+type: changed
diff --git a/changelogs/unreleased/gl-form-import.yml b/changelogs/unreleased/gl-form-import.yml
new file mode 100644
index 00000000000..988ef53c4d4
--- /dev/null
+++ b/changelogs/unreleased/gl-form-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add gl-form-input class for fields in import page
+merge_request: 58316
+author: Yogi (@yo)
+type: changed
diff --git a/changelogs/unreleased/justin_ho-update-button-variant-to-confirm-on-merge-request-page.yml b/changelogs/unreleased/justin_ho-update-button-variant-to-confirm-on-merge-request-page.yml
new file mode 100644
index 00000000000..d9ba6b8124a
--- /dev/null
+++ b/changelogs/unreleased/justin_ho-update-button-variant-to-confirm-on-merge-request-page.yml
@@ -0,0 +1,5 @@
+---
+title: Update button variants to btn-confirm on MR page
+merge_request: 60254
+author:
+type: changed
diff --git a/changelogs/unreleased/mermaid-chaining.yml b/changelogs/unreleased/mermaid-chaining.yml
new file mode 100644
index 00000000000..589aed8a506
--- /dev/null
+++ b/changelogs/unreleased/mermaid-chaining.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent DOS from Chaining in Mermaid
+merge_request: 60382
+author:
+type: security
diff --git a/changelogs/unreleased/ph-327054-mrSettingsTracking.yml b/changelogs/unreleased/ph-327054-mrSettingsTracking.yml
new file mode 100644
index 00000000000..a3cc1459680
--- /dev/null
+++ b/changelogs/unreleased/ph-327054-mrSettingsTracking.yml
@@ -0,0 +1,5 @@
+---
+title: Added tracking to diff view settings
+merge_request: 59979
+author:
+type: added
diff --git a/changelogs/unreleased/trans-levels-master-patch-26507.yml b/changelogs/unreleased/trans-levels-master-patch-26507.yml
new file mode 100644
index 00000000000..f56e33ed39d
--- /dev/null
+++ b/changelogs/unreleased/trans-levels-master-patch-26507.yml
@@ -0,0 +1,5 @@
+---
+title: Add the translation level for each language in the user profile language selector
+merge_request: 59420
+author:
+type: changed
diff --git a/config/feature_flags/development/branch_list_keyset_pagination.yml b/config/feature_flags/development/branch_list_keyset_pagination.yml
index 23b573e5004..12200292058 100644
--- a/config/feature_flags/development/branch_list_keyset_pagination.yml
+++ b/config/feature_flags/development/branch_list_keyset_pagination.yml
@@ -5,4 +5,4 @@ rollout_issue_url:
milestone: '13.2'
type: development
group: group::source code
-default_enabled: true
+default_enabled: false
diff --git a/config/feature_flags/development/diff_settings_usage_data.yml b/config/feature_flags/development/diff_settings_usage_data.yml
new file mode 100644
index 00000000000..e58552b51c2
--- /dev/null
+++ b/config/feature_flags/development/diff_settings_usage_data.yml
@@ -0,0 +1,8 @@
+---
+name: diff_settings_usage_data
+introduced_by_url:
+rollout_issue_url:
+milestone: '13.11'
+type: development
+group: group::code review
+default_enabled: true
diff --git a/config/metrics/aggregates/code_review.yml b/config/metrics/aggregates/code_review.yml
index a2e931bd217..e1f30777612 100644
--- a/config/metrics/aggregates/code_review.yml
+++ b/config/metrics/aggregates/code_review.yml
@@ -52,6 +52,17 @@
- 'i_code_review_user_reviewers_changed'
- 'i_code_review_user_milestone_changed'
- 'i_code_review_user_labels_changed'
+ - 'i_code_review_click_single_file_mode_setting'
+ - 'i_code_review_click_file_browser_setting'
+ - 'i_code_review_click_whitespace_setting'
+ - 'i_code_review_diff_view_inline'
+ - 'i_code_review_diff_view_parallel'
+ - 'i_code_review_file_browser_tree_view'
+ - 'i_code_review_file_browser_list_view'
+ - 'i_code_review_diff_show_whitespace'
+ - 'i_code_review_diff_hide_whitespace'
+ - 'i_code_review_diff_single_file'
+ - 'i_code_review_diff_multiple_files'
- name: code_review_category_monthly_active_users
operator: OR
feature_flag: usage_data_code_review_aggregation
@@ -96,6 +107,17 @@
- 'i_code_review_user_reviewers_changed'
- 'i_code_review_user_milestone_changed'
- 'i_code_review_user_labels_changed'
+ - 'i_code_review_click_single_file_mode_setting'
+ - 'i_code_review_click_file_browser_setting'
+ - 'i_code_review_click_whitespace_setting'
+ - 'i_code_review_diff_view_inline'
+ - 'i_code_review_diff_view_parallel'
+ - 'i_code_review_file_browser_tree_view'
+ - 'i_code_review_file_browser_list_view'
+ - 'i_code_review_diff_show_whitespace'
+ - 'i_code_review_diff_hide_whitespace'
+ - 'i_code_review_diff_single_file'
+ - 'i_code_review_diff_multiple_files'
- name: code_review_extension_category_monthly_active_users
operator: OR
feature_flag: usage_data_code_review_aggregation
diff --git a/config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml b/config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml
new file mode 100644
index 00000000000..2bcd2a448ec
--- /dev/null
+++ b/config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_click_single_file_mode_setting_monthly
+description: Count of users clicking single file mode setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml b/config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml
new file mode 100644
index 00000000000..3b7c5be9b84
--- /dev/null
+++ b/config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_click_file_browser_setting_monthly
+description: Count of users clicking merge request file browser setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml b/config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml
new file mode 100644
index 00000000000..31fe497fe0e
--- /dev/null
+++ b/config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_click_whitespace_setting_monthly
+description: Count of users clicking merge request whitespae setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml b/config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml
new file mode 100644
index 00000000000..821f7244209
--- /dev/null
+++ b/config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_view_inline_monthly
+description: Count of users with merge request view type as inline
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml b/config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml
new file mode 100644
index 00000000000..04ff06143af
--- /dev/null
+++ b/config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_view_parallel_monthly
+description: Count of users with merge request view type as parallel
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml b/config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml
new file mode 100644
index 00000000000..ad5b5e835c1
--- /dev/null
+++ b/config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_file_browser_tree_view_monthly
+description: Count of users with merge request file tree setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml b/config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml
new file mode 100644
index 00000000000..c71bacd6004
--- /dev/null
+++ b/config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_file_browser_list_view_monthly
+description: Count of users with merge request file list setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml b/config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml
new file mode 100644
index 00000000000..0d17fa0d965
--- /dev/null
+++ b/config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_show_whitespace_monthly
+description: Count of users with show whitespace enabled
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml b/config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml
new file mode 100644
index 00000000000..6038bb5cda8
--- /dev/null
+++ b/config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_hide_whitespace_monthly
+description: Count of users with show whitespace disabled
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml b/config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml
new file mode 100644
index 00000000000..3cfcdf23a47
--- /dev/null
+++ b/config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_single_file_monthly
+description: Count of users with single file mode enabled
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml b/config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml
new file mode 100644
index 00000000000..16784a0c408
--- /dev/null
+++ b/config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_multiple_files_monthly
+description: Count of users with single mode disabled
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 28d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml b/config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml
new file mode 100644
index 00000000000..179d2fa6e47
--- /dev/null
+++ b/config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_click_single_file_mode_setting_weekly
+description: Count of users clicking single file mode setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml b/config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml
new file mode 100644
index 00000000000..50264bf27cf
--- /dev/null
+++ b/config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_click_file_browser_setting_weekly
+description: Count of users with merge request file list setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml b/config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml
new file mode 100644
index 00000000000..3206bac8b49
--- /dev/null
+++ b/config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_click_whitespace_setting_weekly
+description: Count of users clicking merge request whitespae setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml b/config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml
new file mode 100644
index 00000000000..7a3a606c886
--- /dev/null
+++ b/config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_view_inline_weekly
+description: Count of users with merge request view type as inline
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml b/config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml
new file mode 100644
index 00000000000..36dbeae82e7
--- /dev/null
+++ b/config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_view_parallel_weekly
+description: Count of users with merge request view type as parallel
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml b/config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml
new file mode 100644
index 00000000000..ef74ff6613f
--- /dev/null
+++ b/config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_file_browser_tree_view_weekly
+description: Count of users with merge request file tree setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml b/config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml
new file mode 100644
index 00000000000..e2b9f141bb7
--- /dev/null
+++ b/config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_file_browser_list_view_weekly
+description: Count of users with merge request file list setting
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml b/config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml
new file mode 100644
index 00000000000..6cca3840faa
--- /dev/null
+++ b/config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_show_whitespace_weekly
+description: Count of users with show whitespace enabled
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml b/config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml
new file mode 100644
index 00000000000..c1fccc9f774
--- /dev/null
+++ b/config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_hide_whitespace_weekly
+description: Count of users with show whitespace disabled
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml b/config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml
new file mode 100644
index 00000000000..3b1abf6e0e1
--- /dev/null
+++ b/config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_single_file_weekly
+description: Count of users with single file mode enabled
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml b/config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml
new file mode 100644
index 00000000000..b3105b041f8
--- /dev/null
+++ b/config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml
@@ -0,0 +1,19 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_diff_multiple_files_weekly
+description: Count of users with single mode disabled
+product_section: dev
+product_stage: create
+product_group: group::code review
+product_category: code_review
+value_type: number
+status: implemented
+milestone: '13.12'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979
+time_frame: 7d
+data_source: redis_hll
+distribution:
+ - ce
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 57a3b55f88f..1744396d995 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -13558,9 +13558,9 @@ Group member relation.
| Value | Description |
| ----- | ----------- |
-| <a id="groupmemberrelationdescendants"></a>`DESCENDANTS` | Descendants members. |
-| <a id="groupmemberrelationdirect"></a>`DIRECT` | Direct members. |
-| <a id="groupmemberrelationinherited"></a>`INHERITED` | Inherited members. |
+| <a id="groupmemberrelationdescendants"></a>`DESCENDANTS` | Members in the group's subgroups. |
+| <a id="groupmemberrelationdirect"></a>`DIRECT` | Members in the group itself. |
+| <a id="groupmemberrelationinherited"></a>`INHERITED` | Members in the group's ancestor groups. |
### `HealthStatus`
diff --git a/doc/development/i18n/merging_translations.md b/doc/development/i18n/merging_translations.md
index 553820733e7..48474a68d16 100644
--- a/doc/development/i18n/merging_translations.md
+++ b/doc/development/i18n/merging_translations.md
@@ -78,3 +78,10 @@ recreate it with the following steps:
1. Select the `gitlab-org/gitlab` repository.
1. In `Select Branches for Translation`, select `master`.
1. Ensure the `Service Branch Name` is `master-i18n`.
+
+## Manually update the translation levels
+
+There's no automated way to pull the translation levels from CrowdIn, to display
+this information in the language selection dropdown. Therefore, the translation
+levels are hard-coded in the `TRANSLATION_LEVELS` constant in [`i18n.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/i18n.rb),
+and must be regularly updated.
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index a5ed7ad2245..41b5d3179ed 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -168,7 +168,7 @@ can be used:
```ruby
RSpec.describe API::Search, factory_default: :keep do
- let_it_be(:namespace) { create_default(:namespace).freeze }
+ let_it_be(:namespace) { create_default(:namespace) }
```
Then every project we create uses this `namespace`, without us having to pass
@@ -186,7 +186,7 @@ projects we create are ones we ask for (76/208). There is benefit in setting
a default value for projects as well:
```ruby
- let_it_be(:project) { create_default(:project).freeze }
+ let_it_be(:project) { create_default(:project) }
```
In this case, the `total time` and `top-level time` numbers match more closely:
diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md
index a9af8f03d63..6b1c7a7eb58 100644
--- a/doc/development/testing_guide/flaky_tests.md
+++ b/doc/development/testing_guide/flaky_tests.md
@@ -76,6 +76,33 @@ For instance `RETRIES=1 bin/rspec ...` would retry the failing examples once.
- [Replace FFaker factory data with sequences](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/29643): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10184>
- [Transient failure in spec/finders/issues_finder_spec.rb](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30211#note_26707685): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10404>
+### Order-dependent flaky tests
+
+These flaky tests can fail depending on the order they run with other tests. For example:
+
+- <https://gitlab.com/gitlab-org/gitlab/-/issues/327668>
+
+To identify the tests that lead to such failure, we can use `rspec --bisect`,
+which would give us the minimal test combination to reproduce the failure:
+
+```shell
+rspec --bisect ee/spec/services/ee/merge_requests/update_service_spec.rb ee/spec/services/ee/notes/quick_actions_service_spec.rb ee/spec/services/epic_links/create_service_spec.rb ee/spec/services/ee/issuable/bulk_update_service_spec.rb
+Bisect started using options: "ee/spec/services/ee/merge_requests/update_service_spec.rb ee/spec/services/ee/notes/quick_actions_service_spec.rb ee/spec/services/epic_links/create_service_spec.rb ee/spec/services/ee/issuable/bulk_update_service_spec.rb"
+Running suite to find failures... (2 minutes 18.4 seconds)
+Starting bisect with 3 failing examples and 144 non-failing examples.
+Checking that failure(s) are order-dependent... failure appears to be order-dependent
+
+Round 1: bisecting over non-failing examples 1-144 . ignoring examples 1-72 (1 minute 11.33 seconds)
+...
+Round 7: bisecting over non-failing examples 132-133 . ignoring example 132 (43.78 seconds)
+Bisect complete! Reduced necessary non-failing examples from 144 to 1 in 8 minutes 31 seconds.
+
+The minimal reproduction command is:
+ rspec ./ee/spec/services/ee/issuable/bulk_update_service_spec.rb[1:2:1:1:1:1,1:2:1:2:1:1,1:2:1:3:1] ./ee/spec/services/epic_links/create_service_spec.rb[1:1:2:2:6:4]
+```
+
+We can reproduce the test failure with the reproduction command above. If we change the order of the tests, the test would pass.
+
### Time-sensitive flaky tests
- <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10046>
diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md
index 9a256b85983..036683b3d18 100644
--- a/doc/development/usage_ping/dictionary.md
+++ b/doc/development/usage_ping/dictionary.md
@@ -8048,6 +8048,222 @@ Status: `data_available`
Tiers: `free`, `premium`, `ultimate`
+### `redis_hll_counters.code_review.i_code_review_click_file_browser_setting_monthly`
+
+Count of users clicking merge request file browser setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_click_file_browser_setting_weekly`
+
+Count of users with merge request file list setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_click_single_file_mode_setting_monthly`
+
+Count of users clicking single file mode setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_click_single_file_mode_setting_weekly`
+
+Count of users clicking single file mode setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_click_whitespace_setting_monthly`
+
+Count of users clicking merge request whitespae setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_click_whitespace_setting_weekly`
+
+Count of users clicking merge request whitespae setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_hide_whitespace_monthly`
+
+Count of users with show whitespace disabled
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_hide_whitespace_weekly`
+
+Count of users with show whitespace disabled
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_multiple_files_monthly`
+
+Count of users with single mode disabled
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_multiple_files_weekly`
+
+Count of users with single mode disabled
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_show_whitespace_monthly`
+
+Count of users with show whitespace enabled
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_show_whitespace_weekly`
+
+Count of users with show whitespace enabled
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_single_file_monthly`
+
+Count of users with single file mode enabled
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_single_file_weekly`
+
+Count of users with single file mode enabled
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_view_inline_monthly`
+
+Count of users with merge request view type as inline
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_view_inline_weekly`
+
+Count of users with merge request view type as inline
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_view_parallel_monthly`
+
+Count of users with merge request view type as parallel
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_diff_view_parallel_weekly`
+
+Count of users with merge request view type as parallel
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
### `redis_hll_counters.code_review.i_code_review_edit_mr_desc_monthly`
Count of unique users per month who edit the description of a merge request
@@ -8096,6 +8312,54 @@ Status: `data_available`
Tiers: `free`, `premium`, `ultimate`
+### `redis_hll_counters.code_review.i_code_review_file_browser_list_view_monthly`
+
+Count of users with merge request file list setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_file_browser_list_view_weekly`
+
+Count of users with merge request file list setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_file_browser_tree_view_monthly`
+
+Count of users with merge request file tree setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
+### `redis_hll_counters.code_review.i_code_review_file_browser_tree_view_weekly`
+
+Count of users with merge request file tree setting
+
+[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml)
+
+Group: `group::code review`
+
+Status: `implemented`
+
+Tiers: `free`, `premium`, `ultimate`
+
### `redis_hll_counters.code_review.i_code_review_mr_diffs_monthly`
Count of unique merge requests per month with diffs viewed
diff --git a/doc/user/clusters/agent/repository.md b/doc/user/clusters/agent/repository.md
index 9caa4a89daf..49e5e8c58df 100644
--- a/doc/user/clusters/agent/repository.md
+++ b/doc/user/clusters/agent/repository.md
@@ -94,3 +94,41 @@ gitops:
# If 'paths' is not specified or is an empty list, the configuration below is used
- glob: '/**/*.{yaml,yml,json}'
```
+
+## Surface network security alerts from cluster to GitLab
+
+The GitLab Agent provides an [integration with Cilium](index.md#kubernetes-network-security-alerts).
+To integrate, add a top-level `cilium` section to your `config.yml` file. Currently, the
+only configuration option is the Hubble relay address:
+
+```yaml
+cilium:
+ hubble_relay_address: "<hubble-relay-host>:<hubble-relay-port>"
+```
+
+If your Cilium integration was performed through GitLab Managed Apps, you can use `hubble-relay.gitlab-managed-apps.svc.cluster.local:80` as the address:
+
+```yaml
+cilium:
+ hubble_relay_address: "hubble-relay.gitlab-managed-apps.svc.cluster.local:80"
+```
+
+## Debugging
+
+To debug the cluster-side component (`agentk`) of the GitLab Kubernetes Agent, set the log
+level according to the available options:
+
+- `off`
+- `warning`
+- `error`
+- `info`
+- `debug`
+
+The log level defaults to `info`. You can change it by using a top-level `observability`
+section in the configuration file, for example:
+
+```yaml
+observability:
+ logging:
+ level: debug
+```
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 4f2ac73c0d3..c844655f0b3 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -249,7 +249,6 @@ module API
authorize! :create_issue, user_project
issue_params = declared_params(include_missing: false)
- issue_params[:system_note_timestamp] = params[:created_at]
issue_params = convert_parameters_from_legacy_format(issue_params)
@@ -293,8 +292,6 @@ module API
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue
- issue.system_note_timestamp = params[:updated_at]
-
update_params = declared_params(include_missing: false).merge(request: request, api: true)
update_params = convert_parameters_from_legacy_format(update_params)
diff --git a/lib/gitlab/auth/scope_validator.rb b/lib/gitlab/auth/scope_validator.rb
new file mode 100644
index 00000000000..de4c36ad594
--- /dev/null
+++ b/lib/gitlab/auth/scope_validator.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+# Wrapper around a RequestAuthenticator to
+# perform authorization of scopes. Access is limited to
+# only those methods needed to validate that an API user
+# has at least one permitted scope.
+module Gitlab
+ module Auth
+ class ScopeValidator
+ def initialize(api_user, request_authenticator)
+ @api_user = api_user
+ @request_authenticator = request_authenticator
+ end
+
+ def valid_for?(permitted)
+ return true unless @api_user
+ return true if permitted.none?
+
+ scopes = permitted.map { |s| API::Scope.new(s) }
+ @request_authenticator.valid_access_token?(scopes: scopes)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/authorize/object_authorization.rb b/lib/gitlab/graphql/authorize/object_authorization.rb
index 0bc87108871..f13acc9ea27 100644
--- a/lib/gitlab/graphql/authorize/object_authorization.rb
+++ b/lib/gitlab/graphql/authorize/object_authorization.rb
@@ -4,10 +4,11 @@ module Gitlab
module Graphql
module Authorize
class ObjectAuthorization
- attr_reader :abilities
+ attr_reader :abilities, :permitted_scopes
- def initialize(abilities)
+ def initialize(abilities, scopes = %i[api read_api])
@abilities = Array.wrap(abilities).flatten
+ @permitted_scopes = Array.wrap(scopes)
end
def none?
@@ -18,7 +19,13 @@ module Gitlab
abilities.present?
end
- def ok?(object, current_user)
+ def ok?(object, current_user, scope_validator: nil)
+ scopes_ok?(scope_validator) && abilities_ok?(object, current_user)
+ end
+
+ private
+
+ def abilities_ok?(object, current_user)
return true if none?
subject = object.try(:declarative_policy_subject) || object
@@ -26,6 +33,12 @@ module Gitlab
Ability.allowed?(current_user, ability, subject)
end
end
+
+ def scopes_ok?(validator)
+ return true unless validator.present?
+
+ validator.valid_for?(permitted_scopes)
+ end
end
end
end
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 3b19ae3d7ff..12a3878e966 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -4,20 +4,6 @@ module Gitlab
module I18n
extend self
- # Languages with less then 2% of available translations will not
- # be available in the UI.
- # https://gitlab.com/gitlab-org/gitlab/-/issues/221012
- NOT_AVAILABLE_IN_UI = %w[
- fil_PH
- pl_PL
- nl_NL
- id_ID
- cs_CZ
- bg
- eo
- gl_ES
- ].freeze
-
AVAILABLE_LANGUAGES = {
'bg' => 'Bulgarian - български',
'cs_CZ' => 'Czech - čeština',
@@ -42,9 +28,49 @@ module Gitlab
'zh_HK' => 'Chinese, Traditional (Hong Kong) - 繁體中文 (香港)',
'zh_TW' => 'Chinese, Traditional (Taiwan) - 繁體中文 (台灣)'
}.freeze
+ private_constant :AVAILABLE_LANGUAGES
+
+ # Languages with less then MINIMUM_TRANSLATION_LEVEL% of available translations will not
+ # be available in the UI.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/221012
+ MINIMUM_TRANSLATION_LEVEL = 2
+
+ # Currently monthly updated manually by ~group::import PM.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/18923
+ TRANSLATION_LEVELS = {
+ 'bg' => 1,
+ 'cs_CZ' => 1,
+ 'de' => 20,
+ 'en' => 100,
+ 'eo' => 1,
+ 'es' => 44,
+ 'fil_PH' => 1,
+ 'fr' => 14,
+ 'gl_ES' => 1,
+ 'id_ID' => 0,
+ 'it' => 3,
+ 'ja' => 49,
+ 'ko' => 15,
+ 'nl_NL' => 1,
+ 'pl_PL' => 1,
+ 'pt_BR' => 23,
+ 'ru' => 34,
+ 'tr_TR' => 18,
+ 'uk' => 46,
+ 'zh_CN' => 78,
+ 'zh_HK' => 3,
+ 'zh_TW' => 4
+ }.freeze
+ private_constant :TRANSLATION_LEVELS
def selectable_locales
- AVAILABLE_LANGUAGES.reject { |key, _value| NOT_AVAILABLE_IN_UI.include? key }
+ @selectable_locales ||= AVAILABLE_LANGUAGES.reject do |code, _name|
+ percentage_translated_for(code) < MINIMUM_TRANSLATION_LEVEL
+ end
+ end
+
+ def percentage_translated_for(code)
+ TRANSLATION_LEVELS.fetch(code, 0)
end
def available_locales
diff --git a/lib/gitlab/pagination/gitaly_keyset_pager.rb b/lib/gitlab/pagination/gitaly_keyset_pager.rb
index 1350168967e..b05891066ac 100644
--- a/lib/gitlab/pagination/gitaly_keyset_pager.rb
+++ b/lib/gitlab/pagination/gitaly_keyset_pager.rb
@@ -26,11 +26,11 @@ module Gitlab
private
def keyset_pagination_enabled?
- Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true) && params[:pagination] == 'keyset'
+ Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: :yaml) && params[:pagination] == 'keyset'
end
def paginate_first_page?
- Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true) && (params[:page].blank? || params[:page].to_i == 1)
+ Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: :yaml) && (params[:page].blank? || params[:page].to_i == 1)
end
def paginate_via_gitaly(finder)
diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
index 18c5dc73de2..2f5c1de0b74 100644
--- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
@@ -204,3 +204,59 @@
category: code_review
aggregation: weekly
feature_flag: usage_data_i_code_review_user_labels_changed
+# Diff settings events
+- name: i_code_review_click_single_file_mode_setting
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_click_file_browser_setting
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_click_whitespace_setting
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_view_inline
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_view_parallel
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_file_browser_tree_view
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_file_browser_list_view
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_show_whitespace
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_hide_whitespace
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_single_file
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_multiple_files
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
diff --git a/lib/sidebars/projects/menus/merge_requests_menu.rb b/lib/sidebars/projects/menus/merge_requests_menu.rb
new file mode 100644
index 00000000000..fe501667d37
--- /dev/null
+++ b/lib/sidebars/projects/menus/merge_requests_menu.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Sidebars
+ module Projects
+ module Menus
+ class MergeRequestsMenu < ::Sidebars::Menu
+ override :link
+ def link
+ project_merge_requests_path(context.project)
+ end
+
+ override :extra_container_html_options
+ def extra_container_html_options
+ {
+ class: 'shortcuts-merge_requests'
+ }
+ end
+
+ override :title
+ def title
+ _('Merge requests')
+ end
+
+ override :title_html_options
+ def title_html_options
+ {
+ id: 'js-onboarding-mr-link'
+ }
+ end
+
+ override :sprite_icon
+ def sprite_icon
+ 'git-merge'
+ end
+
+ override :render?
+ def render?
+ can?(context.current_user, :read_merge_request, context.project) &&
+ context.project.repo_exists?
+ end
+
+ override :has_pill?
+ def has_pill?
+ true
+ end
+
+ override :pill_count
+ def pill_count
+ @pill_count ||= context.project.open_merge_requests_count
+ end
+
+ override :pill_html_options
+ def pill_html_options
+ {
+ class: 'merge_counter js-merge-counter'
+ }
+ end
+
+ override :active_routes
+ def active_routes
+ if context.project.issues_enabled?
+ { controller: :merge_requests }
+ else
+ { controller: [:merge_requests, :milestones] }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/sidebars/projects/panel.rb b/lib/sidebars/projects/panel.rb
index 0e2daca9194..1db4d55740a 100644
--- a/lib/sidebars/projects/panel.rb
+++ b/lib/sidebars/projects/panel.rb
@@ -13,6 +13,7 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::IssuesMenu.new(context))
add_menu(Sidebars::Projects::Menus::ExternalIssueTrackerMenu.new(context))
add_menu(Sidebars::Projects::Menus::LabelsMenu.new(context))
+ add_menu(Sidebars::Projects::Menus::MergeRequestsMenu.new(context))
end
override :render_raw_menus_partial
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 5ca4e7dad0c..f350f6ece5d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -30989,6 +30989,9 @@ msgstr ""
msgid "SuperSonics|Your subscription"
msgstr ""
+msgid "SuperSonics|Your subscription was successfully activated. You can see the details below."
+msgstr ""
+
msgid "Support"
msgstr ""
@@ -37792,6 +37795,9 @@ msgstr ""
msgid "https://your-bitbucket-server"
msgstr ""
+msgid "i18n|%{language} (%{percent_translated}%% translated)"
+msgstr ""
+
msgid "image diff"
msgstr ""
diff --git a/qa/qa/page/project/menu.rb b/qa/qa/page/project/menu.rb
index 4e8178c3cf6..a7c6637bb89 100644
--- a/qa/qa/page/project/menu.rb
+++ b/qa/qa/page/project/menu.rb
@@ -14,7 +14,6 @@ module QA
include SubMenus::Packages
view 'app/views/layouts/nav/sidebar/_project_menus.html.haml' do
- element :merge_requests_link
element :snippets_link
element :members_link
end
@@ -25,7 +24,7 @@ module QA
def click_merge_requests
within_sidebar do
- click_element(:merge_requests_link)
+ click_element(:sidebar_menu_link, menu_item: 'Merge requests')
end
end
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index 19680a827bf..5b11d9cb919 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
expect(page).to have_css('.review-bar-component')
- expect(find('.review-bar-content .btn-success')).to have_content('1')
+ expect(find('.review-bar-content .btn-confirm')).to have_content('1')
end
it 'publishes review' do
@@ -157,7 +157,7 @@ RSpec.describe 'Merge request > Batch comments', :js do
expect(find('.new .draft-note-component')).to have_content('Line is wrong')
expect(find('.old .draft-note-component')).to have_content('Another wrong line')
- expect(find('.review-bar-content .btn-success')).to have_content('2')
+ expect(find('.review-bar-content .btn-confirm')).to have_content('2')
end
end
diff --git a/spec/features/profiles/user_edit_preferences_spec.rb b/spec/features/profiles/user_edit_preferences_spec.rb
index 3129e4bd952..232402f802e 100644
--- a/spec/features/profiles/user_edit_preferences_spec.rb
+++ b/spec/features/profiles/user_edit_preferences_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe 'User edit preferences profile', :js do
@@ -63,17 +64,4 @@ RSpec.describe 'User edit preferences profile', :js do
expect(page).to have_content('Failed to save preferences.')
end
end
-
- describe 'User language' do
- let(:user) { create(:user, preferred_language: :es) }
-
- it 'shows the user preferred language by default' do
- expect(page).to have_select(
- 'user[preferred_language]',
- selected: 'Spanish - español',
- options: Gitlab::I18n.selectable_locales.values,
- visible: :all
- )
- end
- end
end
diff --git a/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap b/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap
index f8c68ca4c83..d9f5ba0bade 100644
--- a/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap
+++ b/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Design reply form component renders button text as "Comment" when creating a comment 1`] = `
-"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled gl-button\\">
+"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn gl-mr-3 gl-w-auto! btn-confirm btn-md disabled gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\">
Comment
@@ -9,7 +9,7 @@ exports[`Design reply form component renders button text as "Comment" when creat
`;
exports[`Design reply form component renders button text as "Save comment" when creating a comment 1`] = `
-"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled gl-button\\">
+"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn gl-mr-3 gl-w-auto! btn-confirm btn-md disabled gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\">
Save comment
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 4d7083c4ca7..adb29f6634d 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -121,6 +121,23 @@ RSpec.describe PreferencesHelper do
end
end
+ describe '#language_choices' do
+ it 'lists all the selectable language options with their translation percent' do
+ stub_const(
+ 'Gitlab::I18n::TRANSLATION_LEVELS',
+ 'en' => 100,
+ 'es' => 65
+ )
+
+ stub_user(preferred_language: :en)
+
+ expect(helper.language_choices).to eq([
+ '<option selected="selected" value="en">English (100% translated)</option>',
+ '<option value="es">Spanish - español (65% translated)</option>'
+ ].join("\n"))
+ end
+ end
+
def stub_user(messages = {})
if messages.empty?
allow(helper).to receive(:current_user).and_return(nil)
diff --git a/spec/lib/gitlab/i18n_spec.rb b/spec/lib/gitlab/i18n_spec.rb
index ee10739195a..f34de298fc0 100644
--- a/spec/lib/gitlab/i18n_spec.rb
+++ b/spec/lib/gitlab/i18n_spec.rb
@@ -3,13 +3,21 @@
require 'spec_helper'
RSpec.describe Gitlab::I18n do
- let(:user) { create(:user, preferred_language: 'es') }
+ let(:user) { create(:user, preferred_language: :es) }
describe '.selectable_locales' do
- it 'does not return languages that should not be available in the UI' do
- Gitlab::I18n::NOT_AVAILABLE_IN_UI.each do |language|
- expect(described_class.selectable_locales).not_to include(language)
- end
+ it 'does not return languages with low translation levels' do
+ stub_const(
+ 'Gitlab::I18n::TRANSLATION_LEVELS',
+ 'pt_BR' => 0,
+ 'en' => 100,
+ 'es' => 65
+ )
+
+ expect(described_class.selectable_locales).to eq({
+ 'en' => 'English',
+ 'es' => 'Spanish - español'
+ })
end
end
diff --git a/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb b/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb
new file mode 100644
index 00000000000..cef303fb068
--- /dev/null
+++ b/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Projects::Menus::MergeRequestsMenu do
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:user) { project.owner }
+ let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
+
+ subject { described_class.new(context) }
+
+ describe '#render?' do
+ context 'when repository is not present' do
+ let(:project) { build(:project) }
+
+ it 'returns false' do
+ expect(subject.render?).to eq false
+ end
+ end
+
+ context 'when repository is present' do
+ context 'when user can read merge requests' do
+ it 'returns true' do
+ expect(subject.render?).to eq true
+ end
+ end
+
+ context 'when user cannot read merge requests' do
+ let(:user) { nil }
+
+ it 'returns false' do
+ expect(subject.render?).to eq false
+ end
+ end
+ end
+ end
+
+ describe '#pill_count' do
+ it 'returns zero when there are no open merge requests' do
+ expect(subject.pill_count).to eq 0
+ end
+
+ it 'memoizes the query' do
+ subject.pill_count
+
+ control = ActiveRecord::QueryRecorder.new do
+ subject.pill_count
+ end
+
+ expect(control.count).to eq 0
+ end
+
+ context 'when there are open merge requests' do
+ it 'returns the number of open merge requests' do
+ create_list(:merge_request, 2, :unique_branches, source_project: project, author: user, state: :opened)
+ create(:merge_request, source_project: project, state: :merged)
+
+ expect(subject.pill_count).to eq 2
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
index 1eed1c8e2ae..8dd8ed361ba 100644
--- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
@@ -31,6 +31,8 @@ RSpec.describe 'Adding a Note' do
project.add_developer(current_user)
end
+ it_behaves_like 'a working GraphQL mutation'
+
it_behaves_like 'a Note mutation that creates a Note'
it_behaves_like 'a Note mutation when there are active record validation errors'
diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb
index 0fe68be027c..8f10de59526 100644
--- a/spec/requests/api/issues/issues_spec.rb
+++ b/spec/requests/api/issues/issues_spec.rb
@@ -943,6 +943,34 @@ RSpec.describe API::Issues do
it_behaves_like 'issuable update endpoint' do
let(:entity) { issue }
end
+
+ describe 'updated_at param' do
+ let(:fixed_time) { Time.new(2001, 1, 1) }
+ let(:updated_at) { Time.new(2000, 1, 1) }
+
+ before do
+ travel_to fixed_time
+ end
+
+ it 'allows admins to set the timestamp' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", admin), params: { labels: 'label1', updated_at: updated_at }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(Time.parse(json_response['updated_at'])).to be_like_time(updated_at)
+ expect(ResourceLabelEvent.last.created_at).to be_like_time(updated_at)
+ end
+
+ it 'does not allow other users to set the timestamp' do
+ reporter = create(:user)
+ project.add_developer(reporter)
+
+ put api("/projects/#{project.id}/issues/#{issue.iid}", reporter), params: { labels: 'label1', updated_at: updated_at }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(Time.parse(json_response['updated_at'])).to be_like_time(fixed_time)
+ expect(ResourceLabelEvent.last.created_at).to be_like_time(fixed_time)
+ end
+ end
end
describe 'DELETE /projects/:id/issues/:issue_iid' do
diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb
index 7f1db620d4f..9d3bd26a200 100644
--- a/spec/requests/api/issues/post_projects_issues_spec.rb
+++ b/spec/requests/api/issues/post_projects_issues_spec.rb
@@ -330,15 +330,21 @@ RSpec.describe API::Issues do
end
context 'setting created_at' do
+ let(:fixed_time) { Time.new(2001, 1, 1) }
let(:creation_time) { 2.weeks.ago }
let(:params) { { title: 'new issue', labels: 'label, label2', created_at: creation_time } }
+ before do
+ travel_to fixed_time
+ end
+
context 'by an admin' do
it 'sets the creation time on the new issue' do
post api("/projects/#{project.id}/issues", admin), params: params
expect(response).to have_gitlab_http_status(:created)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time)
end
end
@@ -348,6 +354,7 @@ RSpec.describe API::Issues do
expect(response).to have_gitlab_http_status(:created)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time)
end
end
@@ -356,19 +363,24 @@ RSpec.describe API::Issues do
group = create(:group)
group_project = create(:project, :public, namespace: group)
group.add_owner(user2)
+
post api("/projects/#{group_project.id}/issues", user2), params: params
expect(response).to have_gitlab_http_status(:created)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
+ expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time)
end
end
context 'by another user' do
it 'ignores the given creation time' do
+ project.add_developer(user2)
+
post api("/projects/#{project.id}/issues", user2), params: params
expect(response).to have_gitlab_http_status(:created)
- expect(Time.parse(json_response['created_at'])).not_to be_like_time(creation_time)
+ expect(Time.parse(json_response['created_at'])).to be_like_time(fixed_time)
+ expect(ResourceLabelEvent.last.created_at).to be_like_time(fixed_time)
end
end
end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index 8be26784a3d..5b5658da97e 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -263,25 +263,21 @@ RSpec.describe JwtController do
let(:credential_user) { group_deploy_token.username }
let(:credential_password) { group_deploy_token.token }
- it_behaves_like 'with valid credentials'
+ it_behaves_like 'returning response status', :forbidden
end
context 'with project deploy token' do
let(:credential_user) { project_deploy_token.username }
let(:credential_password) { project_deploy_token.token }
- it_behaves_like 'with valid credentials'
+ it_behaves_like 'returning response status', :forbidden
end
context 'with invalid credentials' do
let(:credential_user) { 'foo' }
let(:credential_password) { 'bar' }
- it 'returns unauthorized' do
- subject
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
+ it_behaves_like 'returning response status', :unauthorized
end
end
diff --git a/spec/services/auth/dependency_proxy_authentication_service_spec.rb b/spec/services/auth/dependency_proxy_authentication_service_spec.rb
index ba50149f53a..1fd1677c7da 100644
--- a/spec/services/auth/dependency_proxy_authentication_service_spec.rb
+++ b/spec/services/auth/dependency_proxy_authentication_service_spec.rb
@@ -13,28 +13,31 @@ RSpec.describe Auth::DependencyProxyAuthenticationService do
describe '#execute' do
subject { service.execute(authentication_abilities: nil) }
+ shared_examples 'returning' do |status:, message:|
+ it "returns #{message}", :aggregate_failures do
+ expect(subject[:http_status]).to eq(status)
+ expect(subject[:message]).to eq(message)
+ end
+ end
+
context 'dependency proxy is not enabled' do
before do
stub_config(dependency_proxy: { enabled: false })
end
- it 'returns not found' do
- result = subject
-
- expect(result[:http_status]).to eq(404)
- expect(result[:message]).to eq('dependency proxy not enabled')
- end
+ it_behaves_like 'returning', status: 404, message: 'dependency proxy not enabled'
end
context 'without a user' do
let(:user) { nil }
- it 'returns forbidden' do
- result = subject
+ it_behaves_like 'returning', status: 403, message: 'access forbidden'
+ end
+
+ context 'with a deploy token as user' do
+ let_it_be(:user) { create(:deploy_token) }
- expect(result[:http_status]).to eq(403)
- expect(result[:message]).to eq('access forbidden')
- end
+ it_behaves_like 'returning', status: 403, message: 'access forbidden'
end
context 'with a user' do
diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb
index 0f743eaa7f5..7d4fce814f5 100644
--- a/spec/services/projects/download_service_spec.rb
+++ b/spec/services/projects/download_service_spec.rb
@@ -20,8 +20,9 @@ RSpec.describe Projects::DownloadService do
context 'for URLs that are on the whitelist' do
before do
- stub_request(:get, 'http://mycompany.fogbugz.com/rails_sample.jpg').to_return(body: File.read(Rails.root + 'spec/fixtures/rails_sample.jpg'))
- stub_request(:get, 'http://mycompany.fogbugz.com/doc_sample.txt').to_return(body: File.read(Rails.root + 'spec/fixtures/doc_sample.txt'))
+ # `ssrf_filter` resolves the hostname. See https://github.com/carrierwaveuploader/carrierwave/commit/91714adda998bc9e8decf5b1f5d260d808761304
+ stub_request(:get, %r{http://[\d\.]+/rails_sample.jpg}).to_return(body: File.read(Rails.root + 'spec/fixtures/rails_sample.jpg'))
+ stub_request(:get, %r{http://[\d\.]+/doc_sample.txt}).to_return(body: File.read(Rails.root + 'spec/fixtures/doc_sample.txt'))
end
context 'an image file' do
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index ebf22987d50..5dc6945ec5e 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -396,17 +396,21 @@ module GraphqlHelpers
post api('/', current_user, version: 'graphql'), params: { _json: queries }, headers: headers
end
- def post_graphql(query, current_user: nil, variables: nil, headers: {})
+ def post_graphql(query, current_user: nil, variables: nil, headers: {}, token: {})
params = { query: query, variables: serialize_variables(variables) }
- post api('/', current_user, version: 'graphql'), params: params, headers: headers
+ post api('/', current_user, version: 'graphql', **token), params: params, headers: headers
- if graphql_errors # Errors are acceptable, but not this one:
- expect(graphql_errors).not_to include(a_hash_including('message' => 'Internal server error'))
- end
+ return unless graphql_errors
+
+ # Errors are acceptable, but not this one:
+ expect(graphql_errors).not_to include(a_hash_including('message' => 'Internal server error'))
end
- def post_graphql_mutation(mutation, current_user: nil)
- post_graphql(mutation.query, current_user: current_user, variables: mutation.variables)
+ def post_graphql_mutation(mutation, current_user: nil, token: {})
+ post_graphql(mutation.query,
+ current_user: current_user,
+ variables: mutation.variables,
+ token: token)
end
def post_graphql_mutation_with_uploads(mutation, current_user: nil)
diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb
index a66bc7112fe..d133c5ea641 100644
--- a/spec/support/shared_examples/requests/graphql_shared_examples.rb
+++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb
@@ -10,6 +10,52 @@ RSpec.shared_examples 'a working graphql query' do
end
end
+RSpec.shared_examples 'a working GraphQL mutation' do
+ include GraphqlHelpers
+
+ before do
+ post_graphql_mutation(mutation, current_user: current_user, token: token)
+ end
+
+ shared_examples 'allows access to the mutation' do
+ let(:scopes) { ['api'] }
+
+ it_behaves_like 'a working graphql query' do
+ it 'returns data' do
+ expect(graphql_data.compact).not_to be_empty
+ end
+ end
+ end
+
+ shared_examples 'prevents access to the mutation' do
+ let(:scopes) { ['read_api'] }
+
+ it 'does not resolve the mutation' do
+ expect(graphql_data.compact).to be_empty
+ expect(graphql_errors).to be_present
+ end
+ end
+
+ context 'with a personal access token' do
+ let(:token) do
+ pat = create(:personal_access_token, user: current_user, scopes: scopes)
+ { personal_access_token: pat }
+ end
+
+ it_behaves_like 'prevents access to the mutation'
+ it_behaves_like 'allows access to the mutation'
+ end
+
+ context 'with an OAuth token' do
+ let(:token) do
+ { oauth_access_token: create(:oauth_access_token, resource_owner: current_user, scopes: scopes.join(' ')) }
+ end
+
+ it_behaves_like 'prevents access to the mutation'
+ it_behaves_like 'allows access to the mutation'
+ end
+end
+
RSpec.shared_examples 'a mutation on an unauthorized resource' do
it_behaves_like 'a mutation that returns top-level errors',
errors: [::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
index 39cc4a3fdf1..c501c418466 100644
--- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
@@ -243,6 +243,20 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
+ describe 'Merge Requests' do
+ it 'has a link to the merge request list path' do
+ render
+
+ expect(rendered).to have_link('Merge requests', href: project_merge_requests_path(project), class: 'shortcuts-merge_requests')
+ end
+
+ it 'shows pill with the number of merge requests' do
+ render
+
+ expect(rendered).to have_css('span.badge.badge-pill.merge_counter.js-merge-counter')
+ end
+ end
+
describe 'packages tab' do
before do
stub_container_registry_config(enabled: true)