diff options
| author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-01 21:12:02 +0000 |
|---|---|---|
| committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-01 21:12:02 +0000 |
| commit | 71722304bef22d1b162207e3e25e4109b0d0f8c1 (patch) | |
| tree | a80189974f400946831c047ca5d4f018b9bacccc | |
| parent | 143a33345cf3607ad35ec31130cec4922bc1113c (diff) | |
| download | gitlab-ce-71722304bef22d1b162207e3e25e4109b0d0f8c1.tar.gz | |
Add latest changes from gitlab-org/gitlab@master
68 files changed, 543 insertions, 149 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index bea2cf66efd..5757a273926 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -168,6 +168,7 @@ Naming/FileName: - GitLab - JavaScript - VSCode + - JetBrains # default ones: - CLI - DSL diff --git a/app/assets/images/auth_buttons/auth0_64.png b/app/assets/images/auth_buttons/auth0_64.png Binary files differindex 5ad59659380..3b2d8562d9d 100644 --- a/app/assets/images/auth_buttons/auth0_64.png +++ b/app/assets/images/auth_buttons/auth0_64.png diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index f45af5fe08e..74e0e1b6225 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -1,7 +1,7 @@ /* eslint-disable class-methods-use-this */ import $ from 'jquery'; -import Cookies from 'js-cookie'; +import { setCookie } from '~/lib/utils/common_utils'; import createFlash from '~/flash'; import { s__ } from '~/locale'; import { localTimeAgo } from './lib/utils/datetime_utility'; @@ -55,7 +55,7 @@ export default class Activities { const filter = $sender.attr('id').split('_')[0]; $('.event-filter .active').removeClass('active'); - Cookies.set('event_filter', filter); + setCookie('event_filter', filter); $sender.closest('li').toggleClass('active'); } diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 43ca5b5cf89..aa735df7da5 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -2,10 +2,10 @@ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import $ from 'jquery'; -import Cookies from 'js-cookie'; import { uniq } from 'lodash'; +import { getCookie, setCookie, scrollToElement } from '~/lib/utils/common_utils'; import * as Emoji from '~/emoji'; -import { scrollToElement } from '~/lib/utils/common_utils'; + import { dispose, fixTitle } from '~/tooltips'; import createFlash from './flash'; import axios from './lib/utils/axios_utils'; @@ -506,7 +506,7 @@ export class AwardsHandler { addEmojiToFrequentlyUsedList(emoji) { if (this.emoji.isEmojiNameValid(emoji)) { this.frequentlyUsedEmojis = uniq(this.getFrequentlyUsedEmojis().concat(emoji)); - Cookies.set('frequently_used_emojis', this.frequentlyUsedEmojis.join(','), { expires: 365 }); + setCookie('frequently_used_emojis', this.frequentlyUsedEmojis.join(',')); } } @@ -514,7 +514,7 @@ export class AwardsHandler { return ( this.frequentlyUsedEmojis || (() => { - const frequentlyUsedEmojis = uniq((Cookies.get('frequently_used_emojis') || '').split(',')); + const frequentlyUsedEmojis = uniq((getCookie('frequently_used_emojis') || '').split(',')); this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter((inputName) => this.emoji.isEmojiNameValid(inputName), ); diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js index acb2355852d..9297b14aac9 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js @@ -1,9 +1,9 @@ import $ from 'jquery'; -import Cookies from 'js-cookie'; import { flatten } from 'lodash'; import Mousetrap from 'mousetrap'; import Vue from 'vue'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils'; + import findAndFollowLink from '~/lib/utils/navigation_utility'; import { refreshCurrentPage, visitUrl } from '~/lib/utils/url_utility'; import { @@ -161,10 +161,10 @@ export default class Shortcuts { static onTogglePerfBar(e) { e.preventDefault(); const performanceBarCookieName = 'perf_bar_enabled'; - if (parseBoolean(Cookies.get(performanceBarCookieName))) { - Cookies.set(performanceBarCookieName, 'false', { expires: 365, path: '/' }); + if (parseBoolean(getCookie(performanceBarCookieName))) { + setCookie(performanceBarCookieName, 'false', { path: '/' }); } else { - Cookies.set(performanceBarCookieName, 'true', { expires: 365, path: '/' }); + setCookie(performanceBarCookieName, 'true', { path: '/' }); } refreshCurrentPage(); } @@ -172,8 +172,8 @@ export default class Shortcuts { static onToggleCanary(e) { e.preventDefault(); const canaryCookieName = 'gitlab_canary'; - const currentValue = parseBoolean(Cookies.get(canaryCookieName)); - Cookies.set(canaryCookieName, (!currentValue).toString(), { + const currentValue = parseBoolean(getCookie(canaryCookieName)); + setCookie(canaryCookieName, (!currentValue).toString(), { expires: 365, path: '/', // next.gitlab.com uses a leading period. See https://gitlab.com/gitlab-org/gitlab/-/issues/350186 diff --git a/app/assets/javascripts/blob/pipeline_tour_success_modal.vue b/app/assets/javascripts/blob/pipeline_tour_success_modal.vue index 47a0c4ba2d1..b4ca29114cb 100644 --- a/app/assets/javascripts/blob/pipeline_tour_success_modal.vue +++ b/app/assets/javascripts/blob/pipeline_tour_success_modal.vue @@ -1,6 +1,6 @@ <script> import { GlModal, GlSprintf, GlLink, GlButton } from '@gitlab/ui'; -import Cookies from 'js-cookie'; +import { getCookie, removeCookie } from '~/lib/utils/common_utils'; import { __, s__ } from '~/locale'; import Tracking from '~/tracking'; @@ -62,7 +62,7 @@ export default { return this.commitCookiePath || this.projectMergeRequestsPath; }, commitCookiePath() { - const cookieVal = Cookies.get(this.commitCookie); + const cookieVal = getCookie(this.commitCookie); if (cookieVal !== 'true') return cookieVal; return ''; @@ -85,7 +85,7 @@ export default { }, methods: { disableModalFromRenderingAgain() { - Cookies.remove(this.commitCookie); + removeCookie(this.commitCookie); }, }, }; diff --git a/app/assets/javascripts/broadcast_notification.js b/app/assets/javascripts/broadcast_notification.js index 2cf2e922f68..34282c6932e 100644 --- a/app/assets/javascripts/broadcast_notification.js +++ b/app/assets/javascripts/broadcast_notification.js @@ -1,4 +1,4 @@ -import Cookies from 'js-cookie'; +import { setCookie } from '~/lib/utils/common_utils'; const handleOnDismiss = ({ currentTarget }) => { currentTarget.removeEventListener('click', handleOnDismiss); @@ -6,7 +6,7 @@ const handleOnDismiss = ({ currentTarget }) => { dataset: { id, expireDate }, } = currentTarget; - Cookies.set(`hide_broadcast_message_${id}`, true, { expires: new Date(expireDate) }); + setCookie(`hide_broadcast_message_${id}`, true, { expires: new Date(expireDate) }); const notification = document.querySelector(`.js-broadcast-notification-${id}`); notification.parentNode.removeChild(notification); diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue index e630ce71bd3..2e198c59926 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue @@ -14,8 +14,8 @@ import { GlModal, GlSprintf, } from '@gitlab/ui'; -import Cookies from 'js-cookie'; import { mapActions, mapState } from 'vuex'; +import { getCookie, setCookie } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; import Tracking from '~/tracking'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; @@ -59,7 +59,7 @@ export default { mixins: [glFeatureFlagsMixin(), trackingMixin], data() { return { - isTipDismissed: Cookies.get(AWS_TIP_DISMISSED_COOKIE_NAME) === 'true', + isTipDismissed: getCookie(AWS_TIP_DISMISSED_COOKIE_NAME) === 'true', validationErrorEventProperty: '', }; }, @@ -176,7 +176,7 @@ export default { 'setVariableProtected', ]), dismissTip() { - Cookies.set(AWS_TIP_DISMISSED_COOKIE_NAME, 'true', { expires: 90 }); + setCookie(AWS_TIP_DISMISSED_COOKIE_NAME, 'true', { expires: 90 }); this.isTipDismissed = true; }, deleteVarAndClose() { diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js index 08942374120..d1a68e80608 100644 --- a/app/assets/javascripts/contextual_sidebar.js +++ b/app/assets/javascripts/contextual_sidebar.js @@ -1,10 +1,9 @@ import { GlBreakpointInstance as bp, breakpoints } from '@gitlab/ui/dist/utils'; import $ from 'jquery'; -import Cookies from 'js-cookie'; import { debounce } from 'lodash'; +import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils'; import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; -import { parseBoolean } from '~/lib/utils/common_utils'; export const SIDEBAR_COLLAPSED_CLASS = 'js-sidebar-collapsed'; @@ -59,7 +58,7 @@ export default class ContextualSidebar { if (!ContextualSidebar.isDesktopBreakpoint()) { return; } - Cookies.set('sidebar_collapsed', value, { expires: 365 * 10 }); + setCookie('sidebar_collapsed', value, { expires: 365 * 10 }); } toggleSidebarNav(show) { @@ -111,7 +110,7 @@ export default class ContextualSidebar { if (!ContextualSidebar.isDesktopBreakpoint()) { this.toggleSidebarNav(false); } else { - const collapse = parseBoolean(Cookies.get('sidebar_collapsed')); + const collapse = parseBoolean(getCookie('sidebar_collapsed')); this.toggleCollapsedSidebar(collapse, true); } diff --git a/app/assets/javascripts/cycle_analytics/components/base.vue b/app/assets/javascripts/cycle_analytics/components/base.vue index bdfabb8e846..210e76115a2 100644 --- a/app/assets/javascripts/cycle_analytics/components/base.vue +++ b/app/assets/javascripts/cycle_analytics/components/base.vue @@ -1,7 +1,7 @@ <script> import { GlLoadingIcon } from '@gitlab/ui'; -import Cookies from 'js-cookie'; import { mapActions, mapState, mapGetters } from 'vuex'; +import { getCookie, setCookie } from '~/lib/utils/common_utils'; import { toYmd } from '~/analytics/shared/utils'; import PathNavigation from '~/cycle_analytics/components/path_navigation.vue'; import StageTable from '~/cycle_analytics/components/stage_table.vue'; @@ -35,7 +35,7 @@ export default { }, data() { return { - isOverviewDialogDismissed: Cookies.get(OVERVIEW_DIALOG_COOKIE), + isOverviewDialogDismissed: getCookie(OVERVIEW_DIALOG_COOKIE), }; }, computed: { @@ -134,7 +134,7 @@ export default { }, dismissOverviewDialog() { this.isOverviewDialogDismissed = true; - Cookies.set(OVERVIEW_DIALOG_COOKIE, '1', { expires: 365 }); + setCookie(OVERVIEW_DIALOG_COOKIE, '1'); }, isUserAllowed(id) { const { permissions } = this; diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js index 4ab3f140b61..ab57cbbc7c0 100644 --- a/app/assets/javascripts/deprecated_notes.js +++ b/app/assets/javascripts/deprecated_notes.js @@ -13,7 +13,6 @@ deprecated_notes_spec.js is the spec for the legacy, jQuery notes application. I import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; import Autosize from 'autosize'; import $ from 'jquery'; -import Cookies from 'js-cookie'; import { escape, uniqueId } from 'lodash'; import Vue from 'vue'; import '~/lib/utils/jquery_at_who'; @@ -28,6 +27,7 @@ import { defaultAutocompleteConfig } from './gfm_auto_complete'; import GLForm from './gl_form'; import axios from './lib/utils/axios_utils'; import { + getCookie, isInViewport, getPagePath, scrollToElement, @@ -121,7 +121,7 @@ export default class Notes { } setViewType(view) { - this.view = Cookies.get('diff_view') || view; + this.view = getCookie('diff_view') || view; } addBinding() { @@ -473,7 +473,7 @@ export default class Notes { } isParallelView() { - return Cookies.get('diff_view') === 'parallel'; + return getCookie('diff_view') === 'parallel'; } /** diff --git a/app/assets/javascripts/design_management/components/design_sidebar.vue b/app/assets/javascripts/design_management/components/design_sidebar.vue index 6d0ed3b08a3..81d0b6d0df4 100644 --- a/app/assets/javascripts/design_management/components/design_sidebar.vue +++ b/app/assets/javascripts/design_management/components/design_sidebar.vue @@ -1,7 +1,7 @@ <script> import { GlCollapse, GlButton, GlPopover } from '@gitlab/ui'; -import Cookies from 'js-cookie'; -import { parseBoolean, isLoggedIn } from '~/lib/utils/common_utils'; +import { getCookie, setCookie, parseBoolean, isLoggedIn } from '~/lib/utils/common_utils'; + import { s__ } from '~/locale'; import Participants from '~/sidebar/components/participants/participants.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; @@ -53,7 +53,7 @@ export default { }, data() { return { - isResolvedCommentsPopoverHidden: parseBoolean(Cookies.get(this.$options.cookieKey)), + isResolvedCommentsPopoverHidden: parseBoolean(getCookie(this.$options.cookieKey)), discussionWithOpenForm: '', isLoggedIn: isLoggedIn(), }; @@ -96,7 +96,7 @@ export default { methods: { handleSidebarClick() { this.isResolvedCommentsPopoverHidden = true; - Cookies.set(this.$options.cookieKey, 'true', { expires: 365 * 10 }); + setCookie(this.$options.cookieKey, 'true', { expires: 365 * 10 }); this.updateActiveDiscussion(); }, updateActiveDiscussion(id) { diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js index 260ebdf2141..dfe29e767c9 100644 --- a/app/assets/javascripts/diffs/index.js +++ b/app/assets/javascripts/diffs/index.js @@ -1,7 +1,7 @@ -import Cookies from 'js-cookie'; import Vue from 'vue'; import { mapActions, mapState, mapGetters } from 'vuex'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { getCookie, setCookie, parseBoolean, removeCookie } from '~/lib/utils/common_utils'; + import { getParameterValues } from '~/lib/utils/url_utility'; import eventHub from '../notes/event_hub'; import diffsApp from './components/app.vue'; @@ -58,14 +58,14 @@ export default function initDiffsApp(store) { // Check for cookie and save that setting for future use. // Then delete the cookie as we are phasing it out and using the database as SSOT. // NOTE: This can/should be removed later - if (Cookies.get(DIFF_WHITESPACE_COOKIE_NAME)) { - const hideWhitespace = Cookies.get(DIFF_WHITESPACE_COOKIE_NAME); + if (getCookie(DIFF_WHITESPACE_COOKIE_NAME)) { + const hideWhitespace = getCookie(DIFF_WHITESPACE_COOKIE_NAME); this.setShowWhitespace({ url: this.endpointUpdateUser, showWhitespace: hideWhitespace !== '1', trackClick: false, }); - Cookies.remove(DIFF_WHITESPACE_COOKIE_NAME); + removeCookie(DIFF_WHITESPACE_COOKIE_NAME); } else { // This is only to set the the user preference in Vuex for use later this.setShowWhitespace({ @@ -77,7 +77,7 @@ export default function initDiffsApp(store) { const vScrollingParam = getParameterValues('virtual_scrolling')[0]; if (vScrollingParam === 'false' || vScrollingParam === 'true') { - Cookies.set('diffs_virtual_scrolling', vScrollingParam); + setCookie('diffs_virtual_scrolling', vScrollingParam); } }, methods: { diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 692cb913a57..572949e585a 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -1,9 +1,14 @@ -import Cookies from 'js-cookie'; import Vue from 'vue'; +import { + setCookie, + handleLocationHash, + historyPushState, + scrollToElement, +} from '~/lib/utils/common_utils'; import createFlash from '~/flash'; import { diffViewerModes } from '~/ide/constants'; import axios from '~/lib/utils/axios_utils'; -import { handleLocationHash, historyPushState, scrollToElement } from '~/lib/utils/common_utils'; + import httpStatusCodes from '~/lib/utils/http_status'; import Poll from '~/lib/utils/poll'; import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility'; @@ -369,7 +374,7 @@ export const setRenderIt = ({ commit }, file) => commit(types.RENDER_FILE, file) export const setInlineDiffViewType = ({ commit }) => { commit(types.SET_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE); - Cookies.set(DIFF_VIEW_COOKIE_NAME, INLINE_DIFF_VIEW_TYPE); + setCookie(DIFF_VIEW_COOKIE_NAME, INLINE_DIFF_VIEW_TYPE); const url = mergeUrlParams({ view: INLINE_DIFF_VIEW_TYPE }, window.location.href); historyPushState(url); @@ -381,7 +386,7 @@ export const setInlineDiffViewType = ({ commit }) => { export const setParallelDiffViewType = ({ commit }) => { commit(types.SET_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE); - Cookies.set(DIFF_VIEW_COOKIE_NAME, PARALLEL_DIFF_VIEW_TYPE); + setCookie(DIFF_VIEW_COOKIE_NAME, PARALLEL_DIFF_VIEW_TYPE); const url = mergeUrlParams({ view: PARALLEL_DIFF_VIEW_TYPE }, window.location.href); historyPushState(url); diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js index ca85be5d829..83c7f8c814b 100644 --- a/app/assets/javascripts/diffs/store/getters.js +++ b/app/assets/javascripts/diffs/store/getters.js @@ -1,4 +1,4 @@ -import Cookies from 'js-cookie'; +import { getCookie } from '~/lib/utils/common_utils'; import { getParameterValues } from '~/lib/utils/url_utility'; import { __, n__ } from '~/locale'; import { @@ -175,7 +175,7 @@ export function suggestionCommitMessage(state, _, rootState) { } export const isVirtualScrollingEnabled = (state) => { - const vSrollerCookie = Cookies.get('diffs_virtual_scrolling'); + const vSrollerCookie = getCookie('diffs_virtual_scrolling'); if (state.disableVirtualScroller) { return false; diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js index 5f66360a040..329db1fe2cf 100644 --- a/app/assets/javascripts/diffs/store/modules/diff_state.js +++ b/app/assets/javascripts/diffs/store/modules/diff_state.js @@ -1,10 +1,10 @@ -import Cookies from 'js-cookie'; +import { getCookie } from '~/lib/utils/common_utils'; import { getParameterValues } from '~/lib/utils/url_utility'; import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants'; const getViewTypeFromQueryString = () => getParameterValues('view')[0]; -const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME); +const viewTypeFromCookie = getCookie(DIFF_VIEW_COOKIE_NAME); const defaultViewType = INLINE_DIFF_VIEW_TYPE; export default () => ({ diff --git a/app/assets/javascripts/emoji/components/utils.js b/app/assets/javascripts/emoji/components/utils.js index 3465a8ae7e6..5eec0992896 100644 --- a/app/assets/javascripts/emoji/components/utils.js +++ b/app/assets/javascripts/emoji/components/utils.js @@ -1,5 +1,5 @@ -import Cookies from 'js-cookie'; import { chunk, memoize, uniq } from 'lodash'; +import { getCookie, setCookie } from '~/lib/utils/common_utils'; import { initEmojiMap, getEmojiCategoryMap } from '~/emoji'; import { EMOJIS_PER_ROW, @@ -13,7 +13,7 @@ export const generateCategoryHeight = (emojisLength) => emojisLength * EMOJI_ROW_HEIGHT + CATEGORY_ROW_HEIGHT; export const getFrequentlyUsedEmojis = () => { - const savedEmojis = Cookies.get(FREQUENTLY_USED_COOKIE_KEY); + const savedEmojis = getCookie(FREQUENTLY_USED_COOKIE_KEY); if (!savedEmojis) return null; @@ -30,13 +30,13 @@ export const getFrequentlyUsedEmojis = () => { export const addToFrequentlyUsed = (emoji) => { const frequentlyUsedEmojis = uniq( - (Cookies.get(FREQUENTLY_USED_COOKIE_KEY) || '') + (getCookie(FREQUENTLY_USED_COOKIE_KEY) || '') .split(',') .filter((e) => e) .concat(emoji), ); - Cookies.set(FREQUENTLY_USED_COOKIE_KEY, frequentlyUsedEmojis.join(','), { expires: 365 }); + setCookie(FREQUENTLY_USED_COOKIE_KEY, frequentlyUsedEmojis.join(',')); }; export const hasFrequentlyUsedEmojis = () => getFrequentlyUsedEmojis() !== null; diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index 0d7a475eb8e..071c95b8f0a 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -4,7 +4,7 @@ * causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a */ -import Cookies from 'js-cookie'; +import { getCookie } from '~/lib/utils/common_utils'; const LINE_NUMBER_CLASS = 'diff-line-num'; const UNFOLDABLE_LINE_CLASS = 'js-unfold'; @@ -29,7 +29,7 @@ export default { $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('canCreateNote') === ''; } - this.isParallelView = Cookies.get('diff_view') === 'parallel'; + this.isParallelView = getCookie('diff_view') === 'parallel'; if (this.userCanCreateNote) { $diffFile diff --git a/app/assets/javascripts/groups/landing.js b/app/assets/javascripts/groups/landing.js index bfb4d9ce67b..ed76bebf843 100644 --- a/app/assets/javascripts/groups/landing.js +++ b/app/assets/javascripts/groups/landing.js @@ -1,5 +1,4 @@ -import Cookies from 'js-cookie'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils'; class Landing { constructor(landingElement, dismissButton, cookieName) { @@ -27,11 +26,11 @@ class Landing { dismissLanding() { this.landingElement.classList.add('hidden'); - Cookies.set(this.cookieName, 'true', { expires: 365 }); + setCookie(this.cookieName, 'true'); } isDismissed() { - return parseBoolean(Cookies.get(this.cookieName)); + return parseBoolean(getCookie(this.cookieName)); } } diff --git a/app/assets/javascripts/issuable/issuable_context.js b/app/assets/javascripts/issuable/issuable_context.js index 453305dd6e0..37001d00a27 100644 --- a/app/assets/javascripts/issuable/issuable_context.js +++ b/app/assets/javascripts/issuable/issuable_context.js @@ -1,6 +1,6 @@ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import $ from 'jquery'; -import Cookies from 'js-cookie'; +import { setCookie } from '~/lib/utils/common_utils'; import { loadCSSFile } from '~/lib/utils/css_utils'; import UsersSelect from '~/users_select'; @@ -62,7 +62,7 @@ export default class IssuableContext { const supportedSizes = ['xs', 'sm', 'md']; if (supportedSizes.includes(bpBreakpoint)) { - Cookies.set('collapsed_gutter', true); + setCookie('collapsed_gutter', true); } }); } diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index eff00dff7a7..cf6ce2c4889 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -705,7 +705,10 @@ export const scopedLabelKey = ({ title = '' }) => { }; // Methods to set and get Cookie -export const setCookie = (name, value) => Cookies.set(name, value, { expires: 365 }); +export const setCookie = (name, value, attributes) => { + const defaults = { expires: 365, secure: Boolean(window.gon?.secure) }; + Cookies.set(name, value, { ...defaults, ...attributes }); +}; export const getCookie = (name) => Cookies.get(name); diff --git a/app/assets/javascripts/merge_conflicts/store/actions.js b/app/assets/javascripts/merge_conflicts/store/actions.js index df515c4ac1a..9c101da52f5 100644 --- a/app/assets/javascripts/merge_conflicts/store/actions.js +++ b/app/assets/javascripts/merge_conflicts/store/actions.js @@ -1,4 +1,4 @@ -import Cookies from 'js-cookie'; +import { setCookie } from '~/lib/utils/common_utils'; import createFlash from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { __ } from '~/locale'; @@ -51,7 +51,7 @@ export const setFailedRequest = ({ commit }, message) => { export const setViewType = ({ commit }, viewType) => { commit(types.SET_VIEW_TYPE, viewType); - Cookies.set('diff_view', viewType); + setCookie('diff_view', viewType); }; export const setSubmitState = ({ commit }, isSubmitting) => { diff --git a/app/assets/javascripts/merge_conflicts/store/state.js b/app/assets/javascripts/merge_conflicts/store/state.js index 8f700f58e54..7a2e28183a7 100644 --- a/app/assets/javascripts/merge_conflicts/store/state.js +++ b/app/assets/javascripts/merge_conflicts/store/state.js @@ -1,7 +1,7 @@ -import Cookies from 'js-cookie'; +import { getCookie } from '~/lib/utils/common_utils'; import { VIEW_TYPES } from '../constants'; -const diffViewType = Cookies.get('diff_view'); +const diffViewType = getCookie('diff_view'); export default () => ({ isLoading: true, diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index a40caea1223..ad0117844cd 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -1,20 +1,21 @@ /* eslint-disable no-new, class-methods-use-this */ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import $ from 'jquery'; -import Cookies from 'js-cookie'; import Vue from 'vue'; +import { + getCookie, + parseUrlPathname, + isMetaClick, + parseBoolean, + scrollToElement, +} from '~/lib/utils/common_utils'; import createEventHub from '~/helpers/event_hub_factory'; import BlobForkSuggestion from './blob/blob_fork_suggestion'; import Diff from './diff'; import createFlash from './flash'; import { initDiffStatsDropdown } from './init_diff_stats_dropdown'; import axios from './lib/utils/axios_utils'; -import { - parseUrlPathname, - isMetaClick, - parseBoolean, - scrollToElement, -} from './lib/utils/common_utils'; + import { localTimeAgo } from './lib/utils/datetime_utility'; import { isInVueNoteablePage } from './lib/utils/dom_utils'; import { __ } from './locale'; @@ -514,7 +515,7 @@ export default class MergeRequestTabs { // Expand the issuable sidebar unless the user explicitly collapsed it expandView() { - if (parseBoolean(Cookies.get('collapsed_gutter'))) { + if (parseBoolean(getCookie('collapsed_gutter'))) { return; } const $gutterBtn = $('.js-sidebar-toggle'); diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue index 42b08bcaa7b..ee70ff858be 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue @@ -1,8 +1,8 @@ <script> import { GlButton } from '@gitlab/ui'; -import Cookies from 'js-cookie'; import Vue from 'vue'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils'; + import Translate from '../../../../../vue_shared/translate'; Vue.use(Translate); @@ -17,13 +17,13 @@ export default { inject: ['docsUrl', 'illustrationUrl'], data() { return { - calloutDismissed: parseBoolean(Cookies.get(cookieKey)), + calloutDismissed: parseBoolean(getCookie(cookieKey)), }; }, methods: { dismissCallout() { this.calloutDismissed = true; - Cookies.set(cookieKey, this.calloutDismissed, { expires: 365 }); + setCookie(cookieKey, this.calloutDismissed); }, }, }; diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js index a26aeeb6db4..0c17bf2f344 100644 --- a/app/assets/javascripts/pages/projects/project.js +++ b/app/assets/javascripts/pages/projects/project.js @@ -1,7 +1,7 @@ /* eslint-disable func-names, no-return-assign */ import $ from 'jquery'; -import Cookies from 'js-cookie'; +import { setCookie } from '~/lib/utils/common_utils'; import initClonePanel from '~/clone_panel'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; import createFlash from '~/flash'; @@ -24,19 +24,19 @@ export default class Project { } $('.js-hide-no-ssh-message').on('click', function (e) { - Cookies.set('hide_no_ssh_message', 'false'); + setCookie('hide_no_ssh_message', 'false'); $(this).parents('.js-no-ssh-key-message').remove(); return e.preventDefault(); }); $('.js-hide-no-password-message').on('click', function (e) { - Cookies.set('hide_no_password_message', 'false'); + setCookie('hide_no_password_message', 'false'); $(this).parents('.js-no-password-message').remove(); return e.preventDefault(); }); $('.hide-auto-devops-implicitly-enabled-banner').on('click', function (e) { const projectId = $(this).data('project-id'); const cookieKey = `hide_auto_devops_implicitly_enabled_banner_${projectId}`; - Cookies.set(cookieKey, 'false'); + setCookie(cookieKey, 'false'); $(this).parents('.auto-devops-implicitly-enabled-banner').remove(); return e.preventDefault(); }); diff --git a/app/assets/javascripts/pages/users/index.js b/app/assets/javascripts/pages/users/index.js index 58ceb524360..5cbb7a06bc1 100644 --- a/app/assets/javascripts/pages/users/index.js +++ b/app/assets/javascripts/pages/users/index.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import Cookies from 'js-cookie'; +import { setCookie } from '~/lib/utils/common_utils'; import UserCallout from '~/user_callout'; import UserTabs from './user_tabs'; @@ -10,7 +10,7 @@ function initUserProfile(action) { // hide project limit message $('.hide-project-limit-message').on('click', (e) => { e.preventDefault(); - Cookies.set('hide_project_limit_message', 'false'); + setCookie('hide_project_limit_message', 'false'); $(this).parents('.project-limit-message').remove(); }); } diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index ee9533bbec3..009afe03ea6 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -1,7 +1,7 @@ /* eslint-disable func-names, consistent-return, no-param-reassign */ import $ from 'jquery'; -import Cookies from 'js-cookie'; +import { setCookie } from '~/lib/utils/common_utils'; import { hide, fixTitle } from '~/tooltips'; import createFlash from './flash'; import axios from './lib/utils/axios_utils'; @@ -80,7 +80,7 @@ Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { hide($this); if (!triggered) { - Cookies.set('collapsed_gutter', $('.right-sidebar').hasClass('right-sidebar-collapsed')); + setCookie('collapsed_gutter', $('.right-sidebar').hasClass('right-sidebar-collapsed')); } }; diff --git a/app/assets/javascripts/serverless/survey_banner.vue b/app/assets/javascripts/serverless/survey_banner.vue index c48c294c0f7..1a49277efa7 100644 --- a/app/assets/javascripts/serverless/survey_banner.vue +++ b/app/assets/javascripts/serverless/survey_banner.vue @@ -1,7 +1,6 @@ <script> import { GlBanner } from '@gitlab/ui'; -import Cookies from 'js-cookie'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils'; export default { components: { @@ -19,13 +18,13 @@ export default { }; }, created() { - if (parseBoolean(Cookies.get('hide_serverless_survey'))) { + if (parseBoolean(getCookie('hide_serverless_survey'))) { this.visible = false; } }, methods: { handleClose() { - Cookies.set('hide_serverless_survey', 'true', { expires: 365 * 10 }); + setCookie('hide_serverless_survey', 'true', { expires: 365 * 10 }); this.visible = false; }, }, diff --git a/app/assets/javascripts/user_callout.js b/app/assets/javascripts/user_callout.js index 44e54c85f3c..ee23f8c5a0c 100644 --- a/app/assets/javascripts/user_callout.js +++ b/app/assets/javascripts/user_callout.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import Cookies from 'js-cookie'; +import { getCookie, setCookie } from '~/lib/utils/common_utils'; export default class UserCallout { constructor(options = {}) { @@ -9,7 +9,7 @@ export default class UserCallout { this.userCalloutBody = $(`.${className}`); this.cookieName = this.userCalloutBody.data('uid'); - this.isCalloutDismissed = Cookies.get(this.cookieName); + this.isCalloutDismissed = getCookie(this.cookieName); this.init(); } @@ -30,7 +30,7 @@ export default class UserCallout { cookieOptions.path = this.userCalloutBody.data('projectPath'); } - Cookies.set(this.cookieName, 'true', cookieOptions); + setCookie(this.cookieName, 'true', cookieOptions); if ($currentTarget.hasClass('close') || $currentTarget.hasClass('js-close')) { this.userCalloutBody.remove(); diff --git a/app/assets/javascripts/vue_alerts.js b/app/assets/javascripts/vue_alerts.js index b44f787cf30..f3bf121c0f8 100644 --- a/app/assets/javascripts/vue_alerts.js +++ b/app/assets/javascripts/vue_alerts.js @@ -1,6 +1,6 @@ import Vue from 'vue'; -import Cookies from 'js-cookie'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { setCookie, parseBoolean } from '~/lib/utils/common_utils'; + import DismissibleAlert from '~/vue_shared/components/dismissible_alert.vue'; const getCookieExpirationPeriod = (expirationPeriod) => { @@ -33,7 +33,7 @@ const mountVueAlert = (el) => { if (!dismissCookieName) { return; } - Cookies.set(dismissCookieName, true, { + setCookie(dismissCookieName, true, { expires: getCookieExpirationPeriod(dismissCookieExpire), }); }, diff --git a/app/assets/javascripts/vue_shared/issuable/sidebar/components/issuable_sidebar_root.vue b/app/assets/javascripts/vue_shared/issuable/sidebar/components/issuable_sidebar_root.vue index 99dcccd12ed..774267639fc 100644 --- a/app/assets/javascripts/vue_shared/issuable/sidebar/components/issuable_sidebar_root.vue +++ b/app/assets/javascripts/vue_shared/issuable/sidebar/components/issuable_sidebar_root.vue @@ -1,8 +1,8 @@ <script> import { GlIcon } from '@gitlab/ui'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; -import Cookies from 'js-cookie'; -import { parseBoolean } from '~/lib/utils/common_utils'; +import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils'; + import { USER_COLLAPSED_GUTTER_COOKIE } from '../constants'; export default { @@ -10,7 +10,7 @@ export default { GlIcon, }, data() { - const userExpanded = !parseBoolean(Cookies.get(USER_COLLAPSED_GUTTER_COOKIE)); + const userExpanded = !parseBoolean(getCookie(USER_COLLAPSED_GUTTER_COOKIE)); // We're deliberately keeping two different props for sidebar status; // 1. userExpanded reflects value based on cookie `collapsed_gutter`. @@ -46,7 +46,7 @@ export default { this.isExpanded = !this.isExpanded; this.userExpanded = this.isExpanded; - Cookies.set(USER_COLLAPSED_GUTTER_COOKIE, !this.userExpanded); + setCookie(USER_COLLAPSED_GUTTER_COOKIE, !this.userExpanded); this.updatePageContainerClass(); }, }, diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 10026e290e8..b858d457969 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -259,10 +259,9 @@ $tabs-holder-z-index: 250; > span { display: inline-block; max-width: 12.5em; - margin-bottom: -3px; + margin-bottom: -6px; white-space: nowrap; text-overflow: ellipsis; - line-height: 14px; overflow: hidden; } } diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 689ca32f6d9..ef229a2abec 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -31,6 +31,7 @@ class GraphqlController < ApplicationController before_action :authorize_access_api! before_action :set_user_last_activity before_action :track_vs_code_usage + before_action :track_jetbrains_usage before_action :disable_query_limiting before_action :limit_query_size @@ -137,6 +138,11 @@ class GraphqlController < ApplicationController .track_api_request_when_trackable(user_agent: request.user_agent, user: current_user) end + def track_jetbrains_usage + Gitlab::UsageDataCounters::JetBrainsPluginActivityUniqueCounter + .track_api_request_when_trackable(user_agent: request.user_agent, user: current_user) + end + def execute_multiplex GitlabSchema.multiplex(multiplex_queries, context: context) end diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb index 8054ecbd502..50b9c312292 100644 --- a/app/finders/users_finder.rb +++ b/app/finders/users_finder.rb @@ -32,7 +32,7 @@ class UsersFinder end def execute - users = User.all.order_id_desc + users = base_scope users = by_username(users) users = by_id(users) users = by_admins(users) @@ -53,6 +53,10 @@ class UsersFinder private + def base_scope + User.all.order_id_desc + end + def by_username(users) return users unless params[:username] diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 89b32a61e2e..c9966d87b68 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -62,23 +62,29 @@ module ProjectsHelper name: author.name } - author_html = [] + inject_classes = ["author-link"] + + if opts[:name] + inject_classes.concat(["js-user-link", opts[:extra_class], opts[:mobile_classes]]) + else + inject_classes.append( "has-tooltip" ) + end + inject_classes = inject_classes.compact.join(" ") + + author_html = [] # Build avatar image tag author_html << link_to_member_avatar(author, opts) if opts[:avatar] - # Build name span tag author_html << author_content_tag(author, opts) if opts[:name] - author_html << capture(&block) if block - author_html = author_html.join.html_safe if opts[:name] - link_to(author_html, user_path(author), class: "author-link js-user-link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}", data: data_attrs).html_safe + link_to(author_html, user_path(author), class: inject_classes, data: data_attrs).html_safe else title = opts[:title].sub(":name", sanitize(author.name)) - link_to(author_html, user_path(author), class: "author-link has-tooltip", title: title, data: { container: 'body', qa_selector: 'assignee_link' }).html_safe + link_to(author_html, user_path(author), class: inject_classes, title: title, data: { container: 'body', qa_selector: 'assignee_link' }).html_safe end end diff --git a/config/feature_flags/development/jobs_tab_vue.yml b/config/feature_flags/development/jobs_tab_vue.yml index 2958532922a..d6e797f66a5 100644 --- a/config/feature_flags/development/jobs_tab_vue.yml +++ b/config/feature_flags/development/jobs_tab_vue.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347371 milestone: '14.6' type: development group: group::pipeline execution -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/usage_data_i_code_review_user_jetbrains_api_request.yml b/config/feature_flags/development/usage_data_i_code_review_user_jetbrains_api_request.yml new file mode 100644 index 00000000000..3ab01c78a28 --- /dev/null +++ b/config/feature_flags/development/usage_data_i_code_review_user_jetbrains_api_request.yml @@ -0,0 +1,8 @@ +--- +name: usage_data_i_code_review_user_jetbrains_api_request +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78713 +rollout_issue_url: +milestone: '14.8' +type: development +group: group::code review +default_enabled: true diff --git a/config/initializers_before_autoloader/004_zeitwerk.rb b/config/initializers_before_autoloader/004_zeitwerk.rb index 4d8dcf4de6b..cb05bf1abb2 100644 --- a/config/initializers_before_autoloader/004_zeitwerk.rb +++ b/config/initializers_before_autoloader/004_zeitwerk.rb @@ -39,6 +39,7 @@ Rails.autoloaders.each do |autoloader| 'hangouts_chat_http_override' => 'HangoutsChatHTTPOverride', 'chunked_io' => 'ChunkedIO', 'http_io' => 'HttpIO', + 'jetbrains_plugin_activity_unique_counter' => 'JetBrainsPluginActivityUniqueCounter', 'json_formatter' => 'JSONFormatter', 'json_web_token' => 'JSONWebToken', 'as_json' => 'AsJSON', diff --git a/config/metrics/aggregates/code_review.yml b/config/metrics/aggregates/code_review.yml index 04a0b5e34e9..aee0e602e7b 100644 --- a/config/metrics/aggregates/code_review.yml +++ b/config/metrics/aggregates/code_review.yml @@ -73,6 +73,7 @@ - 'i_code_review_post_merge_click_cherry_pick' - 'i_code_review_post_merge_submit_revert_modal' - 'i_code_review_post_merge_submit_cherry_pick_modal' + - 'i_code_review_user_jetbrains_api_request' - name: code_review_category_monthly_active_users operator: OR source: redis @@ -144,3 +145,4 @@ time_frame: [7d, 28d] events: - 'i_code_review_user_vs_code_api_request' + - 'i_code_review_user_jetbrains_api_request' diff --git a/config/metrics/counts_28d/20220121140644_user_jetbrains_api_request_monthly.yml b/config/metrics/counts_28d/20220121140644_user_jetbrains_api_request_monthly.yml new file mode 100644 index 00000000000..deae309753a --- /dev/null +++ b/config/metrics/counts_28d/20220121140644_user_jetbrains_api_request_monthly.yml @@ -0,0 +1,26 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_user_jetbrains_api_request_monthly +description: Count of unique users per month who use GitLab plugin for JetBrains +product_section: dev +product_stage: create +product_group: group::code review +product_category: editor_extension +value_type: number +status: active +milestone: "14.8" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78713 +time_frame: 28d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +options: + events: + - i_code_review_user_jetbrains_api_request +performance_indicator_type: [] +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate diff --git a/config/metrics/counts_7d/20220121140634_user_jetbrains_api_request_weekly.yml b/config/metrics/counts_7d/20220121140634_user_jetbrains_api_request_weekly.yml new file mode 100644 index 00000000000..40e7b3ba04f --- /dev/null +++ b/config/metrics/counts_7d/20220121140634_user_jetbrains_api_request_weekly.yml @@ -0,0 +1,26 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_user_jetbrains_api_request_weekly +description: Count of unique users per month who use GitLab plugin for JetBrains +product_section: dev +product_stage: create +product_group: group::code review +product_category: editor_extension +value_type: number +status: active +milestone: "14.8" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78713 +time_frame: 7d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +options: + events: + - i_code_review_user_jetbrains_api_request +performance_indicator_type: [] +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate diff --git a/db/post_migrate/20220201173212_add_user_details_provisioning_index.rb b/db/post_migrate/20220201173212_add_user_details_provisioning_index.rb new file mode 100644 index 00000000000..a864ec7e395 --- /dev/null +++ b/db/post_migrate/20220201173212_add_user_details_provisioning_index.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddUserDetailsProvisioningIndex < Gitlab::Database::Migration[1.0] + disable_ddl_transaction! + + INDEX_NAME = 'idx_user_details_on_provisioned_by_group_id_user_id' + OLD_INDEX_NAME = 'index_user_details_on_provisioned_by_group_id' + + def up + add_concurrent_index :user_details, [:provisioned_by_group_id, :user_id], name: INDEX_NAME + remove_concurrent_index_by_name :user_details, OLD_INDEX_NAME + end + + def down + add_concurrent_index :user_details, :provisioned_by_group_id, name: OLD_INDEX_NAME + remove_concurrent_index_by_name :user_details, INDEX_NAME + end +end diff --git a/db/schema_migrations/20220201173212 b/db/schema_migrations/20220201173212 new file mode 100644 index 00000000000..b07c8e371a5 --- /dev/null +++ b/db/schema_migrations/20220201173212 @@ -0,0 +1 @@ +7a48d49d576d183198df370593642419da5707d8b018a23f541c448e2aa7ad65
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 085ce257403..cb5653a896d 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -25345,6 +25345,8 @@ CREATE INDEX idx_security_scans_on_scan_type ON security_scans USING btree (scan CREATE UNIQUE INDEX idx_serverless_domain_cluster_on_clusters_applications_knative ON serverless_domain_cluster USING btree (clusters_applications_knative_id); +CREATE INDEX idx_user_details_on_provisioned_by_group_id_user_id ON user_details USING btree (provisioned_by_group_id, user_id); + CREATE UNIQUE INDEX idx_vuln_signatures_on_occurrences_id_and_signature_sha ON vulnerability_finding_signatures USING btree (finding_id, signature_sha); CREATE UNIQUE INDEX idx_vuln_signatures_uniqueness_signature_sha ON vulnerability_finding_signatures USING btree (finding_id, algorithm_type, signature_sha); @@ -27987,8 +27989,6 @@ CREATE UNIQUE INDEX index_user_details_on_phone ON user_details USING btree (pho COMMENT ON INDEX index_user_details_on_phone IS 'JiHu-specific index'; -CREATE INDEX index_user_details_on_provisioned_by_group_id ON user_details USING btree (provisioned_by_group_id); - CREATE UNIQUE INDEX index_user_details_on_user_id ON user_details USING btree (user_id); CREATE INDEX index_user_group_callouts_on_group_id ON user_group_callouts USING btree (group_id); diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md index 43293385ed9..8c5bd295a57 100644 --- a/doc/administration/packages/container_registry.md +++ b/doc/administration/packages/container_registry.md @@ -1548,6 +1548,46 @@ To fix this you can do one of two things: We use a concrete example to illustrate how to diagnose a problem with the S3 setup. +#### Investigate a cleanup policy + +If you're unsure why your cleanup policy did or didn't delete a tag, execute the policy line by line +by running the below script from the [Rails console](../../administration/operations/rails_console.md). +This can help diagnose problems with the policy. + +```ruby +repo = ContainerRepository.find(<project_id>) +policy = repo.project.container_expiration_policy + +tags = repo.tags +tags.map(&:name) + +tags.reject!(&:latest?) +tags.map(&:name) + +regex_delete = ::Gitlab::UntrustedRegexp.new("\\A#{policy.name_regex}\\z") +regex_retain = ::Gitlab::UntrustedRegexp.new("\\A#{policy.name_regex_keep}\\z") + +tags.select! { |tag| regex_delete.match?(tag.name) && !regex_retain.match?(tag.name) } + +tags.map(&:name) + +now = DateTime.current +tags.sort_by! { |tag| tag.created_at || now }.reverse! # Lengthy operation + +tags = tags.drop(policy.keep_n) +tags.map(&:name) + +older_than_timestamp = ChronicDuration.parse(policy.older_than).seconds.ago + +tags.select! { |tag| tag.created_at && tag.created_at < older_than_timestamp } + +tags.map(&:name) +``` + +- The script builds the list of tags to delete (`tags`). +- `tags.map(&:name)` prints a list of tags to remove. This may be a lengthy operation. +- After each filter, check the list of `tags` to see if it contains the intended tags to destroy. + #### Unexpected 403 error during push A user attempted to enable an S3-backed Registry. The `docker login` step went diff --git a/doc/api/groups.md b/doc/api/groups.md index 764622a91ec..d1baa8c0a67 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -1523,3 +1523,75 @@ DELETE /groups/:id/push_rule | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) | + +## Provisioned users API **(PREMIUM)** + +> Introduced in GitLab 14.8. + +### List provisioned users + +Get a list of users provisioned by a given group. Does not include users provisioned by subgroups. +Requires at least the Maintainer role on the group. + +```plaintext +GET /group/:provisioned_group_id/provisioned_users +``` + +Parameters: + +| Attribute | Type | Required | Description | +|------------------------|----------|----------|-----------------------------------------------| +| `provisioned_group_id` | integer | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) | +| `username` | string | no | Get a single user with a specific username | +| `search` | string | no | Filter list by name, email or username | +| `active` | boolean | no | Filters only active users | +| `blocked` | boolean | no | Filters only blocked users | +| `created_after` | datetime | no | Return users created after the specified time | +| `created_before` | datetime | no | Return users created before the specified time | + +```json +[ + { + id: 66, + username: "user22", + name: "John Doe22", + state: "active", + avatar_url: "https://www.gravatar.com/avatar/xxx?s=80&d=identicon", + web_url: "http://my.gitlab.com/user22", + created_at: "2021-09-10T12:48:22.381Z", + bio: "", + location: null, + public_email: "", + skype: "", + linkedin: "", + twitter: "", + website_url: "", + organization: null, + job_title: "", + pronouns: null, + bot: false, + work_information: null, + followers: 0, + following: 0, + local_time: null, + last_sign_in_at: null, + confirmed_at: "2021-09-10T12:48:22.330Z", + last_activity_on: null, + email: "user22@example.org", + theme_id: 1, + color_scheme_id: 1, + projects_limit: 100000, + current_sign_in_at: null, + identities: [ ], + can_create_group: true, + can_create_project: true, + two_factor_enabled: false, + external: false, + private_profile: false, + commit_email: "user22@example.org", + shared_runners_minutes_limit: null, + extra_shared_runners_minutes_limit: null + }, + ... +] +``` diff --git a/doc/ci/runners/configure_runners.md b/doc/ci/runners/configure_runners.md index d439702572a..60e21653a45 100644 --- a/doc/ci/runners/configure_runners.md +++ b/doc/ci/runners/configure_runners.md @@ -332,10 +332,6 @@ try to preserve worktrees and try to re-use them by default. This has limitations when using the [Docker Machine executor](https://docs.gitlab.com/runner/executors/docker_machine.html). -It does not work for [the `kubernetes` executor](https://docs.gitlab.com/runner/executors/kubernetes.html), -but a [feature proposal](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3847) exists. -The `kubernetes` executor always clones into an temporary directory. - A Git strategy of `none` also re-uses the local working copy, but skips all Git operations normally done by GitLab. GitLab Runner pre-clone scripts are also skipped, if present. This strategy could mean you need to add `fetch` and `checkout` commands diff --git a/lib/api/api.rb b/lib/api/api.rb index bfd070ba6da..d47709311dd 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -76,6 +76,10 @@ module API Gitlab::UsageDataCounters::VSCodeExtensionActivityUniqueCounter.track_api_request_when_trackable(user_agent: request&.user_agent, user: @current_user) end + after do + Gitlab::UsageDataCounters::JetBrainsPluginActivityUniqueCounter.track_api_request_when_trackable(user_agent: request&.user_agent, user: @current_user) + end + # The locale is set to the current user's locale when `current_user` is loaded after { Gitlab::I18n.use_default_locale } diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index ac3b4de0988..2bd59415771 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -27,6 +27,7 @@ module Gitlab gon.revision = Gitlab.revision gon.feature_category = Gitlab::ApplicationContext.current_context_attribute(:feature_category).presence gon.gitlab_logo = ActionController::Base.helpers.asset_path('gitlab_logo.png') + gon.secure = Gitlab.config.gitlab.https gon.sprite_icons = IconsHelper.sprite_icon_path gon.sprite_file_icons = IconsHelper.sprite_file_icons_path gon.emoji_sprites_css_path = ActionController::Base.helpers.stylesheet_path('emoji_sprites') diff --git a/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter.rb new file mode 100644 index 00000000000..f3d5be5e28f --- /dev/null +++ b/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module UsageDataCounters + module JetBrainsPluginActivityUniqueCounter + JETBRAINS_API_REQUEST_ACTION = 'i_code_review_user_jetbrains_api_request' + JETBRAINS_USER_AGENT_REGEX = /\Agitlab-jetbrains-plugin/.freeze + + class << self + def track_api_request_when_trackable(user_agent:, user:) + user_agent&.match?(JETBRAINS_USER_AGENT_REGEX) && track_unique_action_by_user(JETBRAINS_API_REQUEST_ACTION, user) + end + + private + + def track_unique_action_by_user(action, user) + return unless user + + track_unique_action(action, user.id) + end + + def track_unique_action(action, value) + Gitlab::UsageDataCounters::HLLRedisCounter.track_usage_event(action, value) + end + end + end + end +end diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml index 9668b727099..42c51ec3921 100644 --- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml +++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml @@ -127,6 +127,11 @@ redis_slot: code_review category: code_review aggregation: weekly +- name: i_code_review_user_jetbrains_api_request + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: usage_data_i_code_review_user_jetbrains_api_request - name: i_code_review_user_create_mr_from_issue redis_slot: code_review category: code_review @@ -51,7 +51,8 @@ module QA "smtp" => "SMTP", "otp" => "OTP", "jira_api" => "JiraAPI", - "registry_tls" => "RegistryTLS" + "registry_tls" => "RegistryTLS", + "jetbrains" => "JetBrains" ) loader.setup diff --git a/qa/qa/tools/reliable_report.rb b/qa/qa/tools/reliable_report.rb index b99b97c1ea6..27e54c2d8bf 100644 --- a/qa/qa/tools/reliable_report.rb +++ b/qa/qa/tools/reliable_report.rb @@ -58,7 +58,7 @@ module QA { title: "Reliable e2e test report", description: report_issue_body, - labels: "Quality,test,type::maintenance,reliable test report" + labels: "Quality,test,type::maintenance,reliable test report,automation:devops-mapping-disable" }, headers: { "PRIVATE-TOKEN" => gitlab_access_token } ) diff --git a/qa/spec/tools/reliable_report_spec.rb b/qa/spec/tools/reliable_report_spec.rb index 1ff62df34e0..85b2590d3aa 100644 --- a/qa/spec/tools/reliable_report_spec.rb +++ b/qa/spec/tools/reliable_report_spec.rb @@ -167,7 +167,7 @@ describe QA::Tools::ReliableReport do payload: { title: "Reliable e2e test report", description: issue_body, - labels: "Quality,test,type::maintenance,reliable test report" + labels: "Quality,test,type::maintenance,reliable test report,automation:devops-mapping-disable" } ) expect(slack_notifier).to have_received(:post).with( diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index 578ce04721c..95f60156c40 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -124,6 +124,16 @@ RSpec.describe GraphqlController do post :execute end + + it 'calls the track jetbrains api when trackable method' do + agent = 'gitlab-jetbrains-plugin/0.0.1 intellij-idea/2021.2.4 java/11.0.13 mac-os-x/aarch64/12.1' + request.env['HTTP_USER_AGENT'] = agent + + expect(Gitlab::UsageDataCounters::JetBrainsPluginActivityUniqueCounter) + .to receive(:track_api_request_when_trackable).with(user_agent: agent, user: user) + + post :execute + end end context 'when user uses an API token' do @@ -151,6 +161,16 @@ RSpec.describe GraphqlController do subject end + + it 'calls the track jetbrains api when trackable method' do + agent = 'gitlab-jetbrains-plugin/0.0.1 intellij-idea/2021.2.4 java/11.0.13 mac-os-x/aarch64/12.1' + request.env['HTTP_USER_AGENT'] = agent + + expect(Gitlab::UsageDataCounters::JetBrainsPluginActivityUniqueCounter) + .to receive(:track_api_request_when_trackable).with(user_agent: agent, user: user) + + subject + end end context 'when user is not logged in' do diff --git a/spec/frontend/broadcast_notification_spec.js b/spec/frontend/broadcast_notification_spec.js index 8d433946632..cd947cd417a 100644 --- a/spec/frontend/broadcast_notification_spec.js +++ b/spec/frontend/broadcast_notification_spec.js @@ -30,6 +30,7 @@ describe('broadcast message on dismiss', () => { expect(Cookies.set).toHaveBeenCalledWith('hide_broadcast_message_1', true, { expires: new Date(endsAt), + secure: false, }); }); }); diff --git a/spec/frontend/code_quality_walkthrough/components/step_spec.js b/spec/frontend/code_quality_walkthrough/components/step_spec.js index bdbcda5f902..b43629c2f96 100644 --- a/spec/frontend/code_quality_walkthrough/components/step_spec.js +++ b/spec/frontend/code_quality_walkthrough/components/step_spec.js @@ -118,7 +118,7 @@ describe('When the code_quality_walkthrough URL parameter is present', () => { expect(Cookies.set).toHaveBeenCalledWith( EXPERIMENT_NAME, { commit_ci_file: true, data: dummyContext }, - { expires: 365 }, + { expires: 365, secure: false }, ); }); diff --git a/spec/frontend/design_management/components/design_sidebar_spec.js b/spec/frontend/design_management/components/design_sidebar_spec.js index f690974db1b..a818a86bef6 100644 --- a/spec/frontend/design_management/components/design_sidebar_spec.js +++ b/spec/frontend/design_management/components/design_sidebar_spec.js @@ -254,7 +254,10 @@ describe('Design management design sidebar component', () => { it(`sets a ${cookieKey} cookie on clicking outside the popover`, () => { jest.spyOn(Cookies, 'set'); wrapper.trigger('click'); - expect(Cookies.set).toHaveBeenCalledWith(cookieKey, 'true', { expires: 365 * 10 }); + expect(Cookies.set).toHaveBeenCalledWith(cookieKey, 'true', { + expires: 365 * 10, + secure: false, + }); }); }); diff --git a/spec/frontend/emoji/components/utils_spec.js b/spec/frontend/emoji/components/utils_spec.js index 36521eb1051..03eeb6b6bf7 100644 --- a/spec/frontend/emoji/components/utils_spec.js +++ b/spec/frontend/emoji/components/utils_spec.js @@ -31,6 +31,7 @@ describe('addToFrequentlyUsed', () => { expect(Cookies.set).toHaveBeenCalledWith('frequently_used_emojis', 'thumbsup', { expires: 365, + secure: false, }); }); @@ -41,6 +42,7 @@ describe('addToFrequentlyUsed', () => { expect(Cookies.set).toHaveBeenCalledWith('frequently_used_emojis', 'thumbsdown,thumbsup', { expires: 365, + secure: false, }); }); @@ -51,6 +53,7 @@ describe('addToFrequentlyUsed', () => { expect(Cookies.set).toHaveBeenCalledWith('frequently_used_emojis', 'thumbsup', { expires: 365, + secure: false, }); }); }); diff --git a/spec/frontend/groups/landing_spec.js b/spec/frontend/groups/landing_spec.js index f90f541eb96..d60adea202b 100644 --- a/spec/frontend/groups/landing_spec.js +++ b/spec/frontend/groups/landing_spec.js @@ -159,7 +159,10 @@ describe('Landing', () => { }); it('should call Cookies.set', () => { - expect(Cookies.set).toHaveBeenCalledWith(test.cookieName, 'true', { expires: 365 }); + expect(Cookies.set).toHaveBeenCalledWith(test.cookieName, 'true', { + expires: 365, + secure: false, + }); }); }); diff --git a/spec/frontend/merge_conflicts/store/actions_spec.js b/spec/frontend/merge_conflicts/store/actions_spec.js index 8fa8765a9f9..3e1774a6d56 100644 --- a/spec/frontend/merge_conflicts/store/actions_spec.js +++ b/spec/frontend/merge_conflicts/store/actions_spec.js @@ -207,7 +207,10 @@ describe('merge conflicts actions', () => { ], [], () => { - expect(Cookies.set).toHaveBeenCalledWith('diff_view', payload); + expect(Cookies.set).toHaveBeenCalledWith('diff_view', payload, { + expires: 365, + secure: false, + }); done(); }, ); diff --git a/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js b/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js index 2f61b5e9800..c28a03b35d7 100644 --- a/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js +++ b/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js @@ -84,6 +84,7 @@ describe('Pipeline Schedule Callout', () => { expect(setCookiesSpy).toHaveBeenCalledWith('pipeline_schedules_callout_dismissed', true, { expires: 365, + secure: false, }); }); }); diff --git a/spec/frontend/serverless/survey_banner_spec.js b/spec/frontend/serverless/survey_banner_spec.js index 022aa47c113..3408d610089 100644 --- a/spec/frontend/serverless/survey_banner_spec.js +++ b/spec/frontend/serverless/survey_banner_spec.js @@ -37,7 +37,10 @@ describe('Knative survey banner', () => { wrapper.find(GlBanner).vm.$emit('close'); await nextTick(); - expect(Cookies.set).toHaveBeenCalledWith('hide_serverless_survey', 'true', { expires: 3650 }); + expect(Cookies.set).toHaveBeenCalledWith('hide_serverless_survey', 'true', { + expires: 3650, + secure: false, + }); expect(wrapper.find(GlBanner).exists()).toBe(false); }); diff --git a/spec/frontend/vue_shared/issuable/sidebar/components/issuable_sidebar_root_spec.js b/spec/frontend/vue_shared/issuable/sidebar/components/issuable_sidebar_root_spec.js index 1ef1304185d..47bf3c8ed83 100644 --- a/spec/frontend/vue_shared/issuable/sidebar/components/issuable_sidebar_root_spec.js +++ b/spec/frontend/vue_shared/issuable/sidebar/components/issuable_sidebar_root_spec.js @@ -70,7 +70,10 @@ describe('IssuableSidebarRoot', () => { it('updates "collapsed_gutter" cookie value and layout classes', async () => { await findToggleSidebarButton().trigger('click'); - expect(Cookies.set).toHaveBeenCalledWith(USER_COLLAPSED_GUTTER_COOKIE, true); + expect(Cookies.set).toHaveBeenCalledWith(USER_COLLAPSED_GUTTER_COOKIE, true, { + expires: 365, + secure: false, + }); assertPageLayoutClasses({ isExpanded: false }); }); }); diff --git a/spec/lib/gitlab/gon_helper_spec.rb b/spec/lib/gitlab/gon_helper_spec.rb index b8ed4cf608d..047873d8237 100644 --- a/spec/lib/gitlab/gon_helper_spec.rb +++ b/spec/lib/gitlab/gon_helper_spec.rb @@ -6,9 +6,41 @@ RSpec.describe Gitlab::GonHelper do let(:helper) do Class.new do include Gitlab::GonHelper + + def current_user + nil + end end.new end + describe '#add_gon_variables' do + let(:gon) { double('gon').as_null_object } + let(:https) { true } + + before do + allow(helper).to receive(:gon).and_return(gon) + stub_config_setting(https: https) + end + + context 'when HTTPS is enabled' do + it 'sets the secure flag to true' do + expect(gon).to receive(:secure=).with(true) + + helper.add_gon_variables + end + end + + context 'when HTTP is enabled' do + let(:https) { false } + + it 'sets the secure flag to false' do + expect(gon).to receive(:secure=).with(false) + + helper.add_gon_variables + end + end + end + describe '#push_frontend_feature_flag' do before do skip_feature_flags_yaml_validation diff --git a/spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb new file mode 100644 index 00000000000..64fa8d26d81 --- /dev/null +++ b/spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.shared_examples 'a tracked jetbrains unique action' do |event| + before do + stub_application_setting(usage_ping_enabled: true) + end + + def count_unique(date_from:, date_to:) + Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to) + end + + it 'tracks when the user agent is from jetbrains' do + aggregate_failures do + user_agent = { user_agent: 'gitlab-jetbrains-plugin/0.0.1 intellij-idea/2021.2.4 java/11.0.13 mac-os-x/aarch64/12.1' } + + expect(track_action(user: user1, **user_agent)).to be_truthy + expect(track_action(user: user1, **user_agent)).to be_truthy + expect(track_action(user: user2, **user_agent)).to be_truthy + + expect(count_unique(date_from: time - 1.week, date_to: time + 1.week)).to eq(2) + end + end + + it 'does not track when the user agent is not from jetbrains' do + aggregate_failures do + user_agent = { user_agent: 'normal_user_agent' } + + expect(track_action(user: user1, **user_agent)).to be_falsey + expect(track_action(user: user1, **user_agent)).to be_falsey + expect(track_action(user: user2, **user_agent)).to be_falsey + + expect(count_unique(date_from: time - 1.week, date_to: time + 1.week)).to eq(0) + end + end + + it 'does not track if user agent is not present' do + expect(track_action(user: nil, user_agent: nil)).to be_nil + end + + it 'does not track if user is not present' do + user_agent = { user_agent: 'gitlab-jetbrains-plugin/0.0.1 intellij-idea/2021.2.4 java/11.0.13 mac-os-x/aarch64/12.1' } + + expect(track_action(user: nil, **user_agent)).to be_nil + end +end + +RSpec.describe Gitlab::UsageDataCounters::JetBrainsPluginActivityUniqueCounter, :clean_gitlab_redis_shared_state do + let(:user1) { build(:user, id: 1) } + let(:user2) { build(:user, id: 2) } + let(:time) { Time.current } + + context 'when tracking a jetbrains api request' do + it_behaves_like 'a tracked jetbrains unique action' do + let(:action) { described_class::JETBRAINS_API_REQUEST_ACTION } + + def track_action(params) + described_class.track_api_request_when_trackable(**params) + end + end + end +end diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb index 882c79cb03f..127b1a6d4c4 100644 --- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb @@ -3,11 +3,11 @@ RSpec.shared_examples 'group and project packages query' do include GraphqlHelpers - let_it_be(:versionaless_package) { create(:maven_package, project: project1, version: nil) } - let_it_be(:maven_package) { create(:maven_package, project: project1, name: 'tab', version: '4.0.0', created_at: 5.days.ago) } - let_it_be(:package) { create(:npm_package, project: project1, name: 'uab', version: '5.0.0', created_at: 4.days.ago) } - let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'vab', version: '6.0.0', created_at: 3.days.ago) } - let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'zab', version: '7.0.0', created_at: 2.days.ago) } + let_it_be(:versionless_package) { create(:maven_package, project: project1, version: nil) } + let_it_be(:maven_package) { create(:maven_package, project: project1, name: 'bab', version: '6.0.0', created_at: 1.day.ago) } + let_it_be(:npm_package) { create(:npm_package, project: project1, name: 'cab', version: '7.0.0', created_at: 4.days.ago) } + let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'dab', version: '4.0.0', created_at: 3.days.ago) } + let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'aab', version: '5.0.0', created_at: 2.days.ago) } let_it_be(:composer_metadatum) do create(:composer_metadatum, package: composer_package, target_sha: 'afdeh', @@ -21,11 +21,11 @@ RSpec.shared_examples 'group and project packages query' do let(:fields) do <<~QUERY - count - nodes { - #{all_graphql_fields_for('packages'.classify, excluded: ['project'])} - metadata { #{query_graphql_fragment('ComposerMetadata')} } - } + count + nodes { + #{all_graphql_fields_for('packages'.classify, excluded: ['project'])} + metadata { #{query_graphql_fragment('ComposerMetadata')} } + } QUERY end @@ -47,7 +47,7 @@ RSpec.shared_examples 'group and project packages query' do it 'returns packages successfully' do expect(package_names).to contain_exactly( - package.name, + npm_package.name, maven_package.name, debian_package.name, composer_package.name @@ -88,7 +88,23 @@ RSpec.shared_examples 'group and project packages query' do end describe 'sorting and pagination' do - let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} } + let_it_be(:packages_order_map) do + { + TYPE_ASC: [maven_package, npm_package, composer_package, debian_package], + TYPE_DESC: [debian_package, composer_package, npm_package, maven_package], + + NAME_ASC: [debian_package, maven_package, npm_package, composer_package], + NAME_DESC: [composer_package, npm_package, maven_package, debian_package], + + VERSION_ASC: [composer_package, debian_package, maven_package, npm_package], + VERSION_DESC: [npm_package, maven_package, debian_package, composer_package], + + CREATED_ASC: [npm_package, composer_package, debian_package, maven_package], + CREATED_DESC: [maven_package, debian_package, composer_package, npm_package] + } + end + + let(:expected_packages) { sorted_packages.map { |package| global_id_of(package) } } let(:data_path) { [resource_type, :packages] } @@ -96,22 +112,14 @@ RSpec.shared_examples 'group and project packages query' do resource.add_reporter(current_user) end - [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order| + [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC, :CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order| context "#{order}" do - it_behaves_like 'sorted paginated query' do - let(:sort_param) { order } - let(:first_param) { 4 } - let(:all_records) { ascending_packages } - end - end - end + let(:sorted_packages) { packages_order_map.fetch(order) } - [:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order| - context "#{order}" do it_behaves_like 'sorted paginated query' do let(:sort_param) { order } let(:first_param) { 4 } - let(:all_records) { ascending_packages.reverse } + let(:all_records) { expected_packages } end end end @@ -180,7 +188,7 @@ RSpec.shared_examples 'group and project packages query' do context 'include_versionless' do let(:params) { { include_versionless: true } } - it { is_expected.to include({ "name" => versionaless_package.name }) } + it { is_expected.to include({ "name" => versionless_package.name }) } end end end |
