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-05-10 12:10:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-10 12:10:28 +0300
commitd5f14b5e2cef173b377917829b8a494c9975af03 (patch)
tree0c8fc0258579a94743d151db56e7239f90800b35
parent9d485c177e9404da5ba53702042fdb9c25d459f2 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/boards/stores/actions.js29
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js10
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js45
-rw-r--r--app/assets/javascripts/vue_shared/directives/validation.js10
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/views/layouts/nav/sidebar/_project_menus.html.haml34
-rw-r--r--app/views/shared/nav/_sidebar.html.haml1
-rw-r--r--app/views/shared/nav/_sidebar_hidden_menu_item.html.haml3
-rw-r--r--db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb2
-rw-r--r--doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md2
-rw-r--r--doc/api/users.md8
-rw-r--r--doc/development/changelog.md4
-rw-r--r--doc/development/feature_flags/index.md26
-rw-r--r--doc/user/application_security/api_fuzzing/index.md5
-rw-r--r--doc/user/application_security/security_dashboard/img/security_center_dashboard_link_v12_4.pngbin7921 -> 0 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/security_center_settings_v13_4.pngbin30034 -> 69604 bytes
-rw-r--r--doc/user/application_security/security_dashboard/index.md6
-rw-r--r--lib/sidebars/projects/menus/hidden_menu.rb95
-rw-r--r--lib/sidebars/projects/panel.rb6
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/frontend/boards/stores/actions_spec.js30
-rw-r--r--spec/frontend/boards/stores/mutations_spec.js46
-rw-r--r--spec/frontend/vue_shared/directives/validation_spec.js74
-rw-r--r--spec/lib/sidebars/projects/menus/hidden_menu_spec.rb102
-rw-r--r--spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb38
27 files changed, 349 insertions, 238 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index ea8d3d5236d..f2f3c67b121 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-6941c499e077fe2303dd5c31a08807d14ad7a616
+529ef59e73a21d1abc54833c4edbe92cbcc9fb64
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 19285a1dc85..5158e82c320 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -40,11 +40,6 @@ import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.g
import listsIssuesQuery from '../graphql/lists_issues.query.graphql';
import * as types from './mutation_types';
-const notImplemented = () => {
- /* eslint-disable-next-line @gitlab/require-i18n-strings */
- throw new Error('Not implemented!');
-};
-
export const gqlClient = createGqClient(
{},
{
@@ -737,28 +732,4 @@ export default {
unsetError: ({ commit }) => {
commit(types.SET_ERROR, undefined);
},
-
- fetchBacklog: () => {
- notImplemented();
- },
-
- bulkUpdateIssues: () => {
- notImplemented();
- },
-
- fetchIssue: () => {
- notImplemented();
- },
-
- toggleIssueSubscription: () => {
- notImplemented();
- },
-
- showPage: () => {
- notImplemented();
- },
-
- toggleEmptyState: () => {
- notImplemented();
- },
};
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 22b9905ee62..ccea2917c2c 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -9,9 +9,7 @@ export const GENERATE_DEFAULT_LISTS_FAILURE = 'GENERATE_DEFAULT_LISTS_FAILURE';
export const RECEIVE_BOARD_LISTS_SUCCESS = 'RECEIVE_BOARD_LISTS_SUCCESS';
export const RECEIVE_BOARD_LISTS_FAILURE = 'RECEIVE_BOARD_LISTS_FAILURE';
export const SHOW_PROMOTION_LIST = 'SHOW_PROMOTION_LIST';
-export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST';
export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
-export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR';
export const MOVE_LIST = 'MOVE_LIST';
export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE';
export const TOGGLE_LIST_COLLAPSED = 'TOGGLE_LIST_COLLAPSED';
@@ -20,19 +18,11 @@ export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE';
export const REQUEST_ITEMS_FOR_LIST = 'REQUEST_ITEMS_FOR_LIST';
export const RECEIVE_ITEMS_FOR_LIST_FAILURE = 'RECEIVE_ITEMS_FOR_LIST_FAILURE';
export const RECEIVE_ITEMS_FOR_LIST_SUCCESS = 'RECEIVE_ITEMS_FOR_LIST_SUCCESS';
-export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE';
-export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS';
-export const RECEIVE_ADD_ISSUE_ERROR = 'RECEIVE_ADD_ISSUE_ERROR';
export const UPDATE_BOARD_ITEM = 'UPDATE_BOARD_ITEM';
export const REMOVE_BOARD_ITEM = 'REMOVE_BOARD_ITEM';
-export const REQUEST_UPDATE_ISSUE = 'REQUEST_UPDATE_ISSUE';
export const MUTATE_ISSUE_SUCCESS = 'MUTATE_ISSUE_SUCCESS';
-export const RECEIVE_UPDATE_ISSUE_SUCCESS = 'RECEIVE_UPDATE_ISSUE_SUCCESS';
-export const RECEIVE_UPDATE_ISSUE_ERROR = 'RECEIVE_UPDATE_ISSUE_ERROR';
export const ADD_BOARD_ITEM_TO_LIST = 'ADD_BOARD_ITEM_TO_LIST';
export const REMOVE_BOARD_ITEM_FROM_LIST = 'REMOVE_BOARD_ITEM_FROM_LIST';
-export const SET_CURRENT_PAGE = 'SET_CURRENT_PAGE';
-export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE';
export const SET_ACTIVE_ID = 'SET_ACTIVE_ID';
export const UPDATE_BOARD_ITEM_BY_ID = 'UPDATE_BOARD_ITEM_BY_ID';
export const SET_ASSIGNEE_LOADING = 'SET_ASSIGNEE_LOADING';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 4f89d7e9d1f..667628b2998 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -6,11 +6,6 @@ import { formatIssue } from '../boards_util';
import { issuableTypes } from '../constants';
import * as mutationTypes from './mutation_types';
-const notImplemented = () => {
- /* eslint-disable-next-line @gitlab/require-i18n-strings */
- throw new Error('Not implemented!');
-};
-
const updateListItemsCount = ({ state, listId, value }) => {
const list = state.boardLists[listId];
if (state.issuableType === issuableTypes.epic) {
@@ -94,18 +89,10 @@ export default {
state.error = s__('Boards|An error occurred while generating lists. Please reload the page.');
},
- [mutationTypes.REQUEST_ADD_LIST]: () => {
- notImplemented();
- },
-
[mutationTypes.RECEIVE_ADD_LIST_SUCCESS]: (state, list) => {
Vue.set(state.boardLists, list.id, list);
},
- [mutationTypes.RECEIVE_ADD_LIST_ERROR]: () => {
- notImplemented();
- },
-
[mutationTypes.MOVE_LIST]: (state, { movedList, listAtNewIndex }) => {
const { boardLists } = state;
Vue.set(boardLists, movedList.id, movedList);
@@ -172,35 +159,11 @@ export default {
state.isSettingAssignees = isLoading;
},
- [mutationTypes.REQUEST_ADD_ISSUE]: () => {
- notImplemented();
- },
-
- [mutationTypes.RECEIVE_ADD_ISSUE_SUCCESS]: () => {
- notImplemented();
- },
-
- [mutationTypes.RECEIVE_ADD_ISSUE_ERROR]: () => {
- notImplemented();
- },
-
[mutationTypes.MUTATE_ISSUE_SUCCESS]: (state, { issue }) => {
const issueId = getIdFromGraphQLId(issue.id);
Vue.set(state.boardItems, issueId, formatIssue({ ...issue, id: issueId }));
},
- [mutationTypes.REQUEST_UPDATE_ISSUE]: () => {
- notImplemented();
- },
-
- [mutationTypes.RECEIVE_UPDATE_ISSUE_SUCCESS]: () => {
- notImplemented();
- },
-
- [mutationTypes.RECEIVE_UPDATE_ISSUE_ERROR]: () => {
- notImplemented();
- },
-
[mutationTypes.ADD_BOARD_ITEM_TO_LIST]: (
state,
{ itemId, listId, moveBeforeId, moveAfterId, atIndex },
@@ -220,14 +183,6 @@ export default {
Vue.delete(state.boardItems, itemId);
},
- [mutationTypes.SET_CURRENT_PAGE]: () => {
- notImplemented();
- },
-
- [mutationTypes.TOGGLE_EMPTY_STATE]: () => {
- notImplemented();
- },
-
[mutationTypes.REQUEST_GROUP_PROJECTS]: (state, fetchNext) => {
Vue.set(state, 'groupProjectsFlags', {
[fetchNext ? 'isLoadingMore' : 'isLoading']: true,
diff --git a/app/assets/javascripts/vue_shared/directives/validation.js b/app/assets/javascripts/vue_shared/directives/validation.js
index 176954891e9..df853a7c5b5 100644
--- a/app/assets/javascripts/vue_shared/directives/validation.js
+++ b/app/assets/javascripts/vue_shared/directives/validation.js
@@ -33,6 +33,10 @@ const focusFirstInvalidInput = (e) => {
}
};
+const getInputElement = (el) => {
+ return el.querySelector('input') || el;
+};
+
const isEveryFieldValid = (form) => Object.values(form.fields).every(({ state }) => state === true);
const createValidator = (context, feedbackMap) => ({ el, reportInvalidInput = false }) => {
@@ -91,8 +95,9 @@ export default function initValidation(customFeedbackMap = {}) {
const elDataMap = new WeakMap();
return {
- inserted(el, binding, { context }) {
+ inserted(element, binding, { context }) {
const { arg: showGlobalValidation } = binding;
+ const el = getInputElement(element);
const { form: formEl } = el;
const validate = createValidator(context, feedbackMap);
@@ -121,7 +126,8 @@ export default function initValidation(customFeedbackMap = {}) {
validate({ el, reportInvalidInput: showGlobalValidation });
},
- update(el, binding) {
+ update(element, binding) {
+ const el = getInputElement(element);
const { arg: showGlobalValidation } = binding;
const { validate, isTouched, isBlurred } = elDataMap.get(el);
const showValidationFeedback = showGlobalValidation || (isTouched && isBlurred);
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 1e44321e148..3c377a2bb35 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -103,7 +103,7 @@ module Issuable
end
scope :assigned_to, ->(u) do
assignees_table = Arel::Table.new("#{to_ability_name}_assignees")
- sql = assignees_table.project('true').where(assignees_table[:user_id].in(u)).where(Arel::Nodes::SqlLiteral.new("#{to_ability_name}_id = #{to_ability_name}s.id"))
+ sql = assignees_table.project('true').where(assignees_table[:user_id].in(u.id)).where(Arel::Nodes::SqlLiteral.new("#{to_ability_name}_id = #{to_ability_name}s.id"))
where("EXISTS (#{sql.to_sql})")
end
# rubocop:enable GitlabSecurity/SqlInjection
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 3002ad27e91..b93aff063bb 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -381,7 +381,7 @@ class MergeRequest < ApplicationRecord
scope :review_requested_to, ->(user) do
where(
reviewers_subquery
- .where(Arel::Table.new("#{to_ability_name}_reviewers")[:user_id].eq(user))
+ .where(Arel::Table.new("#{to_ability_name}_reviewers")[:user_id].eq(user.id))
.exists
)
end
@@ -389,7 +389,7 @@ class MergeRequest < ApplicationRecord
scope :no_review_requested_to, ->(user) do
where(
reviewers_subquery
- .where(Arel::Table.new("#{to_ability_name}_reviewers")[:user_id].eq(user))
+ .where(Arel::Table.new("#{to_ability_name}_reviewers")[:user_id].eq(user.id))
.exists
.not
)
diff --git a/app/views/layouts/nav/sidebar/_project_menus.html.haml b/app/views/layouts/nav/sidebar/_project_menus.html.haml
deleted file mode 100644
index 719f3a3de33..00000000000
--- a/app/views/layouts/nav/sidebar/_project_menus.html.haml
+++ /dev/null
@@ -1,34 +0,0 @@
--# Shortcut to Project > Activity
-%li.hidden
- = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do
- %span
- = _('Activity')
-
--# Shortcut to Repository > Graph (formerly, Network)
-- if project_nav_tab? :network
- %li.hidden
- = link_to project_network_path(@project, current_ref), title: _('Network'), class: 'shortcuts-network' do
- = _('Graph')
-
--# Shortcut to Issues > New Issue
-- if project_nav_tab?(:issues)
- %li.hidden
- = link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do
- = _('Create a new issue')
-
--# Shortcut to Pipelines > Jobs
-- if project_nav_tab? :builds
- %li.hidden
- = link_to project_jobs_path(@project), title: _('Jobs'), class: 'shortcuts-builds' do
- = _('Jobs')
-
--# Shortcut to commits page
-- if project_nav_tab? :commits
- %li.hidden
- = link_to project_commits_path(@project), title: _('Commits'), class: 'shortcuts-commits' do
- = _('Commits')
-
--# Shortcut to issue boards
-- if project_nav_tab?(:issues)
- %li.hidden
- = link_to _('Issue Boards'), project_boards_path(@project), title: _('Issue Boards'), class: 'shortcuts-issue-boards'
diff --git a/app/views/shared/nav/_sidebar.html.haml b/app/views/shared/nav/_sidebar.html.haml
index 1c06fc9eebf..8e388f2826d 100644
--- a/app/views/shared/nav/_sidebar.html.haml
+++ b/app/views/shared/nav/_sidebar.html.haml
@@ -11,4 +11,5 @@
- if sidebar.render_raw_menus_partial
= render sidebar.render_raw_menus_partial
+ = render partial: 'shared/nav/sidebar_hidden_menu_item', collection: sidebar.hidden_menu&.items
= render 'shared/sidebar_toggle_button'
diff --git a/app/views/shared/nav/_sidebar_hidden_menu_item.html.haml b/app/views/shared/nav/_sidebar_hidden_menu_item.html.haml
new file mode 100644
index 00000000000..953f7a8ae60
--- /dev/null
+++ b/app/views/shared/nav/_sidebar_hidden_menu_item.html.haml
@@ -0,0 +1,3 @@
+%li.hidden
+ = link_to sidebar_hidden_menu_item.link, **sidebar_hidden_menu_item.container_html_options do
+ = sidebar_hidden_menu_item.title
diff --git a/db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb b/db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb
index 4bb43da43bb..f7df9f28187 100644
--- a/db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb
+++ b/db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb
@@ -9,6 +9,8 @@ class RemoveDuplicateLabelsFromProject < ActiveRecord::Migration[6.0]
disable_ddl_transaction!
class BackupLabel < Label
+ self.inheritance_column = :_type_disabled
+
self.table_name = 'backup_labels'
end
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index 6b1cf2d1194..6a510900a41 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -537,7 +537,7 @@ inactive_users.each do |user|
end
```
-### Find Max permissions for project/group
+### Find a user's max permissions for project/group
```ruby
user = User.find_by_username 'username'
diff --git a/doc/api/users.md b/doc/api/users.md
index 228194f10fa..ac8fbe8492f 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -190,7 +190,7 @@ GET /users
]
```
-Users on GitLab [Premium or higher](https://about.gitlab.com/pricing/) also see the `shared_runners_minutes_limit`, `extra_shared_runners_minutes_limit`, and `using_license_seat` parameters.
+Users on GitLab [Premium or higher](https://about.gitlab.com/pricing/) also see the `shared_runners_minutes_limit`, `extra_shared_runners_minutes_limit`, `is_auditor`, and `using_license_seat` parameters.
```json
[
@@ -199,6 +199,7 @@ Users on GitLab [Premium or higher](https://about.gitlab.com/pricing/) also see
...
"shared_runners_minutes_limit": 133,
"extra_shared_runners_minutes_limit": 133,
+ "is_auditor": false,
"using_license_seat": true
...
}
@@ -359,12 +360,13 @@ NOTE:
The `plan` and `trial` parameters are only available on GitLab Enterprise Edition.
Users on GitLab [Premium or higher](https://about.gitlab.com/pricing/) also see
-the `shared_runners_minutes_limit`, and `extra_shared_runners_minutes_limit` parameters.
+the `shared_runners_minutes_limit`, `is_auditor`, and `extra_shared_runners_minutes_limit` parameters.
```json
{
"id": 1,
"username": "john_smith",
+ "is_auditor": false,
"shared_runners_minutes_limit": 133,
"extra_shared_runners_minutes_limit": 133,
...
@@ -628,6 +630,8 @@ GET /user
}
```
+Users on GitLab [Premium or higher](https://about.gitlab.com/pricing/) also see the `shared_runners_minutes_limit`, `extra_shared_runners_minutes_limit`, `is_auditor`, and `using_license_seat` parameters.
+
## User status
Get the status of the currently signed in user.
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index f77f918d2a1..6412c303735 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -52,11 +52,9 @@ the `author` field. GitLab team members **should not**.
a changelog entry regardless of these guidelines if the contributor wants one.
Example: "Fixed a typo on the search results page."
- Any docs-only changes **should not** have a changelog entry.
-- Any change behind a feature flag **disabled** by default **should not** have a changelog entry.
-- Any change behind a feature flag that is **enabled** by default **should** have a changelog entry.
+- For changes related to feature flags, use [feature flag guide](feature_flags/index.md#changelog) to determine the changelog entry.
- Any change that adds new Usage Data metrics, sets the status of existing ones to `removed`, and changes that need to be documented in Product Intelligence [Metrics Dictionary](usage_ping/dictionary.md) **should** have a changelog entry.
- A change that adds snowplow events **should** have a changelog entry -
-- A change that [removes a feature flag, or removes a feature and its feature flag](feature_flags/index.md) **must** have a changelog entry.
- A fix for a regression introduced and then fixed in the same release (i.e.,
fixing a bug introduced during a monthly release candidate) **should not**
have a changelog entry.
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index 560e4f8cb90..e18bcaa1f4e 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -15,12 +15,6 @@ This document provides guidelines on how to use feature flags
in the GitLab codebase to conditionally enable features
and test them.
-Features that are developed and merged behind a feature flag
-should not include a changelog entry. A changelog entry with `type: added` should be included in the merge
-request removing the feature flag or the merge request where the default value of
-the feature flag is set to enabled. If the feature contains any database migrations, it
-*should* include a changelog entry for the database changes.
-
WARNING:
All newly-introduced feature flags should be [disabled by default](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#feature-flags-in-gitlab-development).
@@ -55,7 +49,7 @@ should be leveraged:
a specific project and ensure that there are no issues with the implementation.
1. When the feature is ready to be announced, create a merge request that adds
documentation about the feature, including [documentation for the feature flag itself](../documentation/feature_flags.md),
- and a changelog entry. In the same merge request either flip the feature flag to
+ and a [changelog entry](#changelog). In the same merge request either flip the feature flag to
be **on by default** or remove it entirely in order to enable the new behavior.
One might be tempted to think that feature flags will delay the release of a
@@ -461,6 +455,24 @@ as follows:
Feature.remove(:feature_flag_name)
```
+## Changelog
+
+- Any change behind a feature flag **disabled** by default **should not** have a changelog entry.
+ - **Exception:** database migrations **should** have a changelog entry.
+- Any change related to a feature flag itself (flag removal, default-on setting) **should** have a changelog entry.
+ Use the flowchart to determine the changelog entry type.
+
+ ```mermaid
+ graph LR
+ A[flag: default off] -->|'added' / 'changed'| B(flag: default on)
+ B -->|'other'| C(remove flag, keep new code)
+ B -->|'removed' / 'changed'| D(remove flag, keep old code)
+ A -->|'added' / 'changed'| C
+ A -->|no changelog| D
+ ```
+
+- Any change behind a feature flag that is **enabled** by default **should** have a changelog entry.
+
## Feature flags in tests
Introducing a feature flag into the codebase creates an additional code path that should be tested.
diff --git a/doc/user/application_security/api_fuzzing/index.md b/doc/user/application_security/api_fuzzing/index.md
index a1193715018..bc6ce132566 100644
--- a/doc/user/application_security/api_fuzzing/index.md
+++ b/doc/user/application_security/api_fuzzing/index.md
@@ -569,7 +569,10 @@ To get you started quickly, GitLab provides the configuration file
[`gitlab-api-fuzzing-config.yml`](https://gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing/-/blob/master/gitlab-api-fuzzing-config.yml).
This file has several testing profiles that perform various numbers of tests. The run time of each
profile increases as the test numbers go up. To use a configuration file, add it to your
-repository's root as `.gitlab-api-fuzzing.yml`.
+repository as `.gitlab/gitlab-api-fuzzing-config.yml`.
+
+NOTE:
++In GitLab 13.11 and earlier, the configuration file was `.gitlab-api-fuzzing.yml` in the repository's root. In GitLab 13.12 and later, it is `.gitlab/gitlab-api-fuzzing-config.yml` in the repository's root.
| Profile | Fuzz Tests (per parameter) |
|:---------|:-----------|
diff --git a/doc/user/application_security/security_dashboard/img/security_center_dashboard_link_v12_4.png b/doc/user/application_security/security_dashboard/img/security_center_dashboard_link_v12_4.png
deleted file mode 100644
index e0e80810b08..00000000000
--- a/doc/user/application_security/security_dashboard/img/security_center_dashboard_link_v12_4.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/security_center_settings_v13_4.png b/doc/user/application_security/security_dashboard/img/security_center_settings_v13_4.png
index 4223494c294..74592e2cea5 100644
--- a/doc/user/application_security/security_dashboard/img/security_center_settings_v13_4.png
+++ b/doc/user/application_security/security_dashboard/img/security_center_settings_v13_4.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 6c03a6aea25..f0b3d895df5 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -150,10 +150,8 @@ the following:
![Security Center Dashboard with projects](img/security_center_dashboard_v13_4.png)
-You can access the Security Center from the menu
-bar at the top of the page. Under **More**, select **Security**.
-
-![Security Center navigation link](img/security_center_dashboard_link_v12_4.png)
+To view the Security Center, from the navigation bar at the top of the page, select
+**More > Security**.
### Adding projects to the Security Center
diff --git a/lib/sidebars/projects/menus/hidden_menu.rb b/lib/sidebars/projects/menus/hidden_menu.rb
new file mode 100644
index 00000000000..19c77c67e1a
--- /dev/null
+++ b/lib/sidebars/projects/menus/hidden_menu.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Sidebars
+ module Projects
+ module Menus
+ class HiddenMenu < ::Sidebars::Menu
+ override :configure_menu_items
+ def configure_menu_items
+ add_item(activity_menu_item)
+ add_item(graph_menu_item)
+ add_item(new_issue_menu_item)
+ add_item(jobs_menu_item)
+ add_item(commits_menu_item)
+ add_item(issue_boards_menu_item)
+
+ true
+ end
+
+ private
+
+ def activity_menu_item
+ ::Sidebars::MenuItem.new(
+ title: _('Activity'),
+ link: activity_project_path(context.project),
+ active_routes: {},
+ container_html_options: { class: 'shortcuts-project-activity' },
+ item_id: :activity
+ )
+ end
+
+ def graph_menu_item
+ return unless can?(context.current_user, :download_code, context.project)
+ return if context.project.empty_repo?
+
+ ::Sidebars::MenuItem.new(
+ title: _('Graph'),
+ link: project_network_path(context.project, context.current_ref),
+ active_routes: {},
+ container_html_options: { class: 'shortcuts-network' },
+ item_id: :graph
+ )
+ end
+
+ def new_issue_menu_item
+ return unless can?(context.current_user, :read_issue, context.project)
+
+ ::Sidebars::MenuItem.new(
+ title: _('Create a new issue'),
+ link: new_project_issue_path(context.project),
+ active_routes: {},
+ container_html_options: { class: 'shortcuts-new-issue' },
+ item_id: :new_issue
+ )
+ end
+
+ def jobs_menu_item
+ return unless can?(context.current_user, :read_build, context.project)
+
+ ::Sidebars::MenuItem.new(
+ title: _('Jobs'),
+ link: project_jobs_path(context.project),
+ active_routes: {},
+ container_html_options: { class: 'shortcuts-builds' },
+ item_id: :jobs
+ )
+ end
+
+ def commits_menu_item
+ return unless can?(context.current_user, :download_code, context.project)
+ return if context.project.empty_repo?
+
+ ::Sidebars::MenuItem.new(
+ title: _('Commits'),
+ link: project_commits_path(context.project),
+ active_routes: {},
+ container_html_options: { class: 'shortcuts-commits' },
+ item_id: :commits
+ )
+ end
+
+ def issue_boards_menu_item
+ return unless can?(context.current_user, :read_issue, context.project)
+
+ ::Sidebars::MenuItem.new(
+ title: _('Issue Boards'),
+ link: project_boards_path(context.project),
+ active_routes: {},
+ container_html_options: { class: 'shortcuts-issue-boards' },
+ item_id: :issue_boards
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/sidebars/projects/panel.rb b/lib/sidebars/projects/panel.rb
index dbf2977df01..7a35c224cbf 100644
--- a/lib/sidebars/projects/panel.rb
+++ b/lib/sidebars/projects/panel.rb
@@ -6,6 +6,7 @@ module Sidebars
override :configure_menus
def configure_menus
set_scope_menu(Sidebars::Projects::Menus::ScopeMenu.new(context))
+ set_hidden_menu(Sidebars::Projects::Menus::HiddenMenu.new(context))
add_menu(Sidebars::Projects::Menus::ProjectInformationMenu.new(context))
add_menu(Sidebars::Projects::Menus::LearnGitlabMenu.new(context))
@@ -27,11 +28,6 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::SettingsMenu.new(context))
end
- override :render_raw_menus_partial
- def render_raw_menus_partial
- 'layouts/nav/sidebar/project_menus'
- end
-
override :aria_label
def aria_label
_('Project navigation')
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 557cf5525b6..9b638aa1a63 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -10199,9 +10199,6 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
-msgid "DastProfiles|Please enter a valid timeout value"
-msgstr ""
-
msgid "DastProfiles|Profile name"
msgstr ""
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 34e24fe0a1c..09343b5704f 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -40,12 +40,6 @@ import {
jest.mock('~/flash');
-const expectNotImplemented = (action) => {
- it('is not implemented', () => {
- expect(action).toThrow(new Error('Not implemented!'));
- });
-};
-
// We need this helper to make sure projectPath is including
// subgroups when the movIssue action is called.
const getProjectPath = (path) => path.split('#')[0];
@@ -1825,27 +1819,3 @@ describe('unsetError', () => {
});
});
});
-
-describe('fetchBacklog', () => {
- expectNotImplemented(actions.fetchBacklog);
-});
-
-describe('bulkUpdateIssues', () => {
- expectNotImplemented(actions.bulkUpdateIssues);
-});
-
-describe('fetchIssue', () => {
- expectNotImplemented(actions.fetchIssue);
-});
-
-describe('toggleIssueSubscription', () => {
- expectNotImplemented(actions.toggleIssueSubscription);
-});
-
-describe('showPage', () => {
- expectNotImplemented(actions.showPage);
-});
-
-describe('toggleEmptyState', () => {
- expectNotImplemented(actions.toggleEmptyState);
-});
diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js
index af6d439e294..d89abcc79ae 100644
--- a/spec/frontend/boards/stores/mutations_spec.js
+++ b/spec/frontend/boards/stores/mutations_spec.js
@@ -13,12 +13,6 @@ import {
mockList,
} from '../mock_data';
-const expectNotImplemented = (action) => {
- it('is not implemented', () => {
- expect(action).toThrow(new Error('Not implemented!'));
- });
-};
-
describe('Board Store Mutations', () => {
let state;
@@ -158,10 +152,6 @@ describe('Board Store Mutations', () => {
});
});
- describe('REQUEST_ADD_LIST', () => {
- expectNotImplemented(mutations.REQUEST_ADD_LIST);
- });
-
describe('RECEIVE_ADD_LIST_SUCCESS', () => {
it('adds list to boardLists state', () => {
mutations.RECEIVE_ADD_LIST_SUCCESS(state, mockLists[0]);
@@ -172,10 +162,6 @@ describe('Board Store Mutations', () => {
});
});
- describe('RECEIVE_ADD_LIST_ERROR', () => {
- expectNotImplemented(mutations.RECEIVE_ADD_LIST_ERROR);
- });
-
describe('MOVE_LIST', () => {
it('updates boardLists state with reordered lists', () => {
state = {
@@ -341,10 +327,6 @@ describe('Board Store Mutations', () => {
});
});
- describe('REQUEST_ADD_ISSUE', () => {
- expectNotImplemented(mutations.REQUEST_ADD_ISSUE);
- });
-
describe('UPDATE_BOARD_ITEM_BY_ID', () => {
const issueId = '1';
const prop = 'id';
@@ -386,14 +368,6 @@ describe('Board Store Mutations', () => {
});
});
- describe('RECEIVE_ADD_ISSUE_SUCCESS', () => {
- expectNotImplemented(mutations.RECEIVE_ADD_ISSUE_SUCCESS);
- });
-
- describe('RECEIVE_ADD_ISSUE_ERROR', () => {
- expectNotImplemented(mutations.RECEIVE_ADD_ISSUE_ERROR);
- });
-
describe('MUTATE_ISSUE_SUCCESS', () => {
it('updates issue in issues state', () => {
const issues = {
@@ -434,18 +408,6 @@ describe('Board Store Mutations', () => {
});
});
- describe('REQUEST_UPDATE_ISSUE', () => {
- expectNotImplemented(mutations.REQUEST_UPDATE_ISSUE);
- });
-
- describe('RECEIVE_UPDATE_ISSUE_SUCCESS', () => {
- expectNotImplemented(mutations.RECEIVE_UPDATE_ISSUE_SUCCESS);
- });
-
- describe('RECEIVE_UPDATE_ISSUE_ERROR', () => {
- expectNotImplemented(mutations.RECEIVE_UPDATE_ISSUE_ERROR);
- });
-
describe('ADD_BOARD_ITEM_TO_LIST', () => {
beforeEach(() => {
setBoardsListsState();
@@ -540,14 +502,6 @@ describe('Board Store Mutations', () => {
});
});
- describe('SET_CURRENT_PAGE', () => {
- expectNotImplemented(mutations.SET_CURRENT_PAGE);
- });
-
- describe('TOGGLE_EMPTY_STATE', () => {
- expectNotImplemented(mutations.TOGGLE_EMPTY_STATE);
- });
-
describe('REQUEST_GROUP_PROJECTS', () => {
it('Should set isLoading in groupProjectsFlags to true in state when fetchNext is false', () => {
mutations[types.REQUEST_GROUP_PROJECTS](state, false);
diff --git a/spec/frontend/vue_shared/directives/validation_spec.js b/spec/frontend/vue_shared/directives/validation_spec.js
index 2764a71d204..0c84be9354f 100644
--- a/spec/frontend/vue_shared/directives/validation_spec.js
+++ b/spec/frontend/vue_shared/directives/validation_spec.js
@@ -4,12 +4,18 @@ import validation from '~/vue_shared/directives/validation';
describe('validation directive', () => {
let wrapper;
- const createComponent = ({ inputAttributes, showValidation } = {}) => {
+ const createComponent = ({ inputAttributes, showValidation, template } = {}) => {
const defaultInputAttributes = {
type: 'text',
required: true,
};
+ const defaultTemplate = `
+ <form>
+ <input v-validation:[showValidation] name="exampleField" v-bind="attributes" />
+ </form>
+ `;
+
const component = {
directives: {
validation: validation(),
@@ -29,11 +35,7 @@ describe('validation directive', () => {
},
};
},
- template: `
- <form>
- <input v-validation:[showValidation] name="exampleField" v-bind="attributes" />
- </form>
- `,
+ template: template || defaultTemplate,
};
wrapper = shallowMount(component, { attachTo: document.body });
@@ -48,6 +50,12 @@ describe('validation directive', () => {
const findForm = () => wrapper.find('form');
const findInput = () => wrapper.find('input');
+ const setValueAndTriggerValidation = (value) => {
+ const input = findInput();
+ input.setValue(value);
+ input.trigger('blur');
+ };
+
describe.each([true, false])(
'with fields untouched and "showValidation" set to "%s"',
(showValidation) => {
@@ -78,12 +86,6 @@ describe('validation directive', () => {
`(
'with input-attributes set to $inputAttributes',
({ inputAttributes, validValue, invalidValue }) => {
- const setValueAndTriggerValidation = (value) => {
- const input = findInput();
- input.setValue(value);
- input.trigger('blur');
- };
-
beforeEach(() => {
createComponent({ inputAttributes });
});
@@ -129,4 +131,52 @@ describe('validation directive', () => {
});
},
);
+
+ describe('with group elements', () => {
+ const template = `
+ <form>
+ <div v-validation:[showValidation]>
+ <input name="exampleField" v-bind="attributes" />
+ </div>
+ </form>
+ `;
+ beforeEach(() => {
+ createComponent({
+ template,
+ inputAttributes: {
+ required: true,
+ },
+ });
+ });
+
+ describe('with invalid value', () => {
+ beforeEach(() => {
+ setValueAndTriggerValidation('');
+ });
+
+ it('should set correct field state', () => {
+ expect(getFormData().fields.exampleField).toEqual({
+ state: false,
+ feedback: expect.any(String),
+ });
+ });
+
+ it('should set correct feedback', () => {
+ expect(getFormData().fields.exampleField.feedback).toBe('Please fill out this field.');
+ });
+ });
+
+ describe('with valid value', () => {
+ beforeEach(() => {
+ setValueAndTriggerValidation('hello');
+ });
+
+ it('set the correct state', () => {
+ expect(getFormData().fields.exampleField).toEqual({
+ state: true,
+ feedback: '',
+ });
+ });
+ });
+ });
});
diff --git a/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb b/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb
new file mode 100644
index 00000000000..f7cb0adbb66
--- /dev/null
+++ b/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Projects::Menus::HiddenMenu do
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:user) { project.owner }
+ let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, current_ref: project.repository.root_ref) }
+
+ describe '#render?' do
+ subject { described_class.new(context) }
+
+ context 'when menu does not have any menu items' do
+ it 'returns false' do
+ allow(subject).to receive(:has_items?).and_return(false)
+
+ expect(subject.render?).to be false
+ end
+ end
+
+ context 'when menu has menu items' do
+ it 'returns true' do
+ expect(subject.render?).to be true
+ end
+ end
+ end
+
+ describe 'Menu items' do
+ subject { described_class.new(context).items.index { |e| e.item_id == item_id } }
+
+ shared_examples 'access rights checks' do
+ specify { is_expected.not_to be_nil }
+
+ describe 'when the user does not have access' do
+ let(:user) { nil }
+
+ specify { is_expected.to be_nil }
+ end
+ end
+
+ describe 'Activity' do
+ let(:item_id) { :activity }
+
+ context 'when user has access to the project' do
+ specify { is_expected.not_to be_nil }
+
+ describe 'when the user is not present' do
+ let(:user) { nil }
+
+ specify { is_expected.not_to be_nil }
+ end
+ end
+ end
+
+ describe 'Graph' do
+ let(:item_id) { :graph }
+
+ context 'when project repository is empty' do
+ before do
+ allow(project).to receive(:empty_repo?).and_return(true)
+ end
+
+ specify { is_expected.to be_nil }
+ end
+
+ it_behaves_like 'access rights checks'
+ end
+
+ describe 'New Issue' do
+ let(:item_id) { :new_issue }
+
+ it_behaves_like 'access rights checks'
+ end
+
+ describe 'Jobs' do
+ let(:item_id) { :jobs }
+
+ it_behaves_like 'access rights checks'
+ end
+
+ describe 'Commits' do
+ let(:item_id) { :commits }
+
+ context 'when project repository is empty' do
+ before do
+ allow(project).to receive(:empty_repo?).and_return(true)
+ end
+
+ specify { is_expected.to be_nil }
+ end
+
+ it_behaves_like 'access rights checks'
+ end
+
+ describe 'Issue Boards' do
+ let(:item_id) { :issue_boards }
+
+ it_behaves_like 'access rights checks'
+ end
+ end
+end
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 11bb81bcd40..fafd5777f93 100644
--- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
@@ -1090,5 +1090,43 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
+ describe 'Hidden menus' do
+ it 'has a link to the Activity page' do
+ render
+
+ expect(rendered).to have_link('Activity', href: activity_project_path(project), class: 'shortcuts-project-activity', visible: false)
+ end
+
+ it 'has a link to the Graph page' do
+ render
+
+ expect(rendered).to have_link('Graph', href: project_network_path(project, current_ref), class: 'shortcuts-network', visible: false)
+ end
+
+ it 'has a link to the New Issue page' do
+ render
+
+ expect(rendered).to have_link('Create a new issue', href: new_project_issue_path(project), class: 'shortcuts-new-issue', visible: false)
+ end
+
+ it 'has a link to the Jobs page' do
+ render
+
+ expect(rendered).to have_link('Jobs', href: project_jobs_path(project), class: 'shortcuts-builds', visible: false)
+ end
+
+ it 'has a link to the Commits page' do
+ render
+
+ expect(rendered).to have_link('Commits', href: project_commits_path(project), class: 'shortcuts-commits', visible: false)
+ end
+
+ it 'has a link to the Issue Boards page' do
+ render
+
+ expect(rendered).to have_link('Issue Boards', href: project_boards_path(project), class: 'shortcuts-issue-boards', visible: false)
+ end
+ end
+
it_behaves_like 'sidebar includes snowplow attributes', 'render', 'projects_side_navigation', 'projects_side_navigation'
end