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-02-12 12:08:48 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-12 12:08:48 +0300
commit7f5e08060f261a63ebf5058a95419da66928173a (patch)
treeb2f77e0445bb28e4eaf156b3d3b71bd46dac8951
parent22864cafe7a3509342c3c880881ade40ce06f752 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_manual_todo.yml8
-rw-r--r--app/assets/javascripts/pages/search/show/refresh_counts.js24
-rw-r--r--app/assets/javascripts/search/index.js2
-rw-r--r--app/assets/javascripts/search/store/actions.js56
-rw-r--r--app/assets/javascripts/search/store/mutation_types.js3
-rw-r--r--app/assets/javascripts/search/store/mutations.js11
-rw-r--r--app/assets/javascripts/search/store/state.js1
-rw-r--r--app/assets/javascripts/search/topbar/components/app.vue66
-rw-r--r--app/assets/javascripts/search/topbar/components/scope_tabs.vue73
-rw-r--r--app/assets/javascripts/search/topbar/constants.js14
-rw-r--r--app/assets/javascripts/search/topbar/index.js6
-rw-r--r--app/assets/stylesheets/pages/search.scss12
-rw-r--r--app/controllers/concerns/boards_actions.rb14
-rw-r--r--app/controllers/concerns/multiple_boards_actions.rb1
-rw-r--r--app/controllers/groups/boards_controller.rb23
-rw-r--r--app/controllers/projects/boards_controller.rb22
-rw-r--r--app/graphql/resolvers/board_resolver.rb2
-rw-r--r--app/graphql/resolvers/boards_resolver.rb2
-rw-r--r--app/helpers/boards_helper.rb3
-rw-r--r--app/helpers/projects_helper.rb3
-rw-r--r--app/helpers/search_helper.rb42
-rw-r--r--app/models/board.rb8
-rw-r--r--app/presenters/ci/pipeline_presenter.rb3
-rw-r--r--app/services/boards/list_service.rb8
-rw-r--r--app/services/boards/visits/create_service.rb1
-rw-r--r--app/views/search/_category.html.haml35
-rw-r--r--app/views/search/show.html.haml3
-rw-r--r--app/views/shared/boards/_show.html.haml5
-rw-r--r--changelogs/unreleased/262066-global-search-scope-tabs-vue-component.yml5
-rw-r--r--changelogs/unreleased/jimcser-openapi-readme.yml5
-rw-r--r--changelogs/unreleased/test_update_merge_request_worker_performance.yml5
-rw-r--r--doc/api/openapi/img/apiviewer01-fs8.pngbin0 -> 79594 bytes
-rw-r--r--doc/api/openapi/img/apiviewer03-fs8.pngbin0 -> 72812 bytes
-rw-r--r--doc/api/openapi/img/apiviewer04-fs8.pngbin0 -> 77404 bytes
-rw-r--r--doc/api/openapi/openapi_interactive.md42
-rw-r--r--doc/development/architecture.md81
-rw-r--r--lib/gitlab/git/push.rb4
-rw-r--r--locale/gitlab.pot6
-rw-r--r--package.json8
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Rakefile2
-rwxr-xr-xqa/bin/qa1
-rwxr-xr-xqa/bin/rubymine1
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Gemfile2
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Rakefile2
-rw-r--r--qa/qa/fixtures/auto_devops_rack/config.ru2
-rw-r--r--qa/qa/page/page_concern.rb2
-rw-r--r--qa/qa/page/search/results.rb2
-rw-r--r--spec/features/global_search_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb15
-rw-r--r--spec/features/search/user_searches_for_comments_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_users_spec.rb4
-rw-r--r--spec/features/snippets/search_snippets_spec.rb2
-rw-r--r--spec/frontend/pages/search/show/__snapshots__/refresh_counts_spec.js.snap7
-rw-r--r--spec/frontend/pages/search/show/refresh_counts_spec.js38
-rw-r--r--spec/frontend/search/mock_data.js25
-rw-r--r--spec/frontend/search/store/actions_spec.js44
-rw-r--r--spec/frontend/search/store/mutations_spec.js36
-rw-r--r--spec/frontend/search/topbar/components/app_spec.js14
-rw-r--r--spec/frontend/search/topbar/components/scope_tabs_spec.js122
-rw-r--r--spec/helpers/search_helper_spec.rb139
-rw-r--r--spec/lib/gitlab/git/push_spec.rb10
-rw-r--r--spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb28
-rw-r--r--spec/views/search/show.html.haml_spec.rb3
-rw-r--r--yarn.lock84
65 files changed, 500 insertions, 698 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index 57c17da243f..96437f47758 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -2497,14 +2497,6 @@ Style/FrozenStringLiteralComment:
- 'bin/secpick'
- 'danger/changes_size/Dangerfile'
- 'danger/metadata/Dangerfile'
- - 'qa/Gemfile'
- - 'qa/Rakefile'
- - 'qa/bin/qa'
- - 'qa/bin/rubymine'
- - 'qa/qa/fixtures/auto_devops_rack/Gemfile'
- - 'qa/qa/fixtures/auto_devops_rack/Rakefile'
- - 'qa/qa/fixtures/auto_devops_rack/config.ru'
- - 'qa/qa/page/page_concern.rb'
- 'scripts/flaky_examples/detect-new-flaky-examples'
- 'scripts/flaky_examples/prune-old-flaky-examples'
- 'scripts/gather-test-memory-data'
diff --git a/app/assets/javascripts/pages/search/show/refresh_counts.js b/app/assets/javascripts/pages/search/show/refresh_counts.js
new file mode 100644
index 00000000000..f3f6312cb7c
--- /dev/null
+++ b/app/assets/javascripts/pages/search/show/refresh_counts.js
@@ -0,0 +1,24 @@
+import axios from '~/lib/utils/axios_utils';
+
+function showCount(el, count) {
+ el.textContent = count;
+ el.classList.remove('hidden');
+}
+
+function refreshCount(el) {
+ const { url } = el.dataset;
+
+ return axios
+ .get(url)
+ .then(({ data }) => showCount(el, data.count))
+ .catch((e) => {
+ // eslint-disable-next-line no-console
+ console.error(`Failed to fetch search count from '${url}'.`, e);
+ });
+}
+
+export default function refreshCounts() {
+ const elements = Array.from(document.querySelectorAll('.js-search-count'));
+
+ return Promise.all(elements.map(refreshCount));
+}
diff --git a/app/assets/javascripts/search/index.js b/app/assets/javascripts/search/index.js
index 0c5d68ab96b..3050b628cd5 100644
--- a/app/assets/javascripts/search/index.js
+++ b/app/assets/javascripts/search/index.js
@@ -1,5 +1,6 @@
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
import Project from '~/pages/projects/project';
+import refreshCounts from '~/pages/search/show/refresh_counts';
import { queryToObject } from '~/lib/utils/url_utility';
import createStore from './store';
import { initTopbar } from './topbar';
@@ -19,5 +20,6 @@ export const initSearchApp = () => {
initSearchSort(store);
setHighlightClass(query.search); // Code Highlighting
+ refreshCounts(); // Other Scope Tab Counts
Project.initRefSwitcher(); // Code Search Branch Picker
};
diff --git a/app/assets/javascripts/search/store/actions.js b/app/assets/javascripts/search/store/actions.js
index e1722d8dee3..bdfe966d990 100644
--- a/app/assets/javascripts/search/store/actions.js
+++ b/app/assets/javascripts/search/store/actions.js
@@ -1,30 +1,9 @@
-import axios from '~/lib/utils/axios_utils';
import Api from '~/api';
import createFlash from '~/flash';
import { __ } from '~/locale';
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import * as types from './mutation_types';
-/* private */
-const getCount = ({ params, state, activeCount }) => {
- const globalSearchCountsPath = '/search/count';
- const url = Api.buildUrl(globalSearchCountsPath);
-
- // count is known for active tab, so return it and skip the Api call
- if (params.scope === state.query?.scope) {
- return { scope: params.scope, count: activeCount };
- }
-
- return axios
- .get(url, { params })
- .then(({ data }) => {
- return { scope: params.scope, count: data.count };
- })
- .catch((e) => {
- throw e;
- });
-};
-
export const fetchGroups = ({ commit }, search) => {
commit(types.REQUEST_GROUPS);
Api.groups(search)
@@ -59,21 +38,6 @@ export const fetchProjects = ({ commit, state }, search) => {
}
};
-export const fetchSearchCounts = ({ commit, state }, { scopeTabs, activeCount }) => {
- commit(types.REQUEST_SEARCH_COUNTS, { scopeTabs, activeCount });
- const promises = scopeTabs.map((scope) =>
- getCount({ params: { ...state.query, scope }, state, activeCount }),
- );
-
- Promise.all(promises)
- .then((data) => {
- commit(types.RECEIVE_SEARCH_COUNTS_SUCCESS, data);
- })
- .catch(() => {
- createFlash({ message: __('There was an error fetching the Search Counts') });
- });
-};
-
export const setQuery = ({ commit }, { key, value }) => {
commit(types.SET_QUERY, { key, value });
};
@@ -82,22 +46,6 @@ export const applyQuery = ({ state }) => {
visitUrl(setUrlParams({ ...state.query, page: null }));
};
-export const resetQuery = ({ state }, snippets = false) => {
- let defaultQuery = {
- page: null,
- state: null,
- confidential: null,
- nav_source: null,
- };
-
- if (snippets) {
- defaultQuery = {
- snippets: true,
- group_id: null,
- project_id: null,
- ...defaultQuery,
- };
- }
-
- visitUrl(setUrlParams({ ...state.query, ...defaultQuery }));
+export const resetQuery = ({ state }) => {
+ visitUrl(setUrlParams({ ...state.query, page: null, state: null, confidential: null }));
};
diff --git a/app/assets/javascripts/search/store/mutation_types.js b/app/assets/javascripts/search/store/mutation_types.js
index 5ba3845bfcc..a6430b53c4f 100644
--- a/app/assets/javascripts/search/store/mutation_types.js
+++ b/app/assets/javascripts/search/store/mutation_types.js
@@ -6,7 +6,4 @@ export const REQUEST_PROJECTS = 'REQUEST_PROJECTS';
export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR';
-export const REQUEST_SEARCH_COUNTS = 'REQUEST_SEARCH_COUNTS';
-export const RECEIVE_SEARCH_COUNTS_SUCCESS = 'RECEIVE_SEARCH_COUNTS_SUCCESS';
-
export const SET_QUERY = 'SET_QUERY';
diff --git a/app/assets/javascripts/search/store/mutations.js b/app/assets/javascripts/search/store/mutations.js
index 57a577060b3..91d7cf66c8f 100644
--- a/app/assets/javascripts/search/store/mutations.js
+++ b/app/assets/javascripts/search/store/mutations.js
@@ -1,4 +1,3 @@
-import { ALL_SCOPE_TABS } from '~/search/topbar/constants';
import * as types from './mutation_types';
export default {
@@ -24,16 +23,6 @@ export default {
state.fetchingProjects = false;
state.projects = [];
},
- [types.REQUEST_SEARCH_COUNTS](state, { scopeTabs, activeCount }) {
- state.inflatedScopeTabs = scopeTabs.map((tab) => {
- return { ...ALL_SCOPE_TABS[tab], count: tab === state.query?.scope ? activeCount : '' };
- });
- },
- [types.RECEIVE_SEARCH_COUNTS_SUCCESS](state, data) {
- state.inflatedScopeTabs = data.map((tab) => {
- return { ...ALL_SCOPE_TABS[tab.scope], count: tab.count };
- });
- },
[types.SET_QUERY](state, { key, value }) {
state.query[key] = value;
},
diff --git a/app/assets/javascripts/search/store/state.js b/app/assets/javascripts/search/store/state.js
index 9528b0400a4..9a0d61d0b93 100644
--- a/app/assets/javascripts/search/store/state.js
+++ b/app/assets/javascripts/search/store/state.js
@@ -4,6 +4,5 @@ const createState = ({ query }) => ({
fetchingGroups: false,
projects: [],
fetchingProjects: false,
- inflatedScopeTabs: [],
});
export default createState;
diff --git a/app/assets/javascripts/search/topbar/components/app.vue b/app/assets/javascripts/search/topbar/components/app.vue
index c858f1fcc42..639cff591c3 100644
--- a/app/assets/javascripts/search/topbar/components/app.vue
+++ b/app/assets/javascripts/search/topbar/components/app.vue
@@ -3,7 +3,6 @@ import { mapState, mapActions } from 'vuex';
import { GlForm, GlSearchBoxByType, GlButton } from '@gitlab/ui';
import GroupFilter from './group_filter.vue';
import ProjectFilter from './project_filter.vue';
-import ScopeTabs from './scope_tabs.vue';
export default {
name: 'GlobalSearchTopbar',
@@ -13,7 +12,6 @@ export default {
GroupFilter,
ProjectFilter,
GlButton,
- ScopeTabs,
},
props: {
groupInitialData: {
@@ -26,16 +24,6 @@ export default {
required: false,
default: () => ({}),
},
- scopeTabs: {
- type: Array,
- required: false,
- default: () => [],
- },
- count: {
- type: String,
- required: false,
- default: '',
- },
},
computed: {
...mapState(['query']),
@@ -50,9 +38,6 @@ export default {
showFilters() {
return !this.query.snippets || this.query.snippets === 'false';
},
- showScopeTabs() {
- return this.query.search;
- },
},
methods: {
...mapActions(['applyQuery', 'setQuery']),
@@ -61,31 +46,28 @@ export default {
</script>
<template>
- <section>
- <gl-form class="search-page-form" @submit.prevent="applyQuery">
- <section class="gl-lg-display-flex gl-align-items-flex-end">
- <div class="gl-flex-fill-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
- <label>{{ __('What are you searching for?') }}</label>
- <gl-search-box-by-type
- id="dashboard_search"
- v-model="search"
- name="search"
- :placeholder="__(`Search for projects, issues, etc.`)"
- />
- </div>
- <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
- <label class="gl-display-block">{{ __('Group') }}</label>
- <group-filter :initial-data="groupInitialData" />
- </div>
- <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
- <label class="gl-display-block">{{ __('Project') }}</label>
- <project-filter :initial-data="projectInitialData" />
- </div>
- <gl-button class="btn-search gl-lg-ml-2" variant="success" type="submit">{{
- __('Search')
- }}</gl-button>
- </section>
- </gl-form>
- <scope-tabs v-if="showScopeTabs" :scope-tabs="scopeTabs" :count="count" />
- </section>
+ <gl-form class="search-page-form" @submit.prevent="applyQuery">
+ <section class="gl-lg-display-flex gl-align-items-flex-end">
+ <div class="gl-flex-fill-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
+ <label>{{ __('What are you searching for?') }}</label>
+ <gl-search-box-by-type
+ id="dashboard_search"
+ v-model="search"
+ name="search"
+ :placeholder="__(`Search for projects, issues, etc.`)"
+ />
+ </div>
+ <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
+ <label class="gl-display-block">{{ __('Group') }}</label>
+ <group-filter :initial-data="groupInitialData" />
+ </div>
+ <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
+ <label class="gl-display-block">{{ __('Project') }}</label>
+ <project-filter :initial-data="projectInitialData" />
+ </div>
+ <gl-button class="btn-search gl-lg-ml-2" variant="success" type="submit">{{
+ __('Search')
+ }}</gl-button>
+ </section>
+ </gl-form>
</template>
diff --git a/app/assets/javascripts/search/topbar/components/scope_tabs.vue b/app/assets/javascripts/search/topbar/components/scope_tabs.vue
deleted file mode 100644
index 83264a1b411..00000000000
--- a/app/assets/javascripts/search/topbar/components/scope_tabs.vue
+++ /dev/null
@@ -1,73 +0,0 @@
-<script>
-import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-
-export default {
- name: 'ScopeTabs',
- components: {
- GlTabs,
- GlTab,
- GlBadge,
- },
- props: {
- scopeTabs: {
- type: Array,
- required: true,
- },
- count: {
- type: String,
- required: false,
- default: '',
- },
- },
- computed: {
- ...mapState(['query', 'inflatedScopeTabs']),
- },
- created() {
- this.fetchSearchCounts({ scopeTabs: this.scopeTabs, activeCount: this.count });
- },
- methods: {
- ...mapActions(['fetchSearchCounts', 'setQuery', 'resetQuery']),
- handleTabChange(scope) {
- this.setQuery({ key: 'scope', value: scope });
- this.resetQuery(scope === 'snippet_titles');
- },
- isTabActive(scope) {
- return scope === this.query.scope;
- },
- },
-};
-</script>
-
-<template>
- <div>
- <gl-tabs
- content-class="gl-p-0"
- nav-class="search-filter search-nav-tabs gl-display-flex gl-overflow-x-auto"
- >
- <gl-tab
- v-for="tab in inflatedScopeTabs"
- :key="tab.scope"
- class="gl-display-flex"
- :active="isTabActive(tab.scope)"
- :data-testid="`tab-${tab.scope}`"
- :title-link-attributes="{ 'data-qa-selector': tab.qaSelector }"
- title-link-class="gl-white-space-nowrap"
- @click="handleTabChange(tab.scope)"
- >
- <template #title>
- <span data-testid="tab-title"> {{ tab.title }} </span>
- <gl-badge
- v-show="tab.count"
- :data-scope="tab.scope"
- :data-testid="`badge-${tab.scope}`"
- :variant="isTabActive(tab.scope) ? 'neutral' : 'muted'"
- size="sm"
- >
- {{ tab.count }}
- </gl-badge>
- </template>
- </gl-tab>
- </gl-tabs>
- </div>
-</template>
diff --git a/app/assets/javascripts/search/topbar/constants.js b/app/assets/javascripts/search/topbar/constants.js
index ec65667cde3..3944b2c8374 100644
--- a/app/assets/javascripts/search/topbar/constants.js
+++ b/app/assets/javascripts/search/topbar/constants.js
@@ -19,17 +19,3 @@ export const PROJECT_DATA = {
selectedDisplayValue: 'name_with_namespace',
itemsDisplayValue: 'name_with_namespace',
};
-
-export const ALL_SCOPE_TABS = {
- blobs: { scope: 'blobs', title: __('Code'), qaSelector: 'code_tab' },
- issues: { scope: 'issues', title: __('Issues') },
- merge_requests: { scope: 'merge_requests', title: __('Merge requests') },
- milestones: { scope: 'milestones', title: __('Milestones') },
- notes: { scope: 'notes', title: __('Comments') },
- wiki_blobs: { scope: 'wiki_blobs', title: __('Wiki') },
- commits: { scope: 'commits', title: __('Commits') },
- epics: { scope: 'epics', title: __('Epics') },
- users: { scope: 'users', title: __('Users') },
- snippet_titles: { scope: 'snippet_titles', title: __('Titles and Descriptions') },
- projects: { scope: 'projects', title: __('Projects'), qaSelector: 'projects_tab' },
-};
diff --git a/app/assets/javascripts/search/topbar/index.js b/app/assets/javascripts/search/topbar/index.js
index f9564b5cfbb..87316e10e8d 100644
--- a/app/assets/javascripts/search/topbar/index.js
+++ b/app/assets/javascripts/search/topbar/index.js
@@ -11,12 +11,10 @@ export const initTopbar = (store) => {
return false;
}
- let { groupInitialData, projectInitialData, scopeTabs } = el.dataset;
- const { count } = el.dataset;
+ let { groupInitialData, projectInitialData } = el.dataset;
groupInitialData = JSON.parse(groupInitialData);
projectInitialData = JSON.parse(projectInitialData);
- scopeTabs = JSON.parse(scopeTabs);
return new Vue({
el,
@@ -26,8 +24,6 @@ export const initTopbar = (store) => {
props: {
groupInitialData,
projectInitialData,
- scopeTabs,
- count,
},
});
},
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index a1961d24bbf..cd99c667001 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -2,7 +2,6 @@ $search-dropdown-max-height: 400px;
$search-avatar-size: 16px;
$search-sidebar-min-width: 240px;
$search-sidebar-max-width: 300px;
-$search-topbar-min-height: 111px;
.search-results {
.search-result-row {
@@ -20,12 +19,6 @@ $search-topbar-min-height: 111px;
}
}
-.search-topbar {
- @include media-breakpoint-up(md) {
- min-height: $search-topbar-min-height;
- }
-}
-
.search-sidebar {
@include media-breakpoint-up(md) {
min-width: $search-sidebar-min-width;
@@ -33,11 +26,6 @@ $search-topbar-min-height: 111px;
}
}
-.search-nav-tabs {
- overflow-y: hidden;
- flex-wrap: nowrap;
-}
-
.search form:hover,
.file-finder-input:hover,
.issuable-search-form:hover,
diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb
index b382e338a78..79e6f027c2f 100644
--- a/app/controllers/concerns/boards_actions.rb
+++ b/app/controllers/concerns/boards_actions.rb
@@ -34,16 +34,26 @@ module BoardsActions
def boards
strong_memoize(:boards) do
- Boards::ListService.new(parent, current_user).execute
+ existing_boards = boards_finder.execute
+ if existing_boards.any?
+ existing_boards
+ else
+ # if no board exists, create one
+ [board_create_service.execute.payload]
+ end
end
end
def board
strong_memoize(:board) do
- boards.find(params[:id])
+ board_finder.execute.first
end
end
+ def board_type
+ board_klass.to_type
+ end
+
def serializer
BoardSerializer.new(current_user: current_user)
end
diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb
index 370b8c72bfe..5206f5759d8 100644
--- a/app/controllers/concerns/multiple_boards_actions.rb
+++ b/app/controllers/concerns/multiple_boards_actions.rb
@@ -65,6 +65,7 @@ module MultipleBoardsActions
private
def redirect_to_recent_board
+ return unless board_type == Board.to_type
return if request.format.json? || !parent.multiple_issue_boards_available? || !latest_visited_board
redirect_to board_path(latest_visited_board.board)
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index b34b826a695..fa109021b7d 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -3,6 +3,7 @@
class Groups::BoardsController < Groups::ApplicationController
include BoardsActions
include RecordUserLastActivity
+ include Gitlab::Utils::StrongMemoize
before_action :authorize_read_board!, only: [:index, :show]
before_action :assign_endpoint_vars
@@ -14,6 +15,28 @@ class Groups::BoardsController < Groups::ApplicationController
private
+ def board_klass
+ Board
+ end
+
+ def boards_finder
+ strong_memoize :boards_finder do
+ Boards::ListService.new(parent, current_user)
+ end
+ end
+
+ def board_finder
+ strong_memoize :board_finder do
+ Boards::ListService.new(parent, current_user, board_id: params[:id])
+ end
+ end
+
+ def board_create_service
+ strong_memoize :board_create_service do
+ Boards::CreateService.new(parent, current_user)
+ end
+ end
+
def assign_endpoint_vars
@boards_endpoint = group_boards_path(group)
@namespace_path = group.to_param
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 51c9bf3699a..d2e5d319f96 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -15,6 +15,28 @@ class Projects::BoardsController < Projects::ApplicationController
private
+ def board_klass
+ Board
+ end
+
+ def boards_finder
+ strong_memoize :boards_finder do
+ Boards::ListService.new(parent, current_user)
+ end
+ end
+
+ def board_finder
+ strong_memoize :board_finder do
+ Boards::ListService.new(parent, current_user, board_id: params[:id])
+ end
+ end
+
+ def board_create_service
+ strong_memoize :board_create_service do
+ Boards::CreateService.new(parent, current_user)
+ end
+ end
+
def assign_endpoint_vars
@boards_endpoint = project_boards_path(project)
@bulk_issues_path = bulk_update_project_issues_path(project)
diff --git a/app/graphql/resolvers/board_resolver.rb b/app/graphql/resolvers/board_resolver.rb
index 582707cc1e4..2c2922c3fbf 100644
--- a/app/graphql/resolvers/board_resolver.rb
+++ b/app/graphql/resolvers/board_resolver.rb
@@ -13,7 +13,7 @@ module Resolvers
def resolve(id: nil)
return unless parent
- ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false).first
+ ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute.first
rescue ActiveRecord::RecordNotFound
nil
end
diff --git a/app/graphql/resolvers/boards_resolver.rb b/app/graphql/resolvers/boards_resolver.rb
index cdb15dc8f37..be2f22175dc 100644
--- a/app/graphql/resolvers/boards_resolver.rb
+++ b/app/graphql/resolvers/boards_resolver.rb
@@ -16,7 +16,7 @@ module Resolvers
return Board.none unless parent
- ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false)
+ ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute
rescue ActiveRecord::RecordNotFound
Board.none
end
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index c827fb4dd95..80b6c62cb0b 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -21,7 +21,8 @@ module BoardsHelper
group_id: @group&.id,
labels_filter_base_path: build_issue_link_base,
labels_fetch_path: labels_fetch_path,
- labels_manage_path: labels_manage_path
+ labels_manage_path: labels_manage_path,
+ board_type: board.to_type
}
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 0d43032a184..a2e9952f350 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -511,8 +511,7 @@ module ProjectsHelper
commits: :download_code,
merge_requests: :read_merge_request,
notes: [:read_merge_request, :download_code, :read_issue, :read_snippet],
- members: :read_project_member,
- wiki_blobs: :read_wiki
+ members: :read_project_member
)
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 1a5b578cc75..a7acc0cd7db 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -1,8 +1,16 @@
# frozen_string_literal: true
module SearchHelper
- PROJECT_SEARCH_TABS = %i{blobs issues merge_requests milestones notes wiki_blobs commits}.freeze
- BASIC_SEARCH_TABS = %i{projects issues merge_requests milestones}.freeze
+ SEARCH_GENERIC_PARAMS = [
+ :search,
+ :scope,
+ :project_id,
+ :group_id,
+ :repository_ref,
+ :snippets,
+ :sort,
+ :force_search_results
+ ].freeze
def search_autocomplete_opts(term)
return unless current_user
@@ -284,19 +292,27 @@ module SearchHelper
Sanitize.clean(str)
end
- def search_nav_tabs
- return [:snippet_titles] if !@project && @show_snippets
+ def search_filter_link(scope, label, data: {}, search: {})
+ search_params = params
+ .merge(search)
+ .merge({ scope: scope })
+ .permit(SEARCH_GENERIC_PARAMS)
- tabs =
- if @project
- PROJECT_SEARCH_TABS.select { |tab| project_search_tabs?(tab) }
- else
- BASIC_SEARCH_TABS.dup
- end
-
- tabs << :users if show_user_search_tab?
+ if @scope == scope
+ li_class = 'active'
+ count = @search_results.formatted_count(scope)
+ else
+ badge_class = 'js-search-count hidden'
+ badge_data = { url: search_count_path(search_params) }
+ end
- tabs
+ content_tag :li, class: li_class, data: data do
+ link_to search_path(search_params) do
+ concat label
+ concat ' '
+ concat content_tag(:span, count, class: ['badge badge-pill', badge_class], data: badge_data)
+ end
+ end
end
def search_filter_input_options(type, placeholder = _('Search or filter results...'))
diff --git a/app/models/board.rb b/app/models/board.rb
index a57d101b30a..85fad762ebe 100644
--- a/app/models/board.rb
+++ b/app/models/board.rb
@@ -44,6 +44,14 @@ class Board < ApplicationRecord
def scoped?
false
end
+
+ def self.to_type
+ name.demodulize
+ end
+
+ def to_type
+ self.class.to_type
+ end
end
Board.prepend_if_ee('EE::Board')
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index f3bb63b31c3..0c68c33cbbe 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -11,6 +11,9 @@ module Ci
{ unknown_failure: 'Unknown pipeline failure!',
config_error: 'CI/CD YAML configuration error!',
external_validation_failure: 'External pipeline validation failed!',
+ activity_limit_exceeded: 'Pipeline activity limit exceeded!',
+ size_limit_exceeded: 'Pipeline size limit exceeded!',
+ job_activity_limit_exceeded: 'Pipeline job activity limit exceeded!',
deployments_limit_exceeded: 'Pipeline deployments limit exceeded!' }
end
diff --git a/app/services/boards/list_service.rb b/app/services/boards/list_service.rb
index 729bca6580e..80ceb91f56d 100644
--- a/app/services/boards/list_service.rb
+++ b/app/services/boards/list_service.rb
@@ -2,9 +2,7 @@
module Boards
class ListService < Boards::BaseService
- def execute(create_default_board: true)
- create_board! if create_default_board && parent.boards.empty?
-
+ def execute
find_boards
end
@@ -18,10 +16,6 @@ module Boards
parent.boards.first_board
end
- def create_board!
- Boards::CreateService.new(parent, current_user).execute
- end
-
def find_boards
found =
if parent.multiple_issue_boards_available?
diff --git a/app/services/boards/visits/create_service.rb b/app/services/boards/visits/create_service.rb
index e2adf755511..428ed1a8bcc 100644
--- a/app/services/boards/visits/create_service.rb
+++ b/app/services/boards/visits/create_service.rb
@@ -5,6 +5,7 @@ module Boards
class CreateService < Boards::BaseService
def execute(board)
return unless current_user && Gitlab::Database.read_write?
+ return unless board.is_a?(Board) # other board types do not support board visits yet
if parent.is_a?(Group)
BoardGroupRecentVisit.visited!(current_user, board)
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
new file mode 100644
index 00000000000..f094a6f5e3b
--- /dev/null
+++ b/app/views/search/_category.html.haml
@@ -0,0 +1,35 @@
+- users = capture_haml do
+ - if show_user_search_tab?
+ = search_filter_link 'users', _("Users")
+
+.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
+ .fade-left= sprite_icon('chevron-lg-left', size: 12)
+ .fade-right= sprite_icon('chevron-lg-right', size: 12)
+ %ul.nav-links.search-filter.scrolling-tabs.nav.nav-tabs
+ - if @project
+ - if project_search_tabs?(:blobs)
+ = search_filter_link 'blobs', _("Code"), data: { qa_selector: 'code_tab' }
+ - if project_search_tabs?(:issues)
+ = search_filter_link 'issues', _("Issues")
+ - if project_search_tabs?(:merge_requests)
+ = search_filter_link 'merge_requests', _("Merge requests")
+ - if project_search_tabs?(:milestones)
+ = search_filter_link 'milestones', _("Milestones")
+ - if project_search_tabs?(:notes)
+ = search_filter_link 'notes', _("Comments")
+ - if project_search_tabs?(:wiki)
+ = search_filter_link 'wiki_blobs', _("Wiki")
+ - if project_search_tabs?(:commits)
+ = search_filter_link 'commits', _("Commits")
+ = users
+
+ - elsif @show_snippets
+ = search_filter_link 'snippet_titles', _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }
+ - else
+ = search_filter_link 'projects', _("Projects"), data: { qa_selector: 'projects_tab' }
+ = search_filter_link 'issues', _("Issues")
+ = search_filter_link 'merge_requests', _("Merge requests")
+ = search_filter_link 'milestones', _("Milestones")
+ = render_if_exists 'search/epics_filter_link'
+ = render_if_exists 'search/category_elasticsearch'
+ = users
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index c0dca74bb8f..d54310bfa82 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -16,6 +16,7 @@
= render_if_exists 'search/form_elasticsearch', attrs: { class: 'mb-2 mb-sm-0 align-self-center' }
.gl-mt-3
- #js-search-topbar.search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json, "scope-tabs": search_nav_tabs.to_json, count: @search_results&.formatted_count(@scope) } }
+ #js-search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json } }
- if @search_term
+ = render 'search/category'
= render 'search/results'
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index e4222d8a4fe..ababbdc7eb9 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -6,7 +6,10 @@
- @no_breadcrumb_container = true
- @no_container = true
- @content_class = "issue-boards-content js-focus-mode-board"
-- breadcrumb_title _("Issue Boards")
+- if board.to_type == "EpicBoard"
+ - breadcrumb_title _("Epic Boards")
+- else
+ - breadcrumb_title _("Issue Boards")
- page_title("#{board.name}", _("Boards"))
- add_page_specific_style 'page_bundles/boards'
diff --git a/changelogs/unreleased/262066-global-search-scope-tabs-vue-component.yml b/changelogs/unreleased/262066-global-search-scope-tabs-vue-component.yml
deleted file mode 100644
index 4917c824150..00000000000
--- a/changelogs/unreleased/262066-global-search-scope-tabs-vue-component.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Change search tab to Vue component
-merge_request: 52018
-author:
-type: changed
diff --git a/changelogs/unreleased/jimcser-openapi-readme.yml b/changelogs/unreleased/jimcser-openapi-readme.yml
new file mode 100644
index 00000000000..ee693d76727
--- /dev/null
+++ b/changelogs/unreleased/jimcser-openapi-readme.yml
@@ -0,0 +1,5 @@
+---
+title: Adds README to OpenAPI docs
+merge_request: 52637
+author: Jim Cser @jimcser
+type: added
diff --git a/changelogs/unreleased/test_update_merge_request_worker_performance.yml b/changelogs/unreleased/test_update_merge_request_worker_performance.yml
new file mode 100644
index 00000000000..6aea28b2be6
--- /dev/null
+++ b/changelogs/unreleased/test_update_merge_request_worker_performance.yml
@@ -0,0 +1,5 @@
+---
+title: Speed up update merge request worker by memoizing whether a push is a force or not
+merge_request: 53536
+author:
+type: performance
diff --git a/doc/api/openapi/img/apiviewer01-fs8.png b/doc/api/openapi/img/apiviewer01-fs8.png
new file mode 100644
index 00000000000..0a2f7062cc8
--- /dev/null
+++ b/doc/api/openapi/img/apiviewer01-fs8.png
Binary files differ
diff --git a/doc/api/openapi/img/apiviewer03-fs8.png b/doc/api/openapi/img/apiviewer03-fs8.png
new file mode 100644
index 00000000000..9fa00e830e7
--- /dev/null
+++ b/doc/api/openapi/img/apiviewer03-fs8.png
Binary files differ
diff --git a/doc/api/openapi/img/apiviewer04-fs8.png b/doc/api/openapi/img/apiviewer04-fs8.png
new file mode 100644
index 00000000000..bd878ba809b
--- /dev/null
+++ b/doc/api/openapi/img/apiviewer04-fs8.png
Binary files differ
diff --git a/doc/api/openapi/openapi_interactive.md b/doc/api/openapi/openapi_interactive.md
new file mode 100644
index 00000000000..0f1544b139d
--- /dev/null
+++ b/doc/api/openapi/openapi_interactive.md
@@ -0,0 +1,42 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Interactive API documentation
+
+Introduces the interactive documentation tool for the GitLab API.
+
+## About the OpenAPI specification
+
+The [OpenAPI specification](https://swagger.io/specification/) (formerly called Swagger) defines a standard, language-agnostic interface to RESTful APIs. OpenAPI definition files are written in the YAML format, which is automatically rendered by the GitLab browser into a more human-readable interface. For general information about the GitLab APIs, see [API Docs](../README.md).
+
+## Overview
+
+The [interactive API documentation tool](openapi.yaml) allows API testing directly on the GitLab.com
+website. Only a few of the available endpoints are documented with the OpenAPI spec, but the current
+list demonstrates the functionality of the tool.
+
+![API viewer screenshot](img/apiviewer01-fs8.png)
+
+## Endpoint parameters
+
+When you expand an endpoint listing, you'll see a description, input parameters (if required),
+and example server responses. Some parameters include a default or a list of allowed values.
+
+![API viewer screenshot](img/apiviewer04-fs8.png)
+
+## Starting an interactive sesssion
+
+A [Personal access token](../../user/profile/personal_access_tokens.md) (PAT) is one way to
+start an interactive session. To do this, select **Authorize** from the main page, and a
+dialog box prompts you to enter your PAT, which is valid for the current web session.
+
+To test the endpoint, first select **Try it out** on the endpoint definition page. Input the parameters
+as required, then select **Execute**. In the following example, we executed a request for the `version`
+endpoint (no parameters required). The tool shows the `curl` command and URL of the request, followed
+by the server responses that are returned. You can create new responses by editing the relevant parameters
+and then select **Execute** once again.
+
+![API viewer screenshot](img/apiviewer03-fs8.png)
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 3aab57d8e15..02430f39ac8 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -233,46 +233,47 @@ Table description links:
- [Source](../install/installation.md)
- [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit)
-| Component | Description | Omnibus GitLab | GitLab chart | Minikube Minimal | GitLab.com | Source | GDK | CE/EE |
-|-------------------------------------------------------|----------------------------------------------------------------------|:--------------:|:------------:|:----------------:|:----------:|:------:|:---:|:-------:|
-| [Certificate Management](#certificate-management) | TLS Settings, Let's Encrypt | ✅ | ✅ | ⚙ | ✅ | ⚙ | ⚙ | CE & EE |
-| [Consul](#consul) | Database node discovery, failover | ⚙ | ❌ | ❌ | ✅ | ❌ | ❌ | EE Only |
-| [Database Migrations](#database-migrations) | Database migrations | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
-| [Elasticsearch](#elasticsearch) | Improved search within GitLab | ⤓ | ⤓ | ⤓ | ✅ | ⤓ | ⤓ | EE Only |
-| [Gitaly](#gitaly) | Git RPC service for handling all Git calls made by GitLab | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
-| [GitLab Exporter](#gitlab-exporter) | Generates a variety of GitLab metrics | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | CE & EE |
-| [GitLab Geo Node](#gitlab-geo) | Geographically distributed GitLab nodes | ⚙ | ⚙ | ❌ | ✅ | ❌ | ⚙ | EE Only |
-| [GitLab Managed Apps](#gitlab-managed-apps) | Deploy Helm, Ingress, Cert-Manager, Prometheus, GitLab Runner, JupyterHub, or Knative to a cluster | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | CE & EE |
-| [GitLab Pages](#gitlab-pages) | Hosts static websites | ⚙ | ❌ | ❌ | ✅ | ⚙ | ⚙ | CE & EE |
-| [GitLab Kubernetes Agent](#gitlab-kubernetes-agent) | Integrate Kubernetes clusters in a cloud-native way | ⚙ | ⚙ | ❌ | ❌ | ⤓ | ⚙ | EE Only |
-| [GitLab self-monitoring: Alertmanager](#alertmanager) | Deduplicates, groups, and routes alerts from Prometheus | ⚙ | ✅ | ⚙ | ✅ | ❌ | ❌ | CE & EE |
-| [GitLab self-monitoring: Grafana](#grafana) | Metrics dashboard | ✅ | ⚙ | ⤓ | ✅ | ❌ | ❌ | CE & EE |
-| [GitLab self-monitoring: Jaeger](#jaeger) | View traces generated by the GitLab instance | ❌ | ⚙ | ❌ | ❌ | ⤓ | ⚙ | CE & EE |
-| [GitLab self-monitoring: Prometheus](#prometheus) | Time-series database, metrics collection, and query service | ✅ | ✅ | ⚙ | ✅ | ❌ | ❌ | CE & EE |
-| [GitLab self-monitoring: Sentry](#sentry) | Track errors generated by the GitLab instance | ⤓ | ⤓ | ❌ | ✅ | ⤓ | ⤓ | CE & EE |
-| [GitLab Shell](#gitlab-shell) | Handles `git` over SSH sessions | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
-| [GitLab Workhorse](#gitlab-workhorse) | Smart reverse proxy, handles large HTTP requests | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
-| [Inbound email (SMTP)](#inbound-email) | Receive messages to update issues | ⤓ | ⚙ | ⤓ | ✅ | ⤓ | ⤓ | CE & EE |
-| [Jaeger integration](#jaeger) | Distributed tracing for deployed apps | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | EE Only |
-| [LDAP Authentication](#ldap-authentication) | Authenticate users against centralized LDAP directory | ⤓ | ⤓ | ⤓ | ❌ | ⤓ | ⤓ | CE & EE |
-| [Mattermost](#mattermost) | Open-source Slack alternative | ⚙ | ⤓ | ⤓ | ⤓ | ❌ | ❌ | CE & EE |
-| [MinIO](#minio) | Object storage service | ⤓ | ✅ | ✅ | ✅ | ❌ | ⚙ | CE & EE |
-| [NGINX](#nginx) | Routes requests to appropriate components, terminates SSL | ✅ | ✅ | ⚙ | ✅ | ⤓ | ❌ | CE & EE |
-| [Node Exporter](#node-exporter) | Prometheus endpoint with system metrics | ✅ | N/A | N/A | ✅ | ❌ | ❌ | CE & EE |
-| [Outbound email (SMTP)](#outbound-email) | Send email messages to users | ⤓ | ⚙ | ⤓ | ✅ | ⤓ | ⤓ | CE & EE |
-| [Patroni](#patroni) | Manage PostgreSQL HA cluster leader selection and replication | ⚙ | ❌ | ❌ | ✅ | ❌ | ❌ | EE Only |
-| [PgBouncer Exporter](#pgbouncer-exporter) | Prometheus endpoint with PgBouncer metrics | ⚙ | ❌ | ❌ | ✅ | ❌ | ❌ | CE & EE |
-| [PgBouncer](#pgbouncer) | Database connection pooling, failover | ⚙ | ❌ | ❌ | ✅ | ❌ | ❌ | EE Only |
-| [PostgreSQL Exporter](#postgresql-exporter) | Prometheus endpoint with PostgreSQL metrics | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | CE & EE |
-| [PostgreSQL](#postgresql) | Database | ✅ | ✅ | ✅ | ✅ | ⤓ | ✅ | CE & EE |
-| [Praefect](#praefect) | A transparent proxy between any Git client and Gitaly storage nodes. | ✅ | ⚙ | ❌ | ✅ | ⚙ | ✅ | CE & EE |
-| [Redis Exporter](#redis-exporter) | Prometheus endpoint with Redis metrics | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | CE & EE |
-| [Redis](#redis) | Caching service | ✅ | ✅ | ✅ | ✅ | ⤓ | ✅ | CE & EE |
-| [Registry](#registry) | Container registry, allows pushing and pulling of images | ⚙ | ✅ | ✅ | ✅ | ⤓ | ⚙ | CE & EE |
-| [Runner](#gitlab-runner) | Executes GitLab CI/CD jobs | ⤓ | ✅ | ⚙ | ✅ | ⚙ | ⚙ | CE & EE |
-| [Sentry integration](#sentry) | Error tracking for deployed apps | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | CE & EE |
-| [Sidekiq](#sidekiq) | Background jobs processor | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | CE & EE |
-| [Puma (GitLab Rails)](#puma) | Handles requests for the web interface and API | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
+| Component | Description | Omnibus GitLab | GitLab Environment Toolkit (GET) - Omnibus | GitLab chart | Minikube Minimal | GitLab.com | Source | GDK | CE/EE |
+|-------------------------------------------------------|----------------------------------------------------------------------|:--------------:|:--------------:|:------------:|:----------------:|:----------:|:------:|:---:|:-------:|
+| [Certificate Management](#certificate-management) | TLS Settings, Let's Encrypt | ✅ | ✅ | ✅ | ⚙ | ✅ | ⚙ | ⚙ | CE & EE |
+| [Consul](#consul) | Database node discovery, failover | ⚙ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | EE Only |
+| [Database Migrations](#database-migrations) | Database migrations | ✅ | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
+| [Elasticsearch](#elasticsearch) | Improved search within GitLab | ⤓ | ⚙ | ⤓ | ⤓ | ✅ | ⤓ | ⤓ | EE Only |
+| [Gitaly](#gitaly) | Git RPC service for handling all Git calls made by GitLab | ✅ | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
+| [GitLab Exporter](#gitlab-exporter) | Generates a variety of GitLab metrics | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | CE & EE |
+| [GitLab Geo Node](#gitlab-geo) | Geographically distributed GitLab nodes | ⚙ | ⚙ | ❌ | ❌ | ✅ | ❌ | ⚙ | EE Only |
+| [GitLab Kubernetes Agent](#gitlab-kubernetes-agent) | Integrate Kubernetes clusters in a cloud-native way | ⚙ | ⚙ | ⚙ | ❌ | ❌ | ⤓ | ⚙ | EE Only |
+| [GitLab Managed Apps](#gitlab-managed-apps) | Deploy Helm, Ingress, Cert-Manager, Prometheus, GitLab Runner, JupyterHub, or Knative to a cluster | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | CE & EE |
+| [GitLab Pages](#gitlab-pages) | Hosts static websites | ⚙ | ⚙ | ❌ | ❌ | ✅ | ⚙ | ⚙ | CE & EE |
+| [GitLab Kubernetes Agent](#gitlab-kubernetes-agent) | Integrate Kubernetes clusters in a cloud-native way | ⚙ | ⚙ | ⚙ | ❌ | ❌ | ⤓ | ⚙ | EE Only |
+| [GitLab self-monitoring: Alertmanager](#alertmanager) | Deduplicates, groups, and routes alerts from Prometheus | ⚙ | ⚙ | ✅ | ⚙ | ✅ | ❌ | ❌ | CE & EE |
+| [GitLab self-monitoring: Grafana](#grafana) | Metrics dashboard | ✅ | ✅ | ⚙ | ⤓ | ✅ | ❌ | ❌ | CE & EE |
+| [GitLab self-monitoring: Jaeger](#jaeger) | View traces generated by the GitLab instance | ❌ | ⚙ | ⚙ | ❌ | ❌ | ⤓ | ⚙ | CE & EE |
+| [GitLab self-monitoring: Prometheus](#prometheus) | Time-series database, metrics collection, and query service | ✅ | ✅ | ✅ | ⚙ | ✅ | ❌ | ❌ | CE & EE |
+| [GitLab self-monitoring: Sentry](#sentry) | Track errors generated by the GitLab instance | ⤓ | ⤓ | ⤓ | ❌ | ✅ | ⤓ | ⤓ | CE & EE |
+| [GitLab Shell](#gitlab-shell) | Handles `git` over SSH sessions | ✅ | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
+| [GitLab Workhorse](#gitlab-workhorse) | Smart reverse proxy, handles large HTTP requests | ✅ | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
+| [Inbound email (SMTP)](#inbound-email) | Receive messages to update issues | ⤓ | ⤓ | ⚙ | ⤓ | ✅ | ⤓ | ⤓ | CE & EE |
+| [Jaeger integration](#jaeger) | Distributed tracing for deployed apps | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | EE Only |
+| [LDAP Authentication](#ldap-authentication) | Authenticate users against centralized LDAP directory | ⤓ | ⤓ | ⤓ | ⤓ | ❌ | ⤓ | ⤓ | CE & EE |
+| [Mattermost](#mattermost) | Open-source Slack alternative | ⚙ | ⚙ | ⤓ | ⤓ | ⤓ | ❌ | ❌ | CE & EE |
+| [MinIO](#minio) | Object storage service | ⤓ | ⤓ | ✅ | ✅ | ✅ | ❌ | ⚙ | CE & EE |
+| [NGINX](#nginx) | Routes requests to appropriate components, terminates SSL | ✅ | ✅ | ✅ | ⚙ | ✅ | ⤓ | ❌ | CE & EE |
+| [Node Exporter](#node-exporter) | Prometheus endpoint with system metrics | ✅ | ✅ | N/A | N/A | ✅ | ❌ | ❌ | CE & EE |
+| [Outbound email (SMTP)](#outbound-email) | Send email messages to users | ⤓ | ⤓ | ⚙ | ⤓ | ✅ | ⤓ | ⤓ | CE & EE |
+| [Patroni](#patroni) | Manage PostgreSQL HA cluster leader selection and replication | ⚙ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | EE Only |
+| [PgBouncer Exporter](#pgbouncer-exporter) | Prometheus endpoint with PgBouncer metrics | ⚙ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | CE & EE |
+| [PgBouncer](#pgbouncer) | Database connection pooling, failover | ⚙ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | EE Only |
+| [PostgreSQL Exporter](#postgresql-exporter) | Prometheus endpoint with PostgreSQL metrics | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | CE & EE |
+| [PostgreSQL](#postgresql) | Database | ✅ | ✅ | ✅ | ✅ | ✅ | ⤓ | ✅ | CE & EE |
+| [Praefect](#praefect) | A transparent proxy between any Git client and Gitaly storage nodes. | ✅ | ✅ | ⚙ | ❌ | ✅ | ⚙ | ✅ | CE & EE |
+| [Puma (GitLab Rails)](#puma) | Handles requests for the web interface and API | ✅ | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE |
+| [Redis Exporter](#redis-exporter) | Prometheus endpoint with Redis metrics | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | CE & EE |
+| [Redis](#redis) | Caching service | ✅ | ✅ | ✅ | ✅ | ✅ | ⤓ | ✅ | CE & EE |
+| [Registry](#registry) | Container registry, allows pushing and pulling of images | ⚙ | ⚙ | ✅ | ✅ | ✅ | ⤓ | ⚙ | CE & EE |
+| [Runner](#gitlab-runner) | Executes GitLab CI/CD jobs | ⤓ | ⤓ | ✅ | ⚙ | ✅ | ⚙ | ⚙ | CE & EE |
+| [Sentry integration](#sentry) | Error tracking for deployed apps | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | CE & EE |
+| [Sidekiq](#sidekiq) | Background jobs processor | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | CE & EE |
### Component details
diff --git a/lib/gitlab/git/push.rb b/lib/gitlab/git/push.rb
index b6577ba17f1..3d533a5185f 100644
--- a/lib/gitlab/git/push.rb
+++ b/lib/gitlab/git/push.rb
@@ -33,7 +33,9 @@ module Gitlab
end
def force_push?
- Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
+ strong_memoize(:force_push) do
+ Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
+ end
end
def branch_push?
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 7ffaa21157f..4d3a62b20a1 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -11523,6 +11523,9 @@ msgstr ""
msgid "Epic"
msgstr ""
+msgid "Epic Boards"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -29563,9 +29566,6 @@ msgstr ""
msgid "There was an error fetching the Node's Groups"
msgstr ""
-msgid "There was an error fetching the Search Counts"
-msgstr ""
-
msgid "There was an error fetching the deploy freezes."
msgstr ""
diff --git a/package.json b/package.json
index 753e051b81a..b7947982c09 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
"autosize": "^4.0.2",
"aws-sdk": "^2.637.0",
"axios": "^0.20.0",
- "babel-loader": "^8.0.6",
+ "babel-loader": "^8.2.2",
"babel-plugin-lodash": "^3.3.4",
"bootstrap": "4.4.1",
"brace-expansion": "^1.1.8",
@@ -69,7 +69,7 @@
"codemirror": "^5.48.4",
"codesandbox-api": "0.0.23",
"compression-webpack-plugin": "^5.0.2",
- "copy-webpack-plugin": "^5.0.5",
+ "copy-webpack-plugin": "^5.1.2",
"core-js": "^3.8.3",
"cron-validator": "^1.1.1",
"cropper": "^2.3.0",
@@ -125,7 +125,7 @@
"prosemirror-markdown": "^1.3.0",
"prosemirror-model": "^1.6.4",
"raphael": "^2.2.7",
- "raw-loader": "^4.0.0",
+ "raw-loader": "^4.0.2",
"select2": "3.5.2-browserify",
"smooshpack": "^0.0.62",
"sortablejs": "^1.10.2",
@@ -154,7 +154,7 @@
"vuex": "^3.6.0",
"web-vitals": "^0.2.4",
"webpack": "^4.46.0",
- "webpack-bundle-analyzer": "^3.6.0",
+ "webpack-bundle-analyzer": "^3.9.0",
"webpack-cli": "^3.3.12",
"webpack-stats-plugin": "^0.3.1",
"worker-loader": "^2.0.0",
diff --git a/qa/Gemfile b/qa/Gemfile
index da45ba3b955..f55d27c159b 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
source 'https://rubygems.org'
gem 'gitlab-qa'
diff --git a/qa/Rakefile b/qa/Rakefile
index 6bafec2a67d..0a040f0aee7 100644
--- a/qa/Rakefile
+++ b/qa/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_relative 'qa/tools/revoke_all_personal_access_tokens'
require_relative 'qa/tools/delete_subgroups'
require_relative 'qa/tools/generate_perf_testdata'
diff --git a/qa/bin/qa b/qa/bin/qa
index cbaad4bd7ad..4328adf719b 100755
--- a/qa/bin/qa
+++ b/qa/bin/qa
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
require_relative '../qa'
diff --git a/qa/bin/rubymine b/qa/bin/rubymine
index 0be0cf0ec33..7db50f4aade 100755
--- a/qa/bin/rubymine
+++ b/qa/bin/rubymine
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
require_relative '../qa'
diff --git a/qa/qa/fixtures/auto_devops_rack/Gemfile b/qa/qa/fixtures/auto_devops_rack/Gemfile
index fc7514242d0..2c7c77adf94 100644
--- a/qa/qa/fixtures/auto_devops_rack/Gemfile
+++ b/qa/qa/fixtures/auto_devops_rack/Gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
source 'https://rubygems.org'
gem 'rack'
gem 'rake'
diff --git a/qa/qa/fixtures/auto_devops_rack/Rakefile b/qa/qa/fixtures/auto_devops_rack/Rakefile
index c865c9aaac1..a6d08103d55 100644
--- a/qa/qa/fixtures/auto_devops_rack/Rakefile
+++ b/qa/qa/fixtures/auto_devops_rack/Rakefile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'rake/testtask'
task default: %w[test]
diff --git a/qa/qa/fixtures/auto_devops_rack/config.ru b/qa/qa/fixtures/auto_devops_rack/config.ru
index e990662145a..aea28ef1893 100644
--- a/qa/qa/fixtures/auto_devops_rack/config.ru
+++ b/qa/qa/fixtures/auto_devops_rack/config.ru
@@ -1 +1,3 @@
+# frozen_string_literal: true
+
run lambda { |env| [200, { 'Content-Type' => 'text/plain' }, StringIO.new("Hello World! #{ENV['OPTIONAL_MESSAGE']}\n")] }
diff --git a/qa/qa/page/page_concern.rb b/qa/qa/page/page_concern.rb
index 6ba2d27f574..cf1153c0ca3 100644
--- a/qa/qa/page/page_concern.rb
+++ b/qa/qa/page/page_concern.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module QA
module Page
module PageConcern
diff --git a/qa/qa/page/search/results.rb b/qa/qa/page/search/results.rb
index 7dab3881ede..3f7aa837d3c 100644
--- a/qa/qa/page/search/results.rb
+++ b/qa/qa/page/search/results.rb
@@ -4,7 +4,7 @@ module QA
module Page
module Search
class Results < QA::Page::Base
- view 'app/assets/javascripts/search/topbar/constants.js' do
+ view 'app/views/search/_category.html.haml' do
element :code_tab
element :projects_tab
end
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index 0fab274e1f7..19fb8e5f52c 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe 'Global search' do
create_list(:issue, 2, project: project, title: 'initial')
end
- it "has a pagination", :js do
+ it "has a pagination" do
submit_search('initial')
select_search_scope('Issues')
diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb
index 14a8176adec..ee3717b3e42 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for code', :js do
+RSpec.describe 'User searches for code' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
@@ -16,7 +16,6 @@ RSpec.describe 'User searches for code', :js do
visit(project_path(project))
submit_search('application.js')
-
select_search_scope('Code')
expect(page).to have_selector('.results', text: 'application.js')
@@ -25,7 +24,7 @@ RSpec.describe 'User searches for code', :js do
expect(page).to have_link('application.js', href: /master\/files\/js\/application.js/)
end
- context 'when on a project page' do
+ context 'when on a project page', :js do
before do
visit(search_path)
find('[data-testid="project-filter"]').click
@@ -49,7 +48,7 @@ RSpec.describe 'User searches for code', :js do
expect(current_url).to match(/master\/.gitignore#L3/)
end
- it 'search multiple words with refs switching' do
+ it 'search mutiple words with refs switching' do
expected_result = 'Use `snake_case` for naming files'
search = 'for naming files'
@@ -68,7 +67,7 @@ RSpec.describe 'User searches for code', :js do
end
end
- context 'search code within refs' do
+ context 'search code within refs', :js do
let(:ref_name) { 'v1.0.0' }
before do
@@ -86,9 +85,9 @@ RSpec.describe 'User searches for code', :js do
expect(find('.js-project-refs-dropdown')).to have_text(ref_name)
end
- # this example is use to test the design that the refs is not
- # only represent the branch as well as the tags.
- it 'ref switcher list all the branches and tags' do
+ # this example is use to test the desgine that the refs is not
+ # only repersent the branch as well as the tags.
+ it 'ref swither list all the branchs and tags' do
find('.js-project-refs-dropdown').click
expect(find('.dropdown-page-one .dropdown-content')).to have_link('sha-starting-with-large-number')
expect(find('.dropdown-page-one .dropdown-content')).to have_link('v1.0.0')
diff --git a/spec/features/search/user_searches_for_comments_spec.rb b/spec/features/search/user_searches_for_comments_spec.rb
index 4bdaed970f6..2a12b22b457 100644
--- a/spec/features/search/user_searches_for_comments_spec.rb
+++ b/spec/features/search/user_searches_for_comments_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for comments', :js do
+RSpec.describe 'User searches for comments' do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/features/search/user_searches_for_users_spec.rb b/spec/features/search/user_searches_for_users_spec.rb
index 70fb7166b21..826ed73c9bf 100644
--- a/spec/features/search/user_searches_for_users_spec.rb
+++ b/spec/features/search/user_searches_for_users_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User searches for users', :js do
+RSpec.describe 'User searches for users' do
let(:user1) { create(:user, username: 'gob_bluth', name: 'Gob Bluth') }
let(:user2) { create(:user, username: 'michael_bluth', name: 'Michael Bluth') }
let(:user3) { create(:user, username: 'gob_2018', name: 'George Oscar Bluth') }
@@ -12,7 +12,7 @@ RSpec.describe 'User searches for users', :js do
end
context 'when on the dashboard' do
- it 'finds the user' do
+ it 'finds the user', :js do
visit dashboard_projects_path
submit_search('gob')
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index d18729d080a..46bc3b7caad 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Search Snippets', :js do
+RSpec.describe 'Search Snippets' do
it 'user searches for snippets by title' do
public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
diff --git a/spec/frontend/pages/search/show/__snapshots__/refresh_counts_spec.js.snap b/spec/frontend/pages/search/show/__snapshots__/refresh_counts_spec.js.snap
new file mode 100644
index 00000000000..ce456d6c899
--- /dev/null
+++ b/spec/frontend/pages/search/show/__snapshots__/refresh_counts_spec.js.snap
@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`pages/search/show/refresh_counts fetches and displays search counts 1`] = `
+"<div class=\\"badge\\">22</div>
+<div class=\\"badge js-search-count\\" data-url=\\"http://test.host/search/count?search=lorem+ipsum&amp;project_id=3&amp;scope=issues\\">4</div>
+<div class=\\"badge js-search-count\\" data-url=\\"http://test.host/search/count?search=lorem+ipsum&amp;project_id=3&amp;scope=merge_requests\\">5</div>"
+`;
diff --git a/spec/frontend/pages/search/show/refresh_counts_spec.js b/spec/frontend/pages/search/show/refresh_counts_spec.js
new file mode 100644
index 00000000000..81c9bf74308
--- /dev/null
+++ b/spec/frontend/pages/search/show/refresh_counts_spec.js
@@ -0,0 +1,38 @@
+import MockAdapter from 'axios-mock-adapter';
+import { TEST_HOST } from 'helpers/test_constants';
+import axios from '~/lib/utils/axios_utils';
+import refreshCounts from '~/pages/search/show/refresh_counts';
+
+const URL = `${TEST_HOST}/search/count?search=lorem+ipsum&project_id=3`;
+const urlWithScope = (scope) => `${URL}&scope=${scope}`;
+const counts = [
+ { scope: 'issues', count: 4 },
+ { scope: 'merge_requests', count: 5 },
+];
+const fixture = `<div class="badge">22</div>
+<div class="badge js-search-count hidden" data-url="${urlWithScope('issues')}"></div>
+<div class="badge js-search-count hidden" data-url="${urlWithScope('merge_requests')}"></div>`;
+
+describe('pages/search/show/refresh_counts', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ setFixtures(fixture);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('fetches and displays search counts', () => {
+ counts.forEach(({ scope, count }) => {
+ mock.onGet(urlWithScope(scope)).reply(200, { count });
+ });
+
+ // assert before act behavior
+ return refreshCounts().then(() => {
+ expect(document.body.innerHTML).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js
index 5cd16a856e7..d076997b04a 100644
--- a/spec/frontend/search/mock_data.js
+++ b/spec/frontend/search/mock_data.js
@@ -61,28 +61,3 @@ export const MOCK_SORT_OPTIONS = [
},
},
];
-
-export const MOCK_SEARCH_COUNTS_INPUT = {
- scopeTabs: ['issues', 'snippet_titles', 'merge_requests'],
- activeCount: '15',
-};
-
-export const MOCK_SEARCH_COUNT = { scope: 'issues', count: '15' };
-
-export const MOCK_SEARCH_COUNTS_SUCCESS = [
- { scope: 'issues', count: '15' },
- { scope: 'snippet_titles', count: '15' },
- { scope: 'merge_requests', count: '15' },
-];
-
-export const MOCK_SEARCH_COUNTS = [
- { scope: 'issues', count: '15' },
- { scope: 'snippet_titles', count: '5' },
- { scope: 'merge_requests', count: '1' },
-];
-
-export const MOCK_SCOPE_TABS = [
- { scope: 'issues', title: 'Issues', count: '15' },
- { scope: 'snippet_titles', title: 'Titles and Descriptions', count: '5' },
- { scope: 'merge_requests', title: 'Merge requests', count: '1' },
-];
diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js
index f751b857c36..e4536a3e136 100644
--- a/spec/frontend/search/store/actions_spec.js
+++ b/spec/frontend/search/store/actions_spec.js
@@ -7,15 +7,7 @@ import * as urlUtils from '~/lib/utils/url_utility';
import createState from '~/search/store/state';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
-import {
- MOCK_QUERY,
- MOCK_GROUPS,
- MOCK_PROJECT,
- MOCK_PROJECTS,
- MOCK_SEARCH_COUNT,
- MOCK_SEARCH_COUNTS_SUCCESS,
- MOCK_SEARCH_COUNTS_INPUT,
-} from '../mock_data';
+import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECT, MOCK_PROJECTS } from '../mock_data';
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
@@ -45,21 +37,19 @@ describe('Global Search Store Actions', () => {
});
describe.each`
- action | axiosMock | payload | type | expectedMutations | callback
- ${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${null} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback}
- ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${null} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback}
- ${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${null} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${noCallback}
- ${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${null} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${flashCallback}
- ${actions.fetchSearchCounts} | ${{ method: 'onGet', code: 200, res: MOCK_SEARCH_COUNT }} | ${MOCK_SEARCH_COUNTS_INPUT} | ${'success'} | ${[{ type: types.REQUEST_SEARCH_COUNTS, payload: MOCK_SEARCH_COUNTS_INPUT }, { type: types.RECEIVE_SEARCH_COUNTS_SUCCESS, payload: MOCK_SEARCH_COUNTS_SUCCESS }]} | ${noCallback}
- ${actions.fetchSearchCounts} | ${{ method: 'onGet', code: 500, res: null }} | ${MOCK_SEARCH_COUNTS_INPUT} | ${'error'} | ${[{ type: types.REQUEST_SEARCH_COUNTS, payload: MOCK_SEARCH_COUNTS_INPUT }]} | ${flashCallback}
- `(`axios calls`, ({ action, axiosMock, payload, type, expectedMutations, callback }) => {
+ action | axiosMock | type | expectedMutations | callback
+ ${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback}
+ ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback}
+ ${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${noCallback}
+ ${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${flashCallback}
+ `(`axios calls`, ({ action, axiosMock, type, expectedMutations, callback }) => {
describe(action.name, () => {
describe(`on ${type}`, () => {
beforeEach(() => {
- mock[axiosMock.method]().reply(axiosMock.code, axiosMock.res);
+ mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res);
});
it(`should dispatch the correct mutations`, () => {
- return testAction({ action, payload, state, expectedMutations }).then(() => callback());
+ return testAction({ action, state, expectedMutations }).then(() => callback());
});
});
});
@@ -125,25 +115,9 @@ describe('Global Search Store Actions', () => {
page: null,
state: null,
confidential: null,
- nav_source: null,
});
expect(urlUtils.visitUrl).toHaveBeenCalled();
});
});
});
-
- it('calls setUrlParams with snippets, group_id, and project_id when snippets param is true', () => {
- return testAction(actions.resetQuery, true, state, [], [], () => {
- expect(urlUtils.setUrlParams).toHaveBeenCalledWith({
- ...state.query,
- page: null,
- state: null,
- confidential: null,
- nav_source: null,
- group_id: null,
- project_id: null,
- snippets: true,
- });
- });
- });
});
diff --git a/spec/frontend/search/store/mutations_spec.js b/spec/frontend/search/store/mutations_spec.js
index e9a1107cb07..560ed66263b 100644
--- a/spec/frontend/search/store/mutations_spec.js
+++ b/spec/frontend/search/store/mutations_spec.js
@@ -1,13 +1,7 @@
import mutations from '~/search/store/mutations';
import createState from '~/search/store/state';
import * as types from '~/search/store/mutation_types';
-import {
- MOCK_QUERY,
- MOCK_GROUPS,
- MOCK_PROJECTS,
- MOCK_SEARCH_COUNTS,
- MOCK_SCOPE_TABS,
-} from '../mock_data';
+import { MOCK_QUERY, MOCK_GROUPS, MOCK_PROJECTS } from '../mock_data';
describe('Global Search Store Mutations', () => {
let state;
@@ -77,32 +71,4 @@ describe('Global Search Store Mutations', () => {
expect(state.query[payload.key]).toBe(payload.value);
});
});
-
- describe('REQUEST_SEARCH_COUNTS', () => {
- it('sets the count to for the query.scope activeCount', () => {
- const payload = { scopeTabs: ['issues'], activeCount: '22' };
- mutations[types.REQUEST_SEARCH_COUNTS](state, payload);
-
- expect(state.inflatedScopeTabs).toStrictEqual([
- { scope: 'issues', title: 'Issues', count: '22' },
- ]);
- });
-
- it('sets other scopes count to empty string', () => {
- const payload = { scopeTabs: ['milestones'], activeCount: '22' };
- mutations[types.REQUEST_SEARCH_COUNTS](state, payload);
-
- expect(state.inflatedScopeTabs).toStrictEqual([
- { scope: 'milestones', title: 'Milestones', count: '' },
- ]);
- });
- });
-
- describe('RECEIVE_SEARCH_COUNTS_SUCCESS', () => {
- it('sets the count from the input for all tabs', () => {
- mutations[types.RECEIVE_SEARCH_COUNTS_SUCCESS](state, MOCK_SEARCH_COUNTS);
-
- expect(state.inflatedScopeTabs).toStrictEqual(MOCK_SCOPE_TABS);
- });
- });
});
diff --git a/spec/frontend/search/topbar/components/app_spec.js b/spec/frontend/search/topbar/components/app_spec.js
index ff37c733fdb..faf3629b444 100644
--- a/spec/frontend/search/topbar/components/app_spec.js
+++ b/spec/frontend/search/topbar/components/app_spec.js
@@ -5,7 +5,6 @@ import { MOCK_QUERY } from 'jest/search/mock_data';
import GlobalSearchTopbar from '~/search/topbar/components/app.vue';
import GroupFilter from '~/search/topbar/components/group_filter.vue';
import ProjectFilter from '~/search/topbar/components/project_filter.vue';
-import ScopeTabs from '~/search/topbar/components/scope_tabs.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -43,7 +42,6 @@ describe('GlobalSearchTopbar', () => {
const findGroupFilter = () => wrapper.find(GroupFilter);
const findProjectFilter = () => wrapper.find(ProjectFilter);
const findSearchButton = () => wrapper.find(GlButton);
- const findScopeTabs = () => wrapper.find(ScopeTabs);
describe('template', () => {
beforeEach(() => {
@@ -54,18 +52,6 @@ describe('GlobalSearchTopbar', () => {
expect(findTopbarForm().exists()).toBe(true);
});
- describe('Scope Tabs', () => {
- it('renders when search param is set', () => {
- createComponent({ query: { search: 'test' } });
- expect(findScopeTabs().exists()).toBe(true);
- });
- it('does not render search param is blank', () => {
- createComponent({ query: {} });
-
- expect(findScopeTabs().exists()).toBe(false);
- });
- });
-
describe('Search box', () => {
it('renders always', () => {
expect(findGlSearchBox().exists()).toBe(true);
diff --git a/spec/frontend/search/topbar/components/scope_tabs_spec.js b/spec/frontend/search/topbar/components/scope_tabs_spec.js
deleted file mode 100644
index 5c06ce7a2ad..00000000000
--- a/spec/frontend/search/topbar/components/scope_tabs_spec.js
+++ /dev/null
@@ -1,122 +0,0 @@
-import Vuex from 'vuex';
-import { createLocalVue, mount } from '@vue/test-utils';
-import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import { MOCK_QUERY, MOCK_SCOPE_TABS } from 'jest/search/mock_data';
-import ScopeTabs from '~/search/topbar/components/scope_tabs.vue';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('ScopeTabs', () => {
- let wrapper;
-
- const actionSpies = {
- fetchSearchCounts: jest.fn(),
- setQuery: jest.fn(),
- resetQuery: jest.fn(),
- };
-
- const defaultProps = {
- scopeTabs: ['issues', 'merge_requests', 'milestones'],
- count: '20',
- };
-
- const createComponent = (props = {}, initialState = {}) => {
- const store = new Vuex.Store({
- state: {
- query: {
- ...MOCK_QUERY,
- search: 'test',
- },
- ...initialState,
- },
- actions: actionSpies,
- });
-
- wrapper = extendedWrapper(
- mount(ScopeTabs, {
- localVue,
- store,
- propsData: {
- ...defaultProps,
- ...props,
- },
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const findScopeTabs = () => wrapper.find(GlTabs);
- const findTabs = () => wrapper.findAll(GlTab);
- const findBadges = () => wrapper.findAll(GlBadge);
- const findTabsTitle = () =>
- wrapper.findAll('[data-testid="tab-title"]').wrappers.map((w) => w.text());
- const findBadgesTitle = () => findBadges().wrappers.map((w) => w.text());
- const findBadgeByScope = (scope) => wrapper.findByTestId(`badge-${scope}`);
- const findTabByScope = (scope) => wrapper.findByTestId(`tab-${scope}`);
-
- describe('template', () => {
- beforeEach(() => {
- createComponent({}, { inflatedScopeTabs: MOCK_SCOPE_TABS });
- });
-
- it('always renders Scope Tabs', () => {
- expect(findScopeTabs().exists()).toBe(true);
- });
-
- describe('findTabs', () => {
- it('renders a tab for each scope', () => {
- expect(findTabs()).toHaveLength(defaultProps.scopeTabs.length);
- expect(findTabsTitle()).toStrictEqual([
- 'Issues',
- 'Titles and Descriptions',
- 'Merge requests',
- ]);
- });
- });
-
- describe('findBadges', () => {
- it('renders a badge for each scope', () => {
- expect(findBadges()).toHaveLength(defaultProps.scopeTabs.length);
- expect(findBadgesTitle()).toStrictEqual(['15', '5', '1']);
- });
-
- it('sets the variant to neutral for active tab only', () => {
- expect(findBadgeByScope('issues').classes()).toContain('badge-neutral');
- expect(findBadgeByScope('snippet_titles').classes()).toContain('badge-muted');
- expect(findBadgeByScope('merge_requests').classes()).toContain('badge-muted');
- });
- });
- });
-
- describe('methods', () => {
- beforeEach(() => {
- createComponent({}, { inflatedScopeTabs: MOCK_SCOPE_TABS });
-
- findTabByScope('snippet_titles').vm.$emit('click');
- });
-
- describe('handleTabChange', () => {
- it('calls setQuery with scope, applies any search params from ALL_SCOPE_TABS, and sends nulls for page, state, confidential, and nav_source', () => {
- expect(actionSpies.setQuery).toHaveBeenCalledWith(expect.any(Object), {
- key: 'scope',
- value: 'snippet_titles',
- });
- });
-
- it('calls resetQuery and sends true for snippet_titles tab', () => {
- expect(actionSpies.resetQuery).toHaveBeenCalledWith(expect.any(Object), true);
- });
-
- it('calls resetQuery and does not send true for other tabs', () => {
- findTabByScope('issues').vm.$emit('click');
- expect(actionSpies.resetQuery).toHaveBeenCalledWith(expect.any(Object), false);
- });
- });
- });
-});
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 4881d4a5b51..a977f2c88c6 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -392,6 +392,63 @@ RSpec.describe SearchHelper do
end
end
+ describe 'search_filter_link' do
+ it 'renders a search filter link for the current scope' do
+ @scope = 'projects'
+ @search_results = double
+
+ expect(@search_results).to receive(:formatted_count).with('projects').and_return('23')
+
+ link = search_filter_link('projects', 'Projects')
+
+ expect(link).to have_css('li.active')
+ expect(link).to have_link('Projects', href: search_path(scope: 'projects'))
+ expect(link).to have_css('span.badge.badge-pill:not(.js-search-count):not(.hidden):not([data-url])', text: '23')
+ end
+
+ it 'renders a search filter link for another scope' do
+ link = search_filter_link('projects', 'Projects')
+ count_path = search_count_path(scope: 'projects')
+
+ expect(link).to have_css('li:not([class="active"])')
+ expect(link).to have_link('Projects', href: search_path(scope: 'projects'))
+ expect(link).to have_css("span.badge.badge-pill.js-search-count.hidden[data-url='#{count_path}']", text: '')
+ end
+
+ it 'merges in the current search params and given params' do
+ expect(self).to receive(:params).and_return(
+ ActionController::Parameters.new(
+ search: 'hello',
+ scope: 'ignored',
+ other_param: 'ignored'
+ )
+ )
+
+ link = search_filter_link('projects', 'Projects', search: { project_id: 23 })
+
+ expect(link).to have_link('Projects', href: search_path(scope: 'projects', search: 'hello', project_id: 23))
+ end
+
+ it 'restricts the params' do
+ expect(self).to receive(:params).and_return(
+ ActionController::Parameters.new(
+ search: 'hello',
+ unknown: 42
+ )
+ )
+
+ link = search_filter_link('projects', 'Projects')
+
+ expect(link).to have_link('Projects', href: search_path(scope: 'projects', search: 'hello'))
+ end
+
+ it 'assigns given data attributes on the list container' do
+ link = search_filter_link('projects', 'Projects', data: { foo: 'bar' })
+
+ expect(link).to have_css('li[data-foo="bar"]')
+ end
+ end
+
describe '#show_user_search_tab?' do
subject { show_user_search_tab? }
@@ -584,86 +641,4 @@ RSpec.describe SearchHelper do
expect(search_sort_options).to eq(mock_created_sort)
end
end
-
- describe '#search_nav_tabs' do
- subject { search_nav_tabs }
-
- let(:current_user) { nil }
-
- before do
- allow(self).to receive(:current_user).and_return(current_user)
- end
-
- context 'when @show_snippets is present' do
- before do
- @show_snippets = 1
- end
-
- it { is_expected.to eq([:snippet_titles]) }
-
- context 'and @project is present' do
- before do
- @project = 1
- allow(self).to receive(:project_search_tabs?).with(anything).and_return(true)
- end
-
- it { is_expected.to eq([:blobs, :issues, :merge_requests, :milestones, :notes, :wiki_blobs, :commits, :users]) }
- end
- end
-
- context 'when @project is present' do
- before do
- @project = 1
- end
-
- context 'when user has access to project' do
- before do
- allow(self).to receive(:project_search_tabs?).with(anything).and_return(true)
- end
-
- it { is_expected.to eq([:blobs, :issues, :merge_requests, :milestones, :notes, :wiki_blobs, :commits, :users]) }
- end
-
- context 'when user does not have access to project' do
- before do
- allow(self).to receive(:project_search_tabs?).with(anything).and_return(false)
- end
-
- it { is_expected.to eq([]) }
- end
-
- context 'when user does not have access to read members for project' do
- before do
- allow(self).to receive(:project_search_tabs?).with(:members).and_return(false)
- allow(self).to receive(:project_search_tabs?).with(:merge_requests).and_return(true)
- allow(self).to receive(:project_search_tabs?).with(:milestones).and_return(true)
- allow(self).to receive(:project_search_tabs?).with(:wiki_blobs).and_return(true)
- allow(self).to receive(:project_search_tabs?).with(:issues).and_return(true)
- allow(self).to receive(:project_search_tabs?).with(:blobs).and_return(true)
- allow(self).to receive(:project_search_tabs?).with(:notes).and_return(true)
- allow(self).to receive(:project_search_tabs?).with(:commits).and_return(true)
- end
-
- it { is_expected.to eq([:blobs, :issues, :merge_requests, :milestones, :notes, :wiki_blobs, :commits]) }
- end
- end
-
- context 'when @show_snippets and @project are not present' do
- context 'when user has access to read users' do
- before do
- allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
- end
-
- it { is_expected.to eq([:projects, :issues, :merge_requests, :milestones, :users]) }
- end
-
- context 'when user does not have access to read users' do
- before do
- allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(false)
- end
-
- it { is_expected.to eq([:projects, :issues, :merge_requests, :milestones]) }
- end
- end
- end
end
diff --git a/spec/lib/gitlab/git/push_spec.rb b/spec/lib/gitlab/git/push_spec.rb
index 0f52f10c0a6..8ba43b2967c 100644
--- a/spec/lib/gitlab/git/push_spec.rb
+++ b/spec/lib/gitlab/git/push_spec.rb
@@ -86,6 +86,16 @@ RSpec.describe Gitlab::Git::Push do
it { is_expected.to be_force_push }
end
+
+ context 'when called muiltiple times' do
+ it 'does not make make multiple calls to the force push check' do
+ expect(Gitlab::Checks::ForcePush).to receive(:force_push?).once
+
+ 2.times do
+ subject.force_push?
+ end
+ end
+ end
end
describe '#branch_added?' do
diff --git a/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
index 8f7c08ed625..0e2bddc19ab 100644
--- a/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/boards_list_service_shared_examples.rb
@@ -1,32 +1,8 @@
# frozen_string_literal: true
RSpec.shared_examples 'boards list service' do
- context 'when parent does not have a board' do
- it 'creates a new parent board' do
- expect { service.execute }.to change(parent.boards, :count).by(1)
- end
-
- it 'delegates the parent board creation to Boards::CreateService' do
- expect_any_instance_of(Boards::CreateService).to receive(:execute).once
-
- service.execute
- end
-
- context 'when create_default_board is false' do
- it 'does not create a new parent board' do
- expect { service.execute(create_default_board: false) }.not_to change(parent.boards, :count)
- end
- end
- end
-
- context 'when parent has a board' do
- before do
- create(:board, resource_parent: parent)
- end
-
- it 'does not create a new board' do
- expect { service.execute }.not_to change(parent.boards, :count)
- end
+ it 'does not create a new board' do
+ expect { service.execute }.not_to change(parent.boards, :count)
end
it 'returns parent boards' do
diff --git a/spec/views/search/show.html.haml_spec.rb b/spec/views/search/show.html.haml_spec.rb
index 1f70aa029eb..eb763d424d3 100644
--- a/spec/views/search/show.html.haml_spec.rb
+++ b/spec/views/search/show.html.haml_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe 'search/show' do
let(:search_term) { nil }
before do
+ stub_template "search/_category.html.haml" => 'Category Partial'
stub_template "search/_results.html.haml" => 'Results Partial'
@search_term = search_term
@@ -20,6 +21,7 @@ RSpec.describe 'search/show' do
end
it 'does not render partials' do
+ expect(rendered).not_to render_template('search/_category')
expect(rendered).not_to render_template('search/_results')
end
end
@@ -28,6 +30,7 @@ RSpec.describe 'search/show' do
let(:search_term) { 'Search Foo' }
it 'renders partials' do
+ expect(rendered).to render_template('search/_category')
expect(rendered).to render_template('search/_results')
end
diff --git a/yarn.lock b/yarn.lock
index cc86928454e..8b9789c8887 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1332,10 +1332,10 @@
jest-diff "^25.2.1"
pretty-format "^25.2.1"
-"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5":
- version "7.0.6"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
- integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
+"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
+ version "7.0.7"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
+ integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
"@types/json5@^0.0.29":
version "0.0.29"
@@ -1671,17 +1671,12 @@ acorn-jsx@^5.2.0, acorn-jsx@^5.3.1:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
-acorn-walk@^6.1.1:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
- integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
-
acorn-walk@^7.1.1:
version "7.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
-acorn@^6.0.7, acorn@^6.3.0, acorn@^6.4.1:
+acorn@^6.3.0, acorn@^6.4.1:
version "6.4.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
@@ -1721,7 +1716,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
-ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2191,15 +2186,15 @@ babel-jest@^26.5.2:
graceful-fs "^4.2.4"
slash "^3.0.0"
-babel-loader@^8.0.6:
- version "8.0.6"
- resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb"
- integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==
+babel-loader@^8.2.2:
+ version "8.2.2"
+ resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81"
+ integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==
dependencies:
- find-cache-dir "^2.0.0"
- loader-utils "^1.0.2"
- mkdirp "^0.5.1"
- pify "^4.0.1"
+ find-cache-dir "^3.3.1"
+ loader-utils "^1.4.0"
+ make-dir "^3.1.0"
+ schema-utils "^2.6.5"
babel-plugin-dynamic-import-node@^2.3.3:
version "2.3.3"
@@ -3329,10 +3324,10 @@ copy-to-clipboard@^3.0.8:
dependencies:
toggle-selection "^1.0.6"
-copy-webpack-plugin@^5.0.5:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz#5481a03dea1123d88a988c6ff8b78247214f0b88"
- integrity sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==
+copy-webpack-plugin@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz#8a889e1dcafa6c91c6cd4be1ad158f1d3823bae2"
+ integrity sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ==
dependencies:
cacache "^12.0.3"
find-cache-dir "^2.1.0"
@@ -3344,7 +3339,7 @@ copy-webpack-plugin@^5.0.5:
normalize-path "^3.0.0"
p-limit "^2.2.1"
schema-utils "^1.0.0"
- serialize-javascript "^2.1.2"
+ serialize-javascript "^4.0.0"
webpack-log "^2.0.0"
core-js-compat@^3.6.2:
@@ -5167,7 +5162,7 @@ finalhandler@~1.1.2:
statuses "~1.5.0"
unpipe "~1.0.0"
-find-cache-dir@^2.0.0, find-cache-dir@^2.1.0:
+find-cache-dir@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
@@ -7984,7 +7979,7 @@ make-dir@^2.0.0, make-dir@^2.1.0:
pify "^4.0.1"
semver "^5.6.0"
-make-dir@^3.0.0, make-dir@^3.0.2:
+make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@@ -9951,13 +9946,13 @@ raw-body@2.4.0:
iconv-lite "0.4.24"
unpipe "1.0.0"
-raw-loader@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.0.tgz#d639c40fb9d72b5c7f8abc1fb2ddb25b29d3d540"
- integrity sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==
+raw-loader@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6"
+ integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==
dependencies:
- loader-utils "^1.2.3"
- schema-utils "^2.5.0"
+ loader-utils "^2.0.0"
+ schema-utils "^3.0.0"
rc@^1.2.8, rc@~1.2.7:
version "1.2.8"
@@ -10546,7 +10541,7 @@ schema-utils@^1.0.0:
ajv-errors "^1.0.0"
ajv-keywords "^3.1.0"
-schema-utils@^2.0.0, schema-utils@^2.5.0, schema-utils@^2.7.0:
+schema-utils@^2.0.0, schema-utils@^2.5.0, schema-utils@^2.6.5, schema-utils@^2.7.0:
version "2.7.1"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
@@ -10555,6 +10550,15 @@ schema-utils@^2.0.0, schema-utils@^2.5.0, schema-utils@^2.7.0:
ajv "^6.12.4"
ajv-keywords "^3.5.2"
+schema-utils@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
+ integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
+ dependencies:
+ "@types/json-schema" "^7.0.6"
+ ajv "^6.12.5"
+ ajv-keywords "^3.5.2"
+
scss-tokenizer@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
@@ -12583,13 +12587,13 @@ webidl-conversions@^6.1.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
-webpack-bundle-analyzer@^3.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz#39b3a8f829ca044682bc6f9e011c95deb554aefd"
- integrity sha512-orUfvVYEfBMDXgEKAKVvab5iQ2wXneIEorGNsyuOyVYpjYrI7CUOhhXNDd3huMwQ3vNNWWlGP+hzflMFYNzi2g==
+webpack-bundle-analyzer@^3.9.0:
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz#f6f94db108fb574e415ad313de41a2707d33ef3c"
+ integrity sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==
dependencies:
- acorn "^6.0.7"
- acorn-walk "^6.1.1"
+ acorn "^7.1.1"
+ acorn-walk "^7.1.1"
bfj "^6.1.1"
chalk "^2.4.1"
commander "^2.18.0"
@@ -12597,7 +12601,7 @@ webpack-bundle-analyzer@^3.6.0:
express "^4.16.3"
filesize "^3.6.1"
gzip-size "^5.0.0"
- lodash "^4.17.15"
+ lodash "^4.17.19"
mkdirp "^0.5.1"
opener "^1.5.1"
ws "^6.0.0"