Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f4d6d3ec77
commit
c0f42c6d66
|
@ -1 +1 @@
|
|||
22f7db01953debe8e7c1c46ff2b1ffb5a143c566
|
||||
dc956109aa3e352cadc55ebba267b43fdd4fdc27
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -129,7 +129,7 @@ gem 'fog-local', '~> 0.6'
|
|||
gem 'fog-openstack', '~> 1.0'
|
||||
gem 'fog-rackspace', '~> 0.1.1'
|
||||
gem 'fog-aliyun', '~> 0.3'
|
||||
gem 'gitlab-fog-azure-rm', '~> 1.0.1', require: false
|
||||
gem 'gitlab-fog-azure-rm', '~> 1.1.1', require: false
|
||||
|
||||
# for Google storage
|
||||
gem 'google-api-client', '~> 0.33'
|
||||
|
|
|
@ -457,7 +457,7 @@ GEM
|
|||
activesupport (>= 3.0)
|
||||
request_store (>= 1.0)
|
||||
scientist (~> 1.6, >= 1.6.0)
|
||||
gitlab-fog-azure-rm (1.0.1)
|
||||
gitlab-fog-azure-rm (1.1.1)
|
||||
azure-storage-blob (~> 2.0)
|
||||
azure-storage-common (~> 2.0)
|
||||
fog-core (= 2.1.0)
|
||||
|
@ -1474,7 +1474,7 @@ DEPENDENCIES
|
|||
gitlab-chronic (~> 0.10.5)
|
||||
gitlab-dangerfiles (~> 2.0.0)
|
||||
gitlab-experiment (~> 0.5.4)
|
||||
gitlab-fog-azure-rm (~> 1.0.1)
|
||||
gitlab-fog-azure-rm (~> 1.1.1)
|
||||
gitlab-fog-google (~> 1.13)
|
||||
gitlab-labkit (~> 0.18.0)
|
||||
gitlab-license (~> 1.5)
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
GlDropdown,
|
||||
GlDropdownSectionHeader,
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlInfiniteScroll,
|
||||
} from '@gitlab/ui';
|
||||
import { throttle } from 'lodash';
|
||||
|
@ -25,7 +24,6 @@ export default {
|
|||
GlDropdown,
|
||||
GlDropdownSectionHeader,
|
||||
GlDropdownItem,
|
||||
GlDropdownDivider,
|
||||
GlInfiniteScroll,
|
||||
LogSimpleFilters,
|
||||
LogAdvancedFilters,
|
||||
|
@ -66,7 +64,7 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState('environmentLogs', ['environments', 'timeRange', 'logs', 'pods', 'managedApps']),
|
||||
...mapState('environmentLogs', ['environments', 'timeRange', 'logs', 'pods']),
|
||||
...mapGetters('environmentLogs', ['trace', 'showAdvancedFilters']),
|
||||
|
||||
showLoader() {
|
||||
|
@ -88,15 +86,12 @@ export default {
|
|||
});
|
||||
|
||||
this.fetchEnvironments(this.environmentsPath);
|
||||
this.fetchManagedApps(this.clustersPath);
|
||||
},
|
||||
methods: {
|
||||
...mapActions('environmentLogs', [
|
||||
'setInitData',
|
||||
'showEnvironment',
|
||||
'showManagedApp',
|
||||
'fetchEnvironments',
|
||||
'fetchManagedApps',
|
||||
'refreshPodLogs',
|
||||
'fetchMoreLogsPrepend',
|
||||
'dismissRequestEnvironmentsError',
|
||||
|
@ -107,9 +102,6 @@ export default {
|
|||
isCurrentEnvironment(envName) {
|
||||
return envName === this.environments.current;
|
||||
},
|
||||
isCurrentManagedApp(appName) {
|
||||
return appName === this.managedApps.current;
|
||||
},
|
||||
topReached() {
|
||||
if (!this.logs.isLoading) {
|
||||
this.fetchMoreLogsPrepend();
|
||||
|
@ -173,7 +165,7 @@ export default {
|
|||
<div class="flex-grow-0">
|
||||
<gl-dropdown
|
||||
id="environments-dropdown"
|
||||
:text="environments.current || managedApps.current"
|
||||
:text="environments.current"
|
||||
:disabled="environments.isLoading"
|
||||
class="gl-mr-3 gl-mb-3 gl-display-flex gl-md-display-block js-environments-dropdown"
|
||||
>
|
||||
|
@ -189,19 +181,6 @@ export default {
|
|||
>
|
||||
{{ env.name }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown-section-header>
|
||||
{{ s__('Environments|Managed apps') }}
|
||||
</gl-dropdown-section-header>
|
||||
<gl-dropdown-item
|
||||
v-for="app in managedApps.options"
|
||||
:key="app.id"
|
||||
:is-check-item="true"
|
||||
:is-checked="isCurrentManagedApp(app.name)"
|
||||
@click="showManagedApp(app.name)"
|
||||
>
|
||||
{{ app.name }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -13,5 +13,4 @@ export const tracking = {
|
|||
|
||||
export const logExplorerOptions = {
|
||||
environments: 'environments',
|
||||
managedApps: 'managedApps',
|
||||
};
|
||||
|
|
|
@ -25,15 +25,9 @@ const requestUntilData = (url, params) =>
|
|||
|
||||
const requestLogsUntilData = ({ commit, state }) => {
|
||||
const params = {};
|
||||
const type = state.environments.current
|
||||
? logExplorerOptions.environments
|
||||
: logExplorerOptions.managedApps;
|
||||
const type = logExplorerOptions.environments;
|
||||
const selectedObj = state[type].options.find(({ name }) => name === state[type].current);
|
||||
|
||||
const path =
|
||||
type === logExplorerOptions.environments
|
||||
? selectedObj.logs_api_path
|
||||
: selectedObj.gitlab_managed_apps_logs_path;
|
||||
const path = selectedObj.logs_api_path;
|
||||
|
||||
if (state.pods.current) {
|
||||
params.pod_name = state.pods.current;
|
||||
|
@ -106,11 +100,6 @@ export const showEnvironment = ({ dispatch, commit }, environmentName) => {
|
|||
dispatch('fetchLogs', tracking.ENVIRONMENT_SELECTED);
|
||||
};
|
||||
|
||||
export const showManagedApp = ({ dispatch, commit }, managedApp) => {
|
||||
commit(types.SET_MANAGED_APP, managedApp);
|
||||
dispatch('fetchLogs', tracking.MANAGED_APP_SELECTED);
|
||||
};
|
||||
|
||||
export const refreshPodLogs = ({ dispatch, commit }) => {
|
||||
commit(types.REFRESH_POD_LOGS);
|
||||
dispatch('fetchLogs', tracking.REFRESH_POD_LOGS);
|
||||
|
@ -135,23 +124,6 @@ export const fetchEnvironments = ({ commit, dispatch }, environmentsPath) => {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch managed apps data
|
||||
* @param {Object} store
|
||||
* @param {String} clustersPath
|
||||
*/
|
||||
|
||||
export const fetchManagedApps = ({ commit }, clustersPath) => {
|
||||
return axios
|
||||
.get(clustersPath)
|
||||
.then(({ data }) => {
|
||||
commit(types.RECEIVE_MANAGED_APPS_DATA_SUCCESS, data.clusters);
|
||||
})
|
||||
.catch(() => {
|
||||
commit(types.RECEIVE_MANAGED_APPS_DATA_ERROR);
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchLogs = ({ commit, state }, trackingLabel) => {
|
||||
commit(types.REQUEST_LOGS_DATA);
|
||||
|
||||
|
|
|
@ -6,16 +6,9 @@ const mapTrace = ({ timestamp = null, pod = '', message = '' }) =>
|
|||
export const trace = (state) => state.logs.lines.map(mapTrace).join('\n');
|
||||
|
||||
export const showAdvancedFilters = (state) => {
|
||||
if (state.environments.current) {
|
||||
const environment = state.environments.options.find(
|
||||
({ name }) => name === state.environments.current,
|
||||
);
|
||||
|
||||
return Boolean(environment?.enable_advanced_logs_querying);
|
||||
}
|
||||
const managedApp = state.managedApps.options.find(
|
||||
({ name }) => name === state.managedApps.current,
|
||||
const environment = state.environments.options.find(
|
||||
({ name }) => name === state.environments.current,
|
||||
);
|
||||
|
||||
return Boolean(managedApp?.enable_advanced_logs_querying);
|
||||
return Boolean(environment?.enable_advanced_logs_querying);
|
||||
};
|
||||
|
|
|
@ -13,9 +13,6 @@ export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCC
|
|||
export const RECEIVE_ENVIRONMENTS_DATA_ERROR = 'RECEIVE_ENVIRONMENTS_DATA_ERROR';
|
||||
export const HIDE_REQUEST_ENVIRONMENTS_ERROR = 'HIDE_REQUEST_ENVIRONMENTS_ERROR';
|
||||
|
||||
export const RECEIVE_MANAGED_APPS_DATA_SUCCESS = 'RECEIVE_MANAGED_APPS_DATA_SUCCESS';
|
||||
export const RECEIVE_MANAGED_APPS_DATA_ERROR = 'RECEIVE_MANAGED_APPS_DATA_ERROR';
|
||||
|
||||
export const REQUEST_LOGS_DATA = 'REQUEST_LOGS_DATA';
|
||||
export const RECEIVE_LOGS_DATA_SUCCESS = 'RECEIVE_LOGS_DATA_SUCCESS';
|
||||
export const RECEIVE_LOGS_DATA_ERROR = 'RECEIVE_LOGS_DATA_ERROR';
|
||||
|
|
|
@ -32,9 +32,6 @@ export default {
|
|||
// Clear current pod options
|
||||
state.pods.current = null;
|
||||
state.pods.options = [];
|
||||
|
||||
// Clear current managedApps options
|
||||
state.managedApps.current = null;
|
||||
},
|
||||
[types.REQUEST_ENVIRONMENTS_DATA](state) {
|
||||
state.environments.options = [];
|
||||
|
@ -110,26 +107,4 @@ export default {
|
|||
[types.RECEIVE_PODS_DATA_ERROR](state) {
|
||||
state.pods.options = [];
|
||||
},
|
||||
// Managed apps data
|
||||
[types.RECEIVE_MANAGED_APPS_DATA_SUCCESS](state, apps) {
|
||||
state.managedApps.options = apps.filter(
|
||||
({ gitlab_managed_apps_logs_path }) => gitlab_managed_apps_logs_path, // eslint-disable-line babel/camelcase
|
||||
);
|
||||
state.managedApps.isLoading = false;
|
||||
},
|
||||
[types.RECEIVE_MANAGED_APPS_DATA_ERROR](state) {
|
||||
state.managedApps.options = [];
|
||||
state.managedApps.isLoading = false;
|
||||
state.managedApps.fetchError = true;
|
||||
},
|
||||
[types.SET_MANAGED_APP](state, managedApp) {
|
||||
state.managedApps.current = managedApp;
|
||||
|
||||
// Clear current pod options
|
||||
state.pods.current = null;
|
||||
state.pods.options = [];
|
||||
|
||||
// Clear current environment options
|
||||
state.environments.current = null;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -30,16 +30,6 @@ export default () => ({
|
|||
fetchError: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Managed apps list information
|
||||
*/
|
||||
managedApps: {
|
||||
options: [],
|
||||
isLoading: false,
|
||||
current: null,
|
||||
fetchError: false,
|
||||
},
|
||||
|
||||
/**
|
||||
* Logs including trace
|
||||
*/
|
||||
|
|
|
@ -1,30 +1,101 @@
|
|||
<script>
|
||||
import { FREQUENT_ITEMS_PROJECTS, FREQUENT_ITEMS_GROUPS } from '~/frequent_items/constants';
|
||||
import { BV_DROPDOWN_SHOW, BV_DROPDOWN_HIDE } from '~/lib/utils/constants';
|
||||
import KeepAliveSlots from '~/vue_shared/components/keep_alive_slots.vue';
|
||||
import eventHub, { EVENT_RESPONSIVE_TOGGLE } from '../event_hub';
|
||||
|
||||
const TEMPORARY_PLACEHOLDER = 'Placeholder for responsive top nav';
|
||||
import { resetMenuItemsActive } from '../utils/reset_menu_items_active';
|
||||
import ResponsiveHeader from './responsive_header.vue';
|
||||
import ResponsiveHome from './responsive_home.vue';
|
||||
import TopNavContainerView from './top_nav_container_view.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
KeepAliveSlots,
|
||||
ResponsiveHeader,
|
||||
ResponsiveHome,
|
||||
TopNavContainerView,
|
||||
},
|
||||
props: {
|
||||
navData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeView: 'home',
|
||||
hasMobileOverlay: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
nav() {
|
||||
return resetMenuItemsActive(this.navData);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
eventHub.$on(EVENT_RESPONSIVE_TOGGLE, this.onToggle);
|
||||
this.$root.$on(BV_DROPDOWN_SHOW, this.showMobileOverlay);
|
||||
this.$root.$on(BV_DROPDOWN_HIDE, this.hideMobileOverlay);
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off(EVENT_RESPONSIVE_TOGGLE, this.onToggle);
|
||||
this.$root.$off(BV_DROPDOWN_SHOW, this.showMobileOverlay);
|
||||
this.$root.$off(BV_DROPDOWN_HIDE, this.hideMobileOverlay);
|
||||
},
|
||||
methods: {
|
||||
onToggle() {
|
||||
document.body.classList.toggle('top-nav-responsive-open');
|
||||
},
|
||||
onMenuItemClick({ view }) {
|
||||
if (view) {
|
||||
this.activeView = view;
|
||||
}
|
||||
},
|
||||
showMobileOverlay() {
|
||||
this.hasMobileOverlay = true;
|
||||
},
|
||||
hideMobileOverlay() {
|
||||
this.hasMobileOverlay = false;
|
||||
},
|
||||
},
|
||||
TEMPORARY_PLACEHOLDER,
|
||||
FREQUENT_ITEMS_PROJECTS,
|
||||
FREQUENT_ITEMS_GROUPS,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p>{{ $options.TEMPORARY_PLACEHOLDER }}</p>
|
||||
<div>
|
||||
<div
|
||||
class="mobile-overlay"
|
||||
:class="{ 'mobile-nav-open': hasMobileOverlay }"
|
||||
data-testid="mobile-overlay"
|
||||
></div>
|
||||
<keep-alive-slots :slot-key="activeView">
|
||||
<template #home>
|
||||
<responsive-home :nav-data="nav" @menu-item-click="onMenuItemClick" />
|
||||
</template>
|
||||
<template #projects>
|
||||
<responsive-header @menu-item-click="onMenuItemClick">
|
||||
{{ __('Projects') }}
|
||||
</responsive-header>
|
||||
<top-nav-container-view
|
||||
:frequent-items-dropdown-type="$options.FREQUENT_ITEMS_PROJECTS.namespace"
|
||||
:frequent-items-vuex-module="$options.FREQUENT_ITEMS_PROJECTS.vuexModule"
|
||||
container-class="gl-px-3"
|
||||
v-bind="nav.views.projects"
|
||||
/>
|
||||
</template>
|
||||
<template #groups>
|
||||
<responsive-header @menu-item-click="onMenuItemClick">
|
||||
{{ __('Groups') }}
|
||||
</responsive-header>
|
||||
<top-nav-container-view
|
||||
:frequent-items-dropdown-type="$options.FREQUENT_ITEMS_GROUPS.namespace"
|
||||
:frequent-items-vuex-module="$options.FREQUENT_ITEMS_GROUPS.vuexModule"
|
||||
container-class="gl-px-3"
|
||||
v-bind="nav.views.groups"
|
||||
/>
|
||||
</template>
|
||||
</keep-alive-slots>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<script>
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import TopNavMenuItem from './top_nav_menu_item.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TopNavMenuItem,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
computed: {
|
||||
menuItem() {
|
||||
return {
|
||||
id: 'home',
|
||||
view: 'home',
|
||||
icon: 'angle-left',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="gl-py-4 gl-display-flex gl-align-items-center">
|
||||
<top-nav-menu-item
|
||||
v-gl-tooltip="{ title: s__('TopNav|Go back') }"
|
||||
class="gl-p-3!"
|
||||
:menu-item="menuItem"
|
||||
icon-only
|
||||
@click="$emit('menu-item-click', menuItem)"
|
||||
/>
|
||||
<span class="gl-font-size-h2 gl-font-weight-bold gl-ml-2">
|
||||
<slot></slot>
|
||||
</span>
|
||||
</header>
|
||||
</template>
|
|
@ -0,0 +1,62 @@
|
|||
<script>
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import TopNavMenuItem from './top_nav_menu_item.vue';
|
||||
import TopNavMenuSections from './top_nav_menu_sections.vue';
|
||||
import TopNavNewDropdown from './top_nav_new_dropdown.vue';
|
||||
|
||||
const NEW_VIEW = 'new';
|
||||
const SEARCH_VIEW = 'search';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TopNavMenuItem,
|
||||
TopNavMenuSections,
|
||||
TopNavNewDropdown,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
navData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
menuSections() {
|
||||
return [
|
||||
{ id: 'primary', menuItems: this.navData.primary },
|
||||
{ id: 'secondary', menuItems: this.navData.secondary },
|
||||
].filter((x) => x.menuItems?.length);
|
||||
},
|
||||
newDropdownViewModel() {
|
||||
return this.navData.views[NEW_VIEW];
|
||||
},
|
||||
searchMenuItem() {
|
||||
return this.navData.views[SEARCH_VIEW];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<header class="gl-display-flex gl-align-items-center gl-py-4 gl-pl-4">
|
||||
<h1 class="gl-m-0 gl-font-size-h2 gl-reset-color gl-mr-auto">{{ __('Menu') }}</h1>
|
||||
<top-nav-menu-item
|
||||
v-if="searchMenuItem"
|
||||
v-gl-tooltip="{ title: searchMenuItem.title }"
|
||||
class="gl-ml-3"
|
||||
:menu-item="searchMenuItem"
|
||||
icon-only
|
||||
/>
|
||||
<top-nav-new-dropdown
|
||||
v-if="newDropdownViewModel"
|
||||
v-gl-tooltip="{ title: newDropdownViewModel.title }"
|
||||
:view-model="newDropdownViewModel"
|
||||
class="gl-ml-3"
|
||||
/>
|
||||
</header>
|
||||
<top-nav-menu-sections class="gl-h-full" :sections="menuSections" v-on="$listeners" />
|
||||
</div>
|
||||
</template>
|
|
@ -20,6 +20,11 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
containerClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
linksPrimary: {
|
||||
type: Array,
|
||||
required: false,
|
||||
|
@ -50,7 +55,11 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="top-nav-container-view gl-display-flex gl-flex-direction-column">
|
||||
<div class="frequent-items-dropdown-container gl-w-auto">
|
||||
<div
|
||||
class="frequent-items-dropdown-container gl-w-auto"
|
||||
:class="containerClass"
|
||||
data-testid="frequent-items-container"
|
||||
>
|
||||
<div class="frequent-items-dropdown-content gl-w-full! gl-pt-0!">
|
||||
<vuex-module-provider :vuex-module="frequentItemsVuexModule">
|
||||
<frequent-items-app v-bind="$attrs" />
|
||||
|
|
|
@ -16,6 +16,11 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
iconOnly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
dataAttrs() {
|
||||
|
@ -32,13 +37,16 @@ export default {
|
|||
:href="menuItem.href"
|
||||
class="top-nav-menu-item gl-display-block"
|
||||
:class="[menuItem.css_class, { [$options.ACTIVE_CLASS]: menuItem.active }]"
|
||||
:aria-label="menuItem.title"
|
||||
v-bind="dataAttrs"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<span class="gl-display-flex">
|
||||
<gl-icon v-if="menuItem.icon" :name="menuItem.icon" class="gl-mr-2!" />
|
||||
{{ menuItem.title }}
|
||||
<gl-icon v-if="menuItem.view" name="chevron-right" class="gl-ml-auto" />
|
||||
<gl-icon v-if="menuItem.icon" :name="menuItem.icon" :class="{ 'gl-mr-2!': !iconOnly }" />
|
||||
<template v-if="!iconOnly">
|
||||
{{ menuItem.title }}
|
||||
<gl-icon v-if="menuItem.view" name="chevron-right" class="gl-ml-auto" />
|
||||
</template>
|
||||
</span>
|
||||
</gl-button>
|
||||
</template>
|
||||
|
|
|
@ -54,6 +54,7 @@ export default {
|
|||
:key="menuItem.id"
|
||||
:menu-item="menuItem"
|
||||
data-testid="menu-item"
|
||||
class="gl-w-full"
|
||||
:class="{ 'gl-mt-1': menuItemIndex > 0 }"
|
||||
@click="onClick(menuItem)"
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownDivider, GlDropdownItem, GlDropdownSectionHeader } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownDivider,
|
||||
GlDropdownItem,
|
||||
GlDropdownSectionHeader,
|
||||
},
|
||||
props: {
|
||||
viewModel: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
sections() {
|
||||
return this.viewModel.menu_sections || [];
|
||||
},
|
||||
showHeaders() {
|
||||
return this.sections.length > 1;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-dropdown
|
||||
toggle-class="top-nav-menu-item"
|
||||
icon="plus"
|
||||
:text="viewModel.title"
|
||||
category="tertiary"
|
||||
text-sr-only
|
||||
no-caret
|
||||
right
|
||||
>
|
||||
<template v-for="({ title, menu_items }, index) in sections">
|
||||
<gl-dropdown-divider v-if="index > 0" :key="`${index}_divider`" data-testid="divider" />
|
||||
<gl-dropdown-section-header v-if="showHeaders" :key="`${index}_header`" data-testid="header">
|
||||
{{ title }}
|
||||
</gl-dropdown-section-header>
|
||||
<template v-for="menuItem in menu_items">
|
||||
<gl-dropdown-item
|
||||
:key="`${index}_item_${menuItem.id}`"
|
||||
link-class="top-nav-menu-item"
|
||||
:href="menuItem.href"
|
||||
data-testid="item"
|
||||
>
|
||||
{{ menuItem.title }}
|
||||
</gl-dropdown-item>
|
||||
</template>
|
||||
</template>
|
||||
</gl-dropdown>
|
||||
</template>
|
|
@ -0,0 +1,14 @@
|
|||
const resetActiveInArray = (arr) => arr?.map((menuItem) => ({ ...menuItem, active: false }));
|
||||
|
||||
/**
|
||||
* This method sets `active: false` for the menu items within the given nav data.
|
||||
*
|
||||
* @returns navData with the menu items updated with `active: false`
|
||||
*/
|
||||
export const resetMenuItemsActive = ({ primary, secondary, ...navData }) => {
|
||||
return {
|
||||
...navData,
|
||||
primary: resetActiveInArray(primary),
|
||||
secondary: resetActiveInArray(secondary),
|
||||
};
|
||||
};
|
|
@ -1,15 +1,14 @@
|
|||
<script>
|
||||
import { GlIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { __ } from '../../locale';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
removesBranchText: __('%{strongStart}Deletes%{strongEnd} source branch'),
|
||||
removesBranchText: __('The source branch will be deleted'),
|
||||
tooltipTitle: __('A user with write access to the source branch selected this option'),
|
||||
},
|
||||
components: {
|
||||
GlIcon,
|
||||
GlSprintf,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
@ -20,11 +19,7 @@ export default {
|
|||
<template>
|
||||
<p v-once class="mr-info-list gl-ml-7 gl-pb-5 gl-mb-0">
|
||||
<span class="status-text">
|
||||
<gl-sprintf :message="$options.i18n.removesBranchText">
|
||||
<template #strong="{ content }">
|
||||
<strong>{{ content }}</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
{{ $options.i18n.removesBranchText }}
|
||||
</span>
|
||||
<gl-icon
|
||||
v-gl-tooltip.hover
|
||||
|
|
|
@ -497,6 +497,9 @@ body {
|
|||
color: #dbdbdb;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
.gl-font-sm {
|
||||
font-size: 12px;
|
||||
}
|
||||
.dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -2179,6 +2182,12 @@ body.gl-dark {
|
|||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
.gl-font-sm {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.gl-font-weight-bold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@import "startup/cloaking";
|
||||
@include cloak-startup-scss(none);
|
||||
|
|
|
@ -482,6 +482,9 @@ body {
|
|||
color: #525252;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
.gl-font-sm {
|
||||
font-size: 12px;
|
||||
}
|
||||
.dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -1962,6 +1965,12 @@ body.sidebar-refactoring
|
|||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
.gl-font-sm {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.gl-font-weight-bold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@import "startup/cloaking";
|
||||
@include cloak-startup-scss(none);
|
||||
|
|
|
@ -48,7 +48,7 @@ module Ci
|
|||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def pipelines_using_cte
|
||||
sha_relation = merge_request.all_commits.select(:sha)
|
||||
sha_relation = sha_relation.distinct if Feature.enabled?(:use_distinct_in_shas_cte)
|
||||
sha_relation = sha_relation.distinct if Feature.enabled?(:use_distinct_in_shas_cte, default_enabled: :yaml)
|
||||
|
||||
cte = Gitlab::SQL::CTE.new(:shas, sha_relation)
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'Filter by tags associated with the runner (comma-separated or array).'
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
required: false,
|
||||
description: 'Filter by full token or partial text in description field.'
|
||||
|
||||
argument :sort, ::Types::Ci::RunnerSortEnum,
|
||||
required: false,
|
||||
description: 'Sort order of results.'
|
||||
|
|
|
@ -4,21 +4,58 @@ module Nav
|
|||
module TopNavHelper
|
||||
PROJECTS_VIEW = :projects
|
||||
GROUPS_VIEW = :groups
|
||||
NEW_VIEW = :new
|
||||
SEARCH_VIEW = :search
|
||||
|
||||
def top_nav_view_model(project:, group:)
|
||||
builder = ::Gitlab::Nav::TopNavViewModelBuilder.new
|
||||
|
||||
if current_user
|
||||
build_view_model(builder: builder, project: project, group: group)
|
||||
else
|
||||
build_anonymous_view_model(builder: builder)
|
||||
build_base_view_model(builder: builder, project: project, group: group)
|
||||
|
||||
builder.build
|
||||
end
|
||||
|
||||
def top_nav_responsive_view_model(project:, group:)
|
||||
builder = ::Gitlab::Nav::TopNavViewModelBuilder.new
|
||||
|
||||
build_base_view_model(builder: builder, project: project, group: group)
|
||||
|
||||
new_view_model = new_dropdown_view_model(project: project, group: group)
|
||||
|
||||
if new_view_model
|
||||
builder.add_view(NEW_VIEW, new_view_model)
|
||||
end
|
||||
|
||||
if top_nav_show_search
|
||||
builder.add_view(SEARCH_VIEW, ::Gitlab::Nav::TopNavMenuItem.build(**top_nav_search_menu_item_attrs))
|
||||
end
|
||||
|
||||
builder.build
|
||||
end
|
||||
|
||||
def top_nav_show_search
|
||||
header_link?(:search)
|
||||
end
|
||||
|
||||
def top_nav_search_menu_item_attrs
|
||||
{
|
||||
id: 'search',
|
||||
title: _('Search'),
|
||||
icon: 'search',
|
||||
href: search_context.search_url
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_base_view_model(builder:, project:, group:)
|
||||
if current_user
|
||||
build_view_model(builder: builder, project: project, group: group)
|
||||
else
|
||||
build_anonymous_view_model(builder: builder)
|
||||
end
|
||||
end
|
||||
|
||||
def build_anonymous_view_model(builder:)
|
||||
# These come from `app/views/layouts/nav/_explore.html.ham`
|
||||
if explore_nav_link?(:projects)
|
||||
|
|
|
@ -168,18 +168,13 @@ module Ci
|
|||
|
||||
# Searches for runners matching the given query.
|
||||
#
|
||||
# This method uses ILIKE on PostgreSQL.
|
||||
#
|
||||
# This method performs a *partial* match on tokens, thus a query for "a"
|
||||
# will match any runner where the token contains the letter "a". As a result
|
||||
# you should *not* use this method for non-admin purposes as otherwise users
|
||||
# might be able to query a list of all runners.
|
||||
# This method uses ILIKE on PostgreSQL for the description field and performs a full match on tokens.
|
||||
#
|
||||
# query - The search query as a String.
|
||||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def self.search(query)
|
||||
fuzzy_search(query, [:token, :description])
|
||||
where(token: query).or(fuzzy_search(query, [:description]))
|
||||
end
|
||||
|
||||
def self.online_contact_time_deadline
|
||||
|
|
|
@ -147,23 +147,15 @@ class Packages::Package < ApplicationRecord
|
|||
scope :order_by_package_file, -> { joins(:package_files).order('packages_package_files.created_at ASC') }
|
||||
|
||||
scope :order_project_path, -> do
|
||||
if Feature.enabled?(:arel_package_scopes)
|
||||
keyset_order = keyset_pagination_order(join_class: Project, column_name: :path, direction: :asc)
|
||||
keyset_order = keyset_pagination_order(join_class: Project, column_name: :path, direction: :asc)
|
||||
|
||||
joins(:project).reorder(keyset_order)
|
||||
else
|
||||
joins(:project).reorder('projects.path ASC, id ASC')
|
||||
end
|
||||
joins(:project).reorder(keyset_order)
|
||||
end
|
||||
|
||||
scope :order_project_path_desc, -> do
|
||||
if Feature.enabled?(:arel_package_scopes)
|
||||
keyset_order = keyset_pagination_order(join_class: Project, column_name: :path, direction: :desc)
|
||||
keyset_order = keyset_pagination_order(join_class: Project, column_name: :path, direction: :desc)
|
||||
|
||||
joins(:project).reorder(keyset_order)
|
||||
else
|
||||
joins(:project).reorder('projects.path DESC, id DESC')
|
||||
end
|
||||
joins(:project).reorder(keyset_order)
|
||||
end
|
||||
|
||||
after_commit :update_composer_cache, on: :destroy, if: -> { composer? }
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
%a.gl-sr-only.gl-accessibility{ href: "#content-body" } Skip to content
|
||||
.container-fluid
|
||||
.header-content
|
||||
.title-container{ class: ('hide-when-menu-expanded' if !use_top_nav_redesign) }
|
||||
.title-container.hide-when-menu-expanded
|
||||
%h1.title
|
||||
%span.gl-sr-only GitLab
|
||||
= link_to root_path, title: _('Dashboard'), id: 'logo', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation') do
|
||||
|
@ -33,12 +33,13 @@
|
|||
%ul.nav.navbar-nav
|
||||
- if current_user
|
||||
= render 'layouts/header/new_dropdown', class: ('gl-display-none gl-sm-display-block' if use_top_nav_redesign)
|
||||
- if header_link?(:search)
|
||||
- if top_nav_show_search
|
||||
- search_menu_item = top_nav_search_menu_item_attrs
|
||||
%li.nav-item.d-none.d-lg-block.m-auto
|
||||
= render 'layouts/search' unless current_controller?(:search)
|
||||
%li.nav-item{ class: use_top_nav_redesign ? "gl-display-none gl-sm-display-inline-block gl-lg-display-none" : "gl-display-inline-block gl-lg-display-none" }
|
||||
= link_to search_context.search_url, title: _('Search'), aria: { label: _('Search') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||
= sprite_icon('search')
|
||||
= link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||
= sprite_icon(search_menu_item.fetch(:icon))
|
||||
- if header_link?(:issues)
|
||||
= nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do
|
||||
= link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues', aria: { label: _('Issues') },
|
||||
|
@ -120,9 +121,9 @@
|
|||
%button.navbar-toggler.d-block.d-sm-none{ type: 'button', class: ('gl-border-none!' if use_top_nav_redesign) }
|
||||
%span.sr-only= _('Toggle navigation')
|
||||
- if use_top_nav_redesign
|
||||
%span.more-icon.gl-px-3
|
||||
%span.more-icon.gl-px-3.gl-font-sm.gl-font-weight-bold
|
||||
%span.gl-pr-2= _('Menu')
|
||||
= sprite_icon('dot-grid', size: 16)
|
||||
= sprite_icon('hamburger', size: 16)
|
||||
- else
|
||||
= sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon')
|
||||
= sprite_icon('close', size: 12, css_class: 'close-icon')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- return unless Feature.enabled?(:combined_menu, current_user, default_enabled: :yaml)
|
||||
|
||||
- top_class = local_assigns.fetch(:class, nil)
|
||||
- view_model = top_nav_view_model(project: @project, group: @group)
|
||||
- view_model = top_nav_responsive_view_model(project: @project, group: @group)
|
||||
|
||||
.top-nav-responsive{ class: top_class }
|
||||
#js-top-nav-responsive{ data: { view_model: view_model.to_json } }
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
had
|
||||
= failed.size
|
||||
failed
|
||||
#{'build'.pluralize(failed.size)}.
|
||||
#{'job'.pluralize(failed.size)}.
|
||||
%tr.table-warning
|
||||
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; border: 1px solid #ededed; border-bottom: 0; border-radius: 4px 4px 0 0; overflow: hidden; background-color: #fdf4f6; color: #d22852; font-size: 14px; line-height: 1.4; text-align: center; padding: 8px 16px;" }
|
||||
Failed builds
|
||||
Failed jobs
|
||||
%tr.section
|
||||
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 0 16px; border: 1px solid #ededed; border-radius: 4px; overflow: hidden; border-top: 0; border-radius: 0 0 4px 4px;" }
|
||||
%table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width: 100%; border-collapse: collapse;" }
|
||||
|
|
|
@ -28,7 +28,7 @@ Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by <%
|
|||
Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) triggered by API
|
||||
<% end -%>
|
||||
<% failed = @pipeline.latest_statuses.failed -%>
|
||||
had <%= failed.size %> failed <%= 'build'.pluralize(failed.size) %>.
|
||||
had <%= failed.size %> failed <%= 'job'.pluralize(failed.size) %>.
|
||||
|
||||
<% failed.each do |build| -%>
|
||||
<%= render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build %>
|
||||
|
|
|
@ -312,11 +312,33 @@ module Gitlab
|
|||
end
|
||||
|
||||
# Cross-origin requests must be enabled for the Authorization code with PKCE OAuth flow when used from a browser.
|
||||
%w(/oauth/token /oauth/revoke).each do |oauth_path|
|
||||
allow do
|
||||
origins '*'
|
||||
resource oauth_path,
|
||||
headers: %w(Authorization),
|
||||
credentials: false,
|
||||
methods: %i(post)
|
||||
end
|
||||
end
|
||||
|
||||
# These are routes from doorkeeper-openid_connect:
|
||||
# https://github.com/doorkeeper-gem/doorkeeper-openid_connect#routes
|
||||
allow do
|
||||
origins '*'
|
||||
resource '/oauth/token',
|
||||
resource '/oauth/userinfo',
|
||||
headers: %w(Authorization),
|
||||
credentials: false,
|
||||
methods: [:post]
|
||||
methods: %i(get head post)
|
||||
end
|
||||
|
||||
%w(/oauth/discovery/keys /.well-known/openid-configuration /.well-known/webfinger).each do |openid_path|
|
||||
allow do
|
||||
origins '*'
|
||||
resource openid_path,
|
||||
credentials: false,
|
||||
methods: %i(get head)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: arel_package_scopes
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62042
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331306
|
||||
milestone: '13.12'
|
||||
type: development
|
||||
group: group::package
|
||||
default_enabled: false
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330586
|
|||
milestone: '13.12'
|
||||
type: development
|
||||
group: group::optimize
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ecosystem.i_ecosystem_jira_service_close_issue_monthly
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Number of users closing Jira issues by month
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ecosystem.i_ecosystem_jira_service_cross_reference_monthly
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Number of users that cross-referenced Jira issues by month
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ecosystem.i_ecosystem_jira_service_list_issues_monthly
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
|
@ -1,16 +0,0 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ecosystem.i_ecosystem_jira_service_create_issue_monthly
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ecosystem.ecosystem_total_unique_counts_monthly
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Number of users performing actions on Jira issues by month
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ecosystem.i_ecosystem_jira_service_close_issue_weekly
|
||||
description: Number of users closing Jira issues by week
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ecosystem.i_ecosystem_jira_service_cross_reference_weekly
|
||||
description: Number of users that cross-referenced Jira issues by week
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ecosystem.ecosystem_total_unique_counts_weekly
|
||||
description: Number of users performing actions on Jira issues by week
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.projects_datadog_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: 'Count of projects with active integrations for Datadog'
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.groups_datadog_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of groups with active integrations for Datadog
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.templates_datadog_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active service templates for Datadog
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.instances_datadog_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active instance-level integrations for Datadog
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.projects_inheriting_datadog_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active projects inheriting integrations for Datadog
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.groups_inheriting_datadog_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active groups inheriting integrations for Datadog
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.projects_ewm_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: 'Count of projects with active integrations for EWM'
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.groups_ewm_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of groups with active integrations for EWM
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.templates_ewm_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active service templates for EWM
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.instances_ewm_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active instance-level integrations for EWM
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.projects_inheriting_ewm_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active projects inheriting integrations for EWM
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
---
|
||||
key_path: counts.groups_inheriting_ewm_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active groups inheriting integrations for EWM
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.projects_mock_ci_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of projects with active integrations for Mock CI
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.groups_mock_ci_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of groups with active integrations for Mock CI
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.templates_mock_ci_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active service templates for Mock CI
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.instances_mock_ci_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active instance-level integrations for Mock CI
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.projects_inheriting_mock_ci_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active projects inheriting integrations for Mock CI
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.groups_inheriting_mock_ci_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active groups inheriting integrations for Mock CI
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.projects_mock_monitoring_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of projects with active integrations for Mock Monitoring
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.groups_mock_monitoring_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of groups with active integrations for Mock Monitoring
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.templates_mock_monitoring_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active service templates for Mock Monitoring
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.instances_mock_monitoring_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active instance-level integrations for Mock Monitoring
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.projects_inheriting_mock_monitoring_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active projects inheriting integrations for Mock Monitoring
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
---
|
||||
key_path: counts.groups_inheriting_mock_monitoring_active
|
||||
description: ''
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
product_category: ''
|
||||
description: Count of active groups inheriting integrations for Mock Monitoring
|
||||
product_section: dev
|
||||
product_stage: create
|
||||
product_group: group::ecosystem
|
||||
product_category: integrations
|
||||
value_type: number
|
||||
status: data_available
|
||||
status: removed
|
||||
milestone_removed: '13.12'
|
||||
time_frame: all
|
||||
data_source: database
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
skip_validation: true
|
||||
- premium
|
||||
- ultimate
|
||||
|
|
|
@ -44,6 +44,14 @@ Rails.application.routes.draw do
|
|||
draw :oauth
|
||||
|
||||
use_doorkeeper_openid_connect
|
||||
# Add OPTIONS method for CORS preflight requests
|
||||
match '/oauth/userinfo' => 'doorkeeper/openid_connect/userinfo#show', via: :options
|
||||
match '/oauth/discovery/keys' => 'doorkeeper/openid_connect/discovery#keys', via: :options
|
||||
match '/.well-known/openid-configuration' => 'doorkeeper/openid_connect/discovery#provider', via: :options
|
||||
match '/.well-known/webfinger' => 'doorkeeper/openid_connect/discovery#webfinger', via: :options
|
||||
|
||||
match '/oauth/token' => 'oauth/tokens#create', via: :options
|
||||
match '/oauth/revoke' => 'oauth/tokens#revoke', via: :options
|
||||
|
||||
# Sign up
|
||||
scope path: '/users/sign_up', module: :registrations, as: :users_sign_up do
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddRunnersDescriptionIndex < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
INDEX_NAME = 'index_ci_runners_on_description_trigram'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :ci_runners, :description, name: INDEX_NAME, using: :gin, opclass: { description: :gin_trgm_ops }
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :ci_runners, INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
96c70de2567fc3e816c720ed6e4cef2446c0f0ee288d0959cd1298523913077f
|
|
@ -22921,6 +22921,8 @@ CREATE INDEX index_ci_runner_projects_on_runner_id ON ci_runner_projects USING b
|
|||
|
||||
CREATE INDEX index_ci_runners_on_contacted_at ON ci_runners USING btree (contacted_at);
|
||||
|
||||
CREATE INDEX index_ci_runners_on_description_trigram ON ci_runners USING gin (description gin_trgm_ops);
|
||||
|
||||
CREATE INDEX index_ci_runners_on_locked ON ci_runners USING btree (locked);
|
||||
|
||||
CREATE INDEX index_ci_runners_on_runner_type ON ci_runners USING btree (runner_type);
|
||||
|
|
|
@ -7,17 +7,14 @@ type: howto
|
|||
|
||||
# Disaster Recovery (Geo) **(PREMIUM SELF)**
|
||||
|
||||
Geo replicates your database, your Git repositories, and few other assets.
|
||||
We will support and replicate more data in the future, that will enable you to
|
||||
failover with minimal effort, in a disaster situation.
|
||||
|
||||
See [Geo limitations](../index.md#limitations) for more information.
|
||||
Geo replicates your database, your Git repositories, and few other assets,
|
||||
but there are some [limitations](../index.md#limitations).
|
||||
|
||||
WARNING:
|
||||
Disaster recovery for multi-secondary configurations is in **Alpha**.
|
||||
For the latest updates, check the [Disaster Recovery epic for complete maturity](https://gitlab.com/groups/gitlab-org/-/epics/3574).
|
||||
Multi-secondary configurations require the complete re-synchronization and re-configuration of all non-promoted secondaries and
|
||||
will cause downtime.
|
||||
causes downtime.
|
||||
|
||||
## Promoting a **secondary** Geo node in single-secondary configurations
|
||||
|
||||
|
@ -91,7 +88,7 @@ Note the following when promoting a secondary:
|
|||
before proceeding. If the secondary node
|
||||
[has been paused](../../geo/index.md#pausing-and-resuming-replication), the promotion
|
||||
performs a point-in-time recovery to the last known state.
|
||||
Data that was created on the primary while the secondary was paused will be lost.
|
||||
Data that was created on the primary while the secondary was paused is lost.
|
||||
- A new **secondary** should not be added at this time. If you want to add a new
|
||||
**secondary**, do this after you have completed the entire process of promoting
|
||||
the **secondary** to the **primary**.
|
||||
|
@ -497,7 +494,7 @@ must disable the **primary** site:
|
|||
WARNING:
|
||||
If the secondary site [has been paused](../../geo/index.md#pausing-and-resuming-replication), this performs
|
||||
a point-in-time recovery to the last known state.
|
||||
Data that was created on the primary while the secondary was paused will be lost.
|
||||
Data that was created on the primary while the secondary was paused is lost.
|
||||
|
||||
1. SSH in to the database node in the **secondary** and trigger PostgreSQL to
|
||||
promote to read-write:
|
||||
|
@ -513,7 +510,7 @@ Data that was created on the primary while the secondary was paused will be lost
|
|||
`geo_secondary_role`:
|
||||
|
||||
NOTE:
|
||||
Depending on your architecture these steps will need to be run on any GitLab node that is external to the **secondary** Kubernetes cluster.
|
||||
Depending on your architecture, these steps need to run on any GitLab node that is external to the **secondary** Kubernetes cluster.
|
||||
|
||||
```ruby
|
||||
## In pre-11.5 documentation, the role was enabled as follows. Remove this line.
|
||||
|
@ -547,7 +544,7 @@ Data that was created on the primary while the secondary was paused will be lost
|
|||
helm --namespace gitlab get values gitlab-geo > gitlab.yaml
|
||||
```
|
||||
|
||||
The existing configuration will contain a section for Geo that should resemble:
|
||||
The existing configuration contains a section for Geo that should resemble:
|
||||
|
||||
```yaml
|
||||
geo:
|
||||
|
@ -564,7 +561,7 @@ Data that was created on the primary while the secondary was paused will be lost
|
|||
|
||||
To promote the **secondary** cluster to a **primary** cluster, update `role: secondary` to `role: primary`.
|
||||
|
||||
You can remove the entire `psql` section if the cluster will remain as a primary site, this refers to the tracking database and will be ignored whilst the cluster is acting as a primary site.
|
||||
If the cluster remains as a primary site, you can remove the entire `psql` section; it refers to the tracking database and is ignored whilst the cluster is acting as a primary site.
|
||||
|
||||
Update the cluster with the new configuration:
|
||||
|
||||
|
|
|
@ -4,12 +4,13 @@ group: Ecosystem
|
|||
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
|
||||
---
|
||||
|
||||
# Getting started with GitLab GraphQL API
|
||||
# Get started with GitLab GraphQL API **(FREE)**
|
||||
|
||||
This guide demonstrates basic usage of the GitLab GraphQL API.
|
||||
|
||||
See the [GraphQL API style guide](../../development/api_graphql_styleguide.md) for implementation details
|
||||
aimed at developers who wish to work on developing the API itself.
|
||||
Read the [GraphQL API style guide](../../development/api_graphql_styleguide.md)
|
||||
for implementation details aimed at developers who wish to work on developing
|
||||
the API itself.
|
||||
|
||||
## Running examples
|
||||
|
||||
|
@ -20,10 +21,11 @@ The examples documented here can be run using:
|
|||
|
||||
### Command line
|
||||
|
||||
You can run GraphQL queries in a `curl` request on the command line on your local machine.
|
||||
A GraphQL request can be made as a `POST` request to `/api/graphql` with the query as the payload.
|
||||
You can authorize your request by generating a [personal access token](../../user/profile/personal_access_tokens.md)
|
||||
to use as a bearer token.
|
||||
You can run GraphQL queries in a `curl` request on the command line on your
|
||||
local computer. A GraphQL request can be made as a `POST` request to `/api/graphql`
|
||||
with the query as the payload. You can authorize your request by generating a
|
||||
[personal access token](../../user/profile/personal_access_tokens.md) to use as
|
||||
a bearer token.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -36,24 +38,26 @@ curl "https://gitlab.com/api/graphql" --header "Authorization: Bearer $GRAPHQL_T
|
|||
|
||||
### GraphiQL
|
||||
|
||||
GraphiQL (pronounced "graphical") allows you to run queries directly against the server endpoint
|
||||
with syntax highlighting and autocomplete. It also allows you to explore the schema and types.
|
||||
GraphiQL (pronounced "graphical") allows you to run queries directly against
|
||||
the server endpoint with syntax highlighting and autocomplete. It also allows
|
||||
you to explore the schema and types.
|
||||
|
||||
The examples below:
|
||||
|
||||
- Can be run directly against GitLab 11.0 or later, though some of the types and fields
|
||||
may not be supported in older versions.
|
||||
- Works against GitLab.com without any further setup. Make sure you are signed in and
|
||||
navigate to the [GraphiQL Explorer](https://gitlab.com/-/graphql-explorer).
|
||||
- Can be run directly against GitLab 11.0 or later, though some of the types
|
||||
and fields may not be supported in older versions.
|
||||
- Works against GitLab.com without any further setup. Make sure you are signed
|
||||
in and navigate to the [GraphiQL Explorer](https://gitlab.com/-/graphql-explorer).
|
||||
|
||||
If you want to run the queries locally, or on a self-managed instance,
|
||||
you must either:
|
||||
If you want to run the queries locally, or on a self-managed instance, you must
|
||||
either:
|
||||
|
||||
- Create the `gitlab-org` group with a project called `graphql-sandbox` under it. Create
|
||||
several issues within the project.
|
||||
- Edit the queries to replace `gitlab-org/graphql-sandbox` with your own group and project.
|
||||
- Create the `gitlab-org` group with a project called `graphql-sandbox` under
|
||||
it. Create several issues in the project.
|
||||
- Edit the queries to replace `gitlab-org/graphql-sandbox` with your own group
|
||||
and project.
|
||||
|
||||
Please refer to [running GraphiQL](index.md#graphiql) for more information.
|
||||
Refer to [running GraphiQL](index.md#graphiql) for more information.
|
||||
|
||||
NOTE:
|
||||
If you are running GitLab 11.0 to 12.0, enable the `graphql`
|
||||
|
@ -74,8 +78,8 @@ which is an object identifier in the format of `"gid://gitlab/Issue/123"`.
|
|||
[GitLab GraphQL Schema](reference/index.md) outlines which objects and fields are
|
||||
available for clients to query and their corresponding data types.
|
||||
|
||||
Example: Get only the names of all the projects the currently logged in user can access (up to a limit, more on that later)
|
||||
in the group `gitlab-org`.
|
||||
Example: Get only the names of all the projects the currently logged in user can
|
||||
access (up to a limit) in the group `gitlab-org`.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
|
@ -108,12 +112,12 @@ query {
|
|||
|
||||
When retrieving child nodes use:
|
||||
|
||||
- the `edges { node { } }` syntax.
|
||||
- the short form `nodes { }` syntax.
|
||||
- The `edges { node { } }` syntax.
|
||||
- The short form `nodes { }` syntax.
|
||||
|
||||
Underneath it all is a graph we are traversing, hence the name GraphQL.
|
||||
|
||||
Example: Get a project (only its name) and the titles of all its issues.
|
||||
Example: Get the name of a project, and the titles of all its issues.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
|
@ -130,23 +134,24 @@ query {
|
|||
```
|
||||
|
||||
More about queries:
|
||||
[GraphQL docs](https://graphql.org/learn/queries/)
|
||||
[GraphQL documentation](https://graphql.org/learn/queries/)
|
||||
|
||||
### Authorization
|
||||
|
||||
Authorization uses the same engine as the GitLab application (and GitLab.com). So if you've signed in to GitLab
|
||||
and use GraphiQL, all queries are performed as you, the signed in user. For more information, see the
|
||||
Authorization uses the same engine as the GitLab application (and GitLab.com).
|
||||
If you've signed in to GitLab and use GraphiQL, all queries are performed as
|
||||
you, the signed in user. For more information, read the
|
||||
[GitLab API documentation](../README.md#authentication).
|
||||
|
||||
### Mutations
|
||||
|
||||
Mutations make changes to data. We can update, delete, or create new records. Mutations
|
||||
generally use InputTypes and variables, neither of which appear here.
|
||||
Mutations make changes to data. We can update, delete, or create new records.
|
||||
Mutations generally use InputTypes and variables, neither of which appear here.
|
||||
|
||||
Mutations have:
|
||||
|
||||
- Inputs. For example, arguments, such as which emoji you'd like to award,
|
||||
and to which object.
|
||||
and to which object.
|
||||
- Return statements. That is, what you'd like to get back when it's successful.
|
||||
- Errors. Always ask for what went wrong, just in case.
|
||||
|
||||
|
@ -174,8 +179,9 @@ mutation {
|
|||
}
|
||||
```
|
||||
|
||||
Example: Add a comment to the issue (we're using the ID of the `GitLab.com` issue - but
|
||||
if you're using a local instance, you must get the ID of an issue you can write to).
|
||||
Example: Add a comment to the issue. In this example, we use the ID of the
|
||||
`GitLab.com` issue. If you're using a local instance, you must get the ID of an
|
||||
issue you can write to.
|
||||
|
||||
```graphql
|
||||
mutation {
|
||||
|
@ -196,7 +202,8 @@ mutation {
|
|||
|
||||
#### Update mutations
|
||||
|
||||
When you see the result `id` of the note you created - take a note of it. Now let's edit it to sip faster!
|
||||
When you see the result `id` of the note you created, take a note of it. Let's
|
||||
edit it to sip faster.
|
||||
|
||||
```graphql
|
||||
mutation {
|
||||
|
@ -214,7 +221,7 @@ mutation {
|
|||
|
||||
#### Deletion mutations
|
||||
|
||||
Let's delete the comment, since our tea is all gone.
|
||||
Let's delete the comment, because our tea is all gone.
|
||||
|
||||
```graphql
|
||||
mutation {
|
||||
|
@ -244,16 +251,18 @@ You should get something like the following output:
|
|||
We've asked for the note details, but it doesn't exist anymore, so we get `null`.
|
||||
|
||||
More about mutations:
|
||||
[GraphQL Docs](https://graphql.org/learn/queries/#mutations).
|
||||
[GraphQL Documentation](https://graphql.org/learn/queries/#mutations).
|
||||
|
||||
### Introspective queries
|
||||
|
||||
Clients can query the GraphQL endpoint for information about its own schema.
|
||||
by making an [introspective query](https://graphql.org/learn/introspection/).
|
||||
The [GraphiQL Query Explorer](https://gitlab.com/-/graphql-explorer) uses an
|
||||
introspection query to:
|
||||
|
||||
It is through an introspection query that the [GraphiQL Query Explorer](https://gitlab.com/-/graphql-explorer)
|
||||
gets all of its knowledge about our GraphQL schema to do autocompletion and provide
|
||||
its interactive `Docs` tab.
|
||||
- Gain knowledge about our GraphQL schema.
|
||||
- Do autocompletion.
|
||||
- Provide its interactive `Docs` tab.
|
||||
|
||||
Example: Get all the type names in the schema.
|
||||
|
||||
|
@ -267,8 +276,8 @@ Example: Get all the type names in the schema.
|
|||
}
|
||||
```
|
||||
|
||||
Example: Get all the fields associated with Issue.
|
||||
`kind` tells us the enum value for the type, like `OBJECT`, `SCALAR` or `INTERFACE`.
|
||||
Example: Get all the fields associated with Issue. `kind` tells us the enum
|
||||
value for the type, like `OBJECT`, `SCALAR` or `INTERFACE`.
|
||||
|
||||
```graphql
|
||||
query IssueTypes {
|
||||
|
@ -287,12 +296,12 @@ query IssueTypes {
|
|||
```
|
||||
|
||||
More about introspection:
|
||||
[GraphQL docs](https://graphql.org/learn/introspection/)
|
||||
[GraphQL documentation](https://graphql.org/learn/introspection/)
|
||||
|
||||
## Sorting
|
||||
|
||||
Some of the GitLab GraphQL endpoints allow you to specify how you'd like a collection of
|
||||
objects to be sorted. You can only sort by what the schema allows you to.
|
||||
Some of the GitLab GraphQL endpoints allow you to specify how to sort a
|
||||
collection of objects. You can only sort by what the schema allows you to.
|
||||
|
||||
Example: Issues can be sorted by creation date:
|
||||
|
||||
|
@ -312,17 +321,18 @@ query {
|
|||
|
||||
## Pagination
|
||||
|
||||
Pagination is a way of only asking for a subset of the records (say, the first 10).
|
||||
If we want more of them, we can make another request for the next 10 from the server
|
||||
(in the form of something like "please give me the next 10 records").
|
||||
Pagination is a way of only asking for a subset of the records, such as the
|
||||
first ten. If we want more of them, we can make another request for the next
|
||||
ten from the server in the form of something like `please give me the next ten records`.
|
||||
|
||||
By default, the GitLab GraphQL API returns 100 records per page.
|
||||
This can be changed by using `first` or `last` arguments. Both arguments take a value,
|
||||
so `first: 10` returns the first 10 records, and `last: 10` the last 10 records.
|
||||
There is a limit on how many records will be returned per page, which is generally `100`.
|
||||
By default, the GitLab GraphQL API returns 100 records per page. To change this
|
||||
behavior, use `first` or `last` arguments. Both arguments take a value, so
|
||||
`first: 10` returns the first ten records, and `last: 10` the last ten records.
|
||||
There is a limit on how many records are returned per page, which is generally
|
||||
`100`.
|
||||
|
||||
Example: Retrieve only the first 2 issues (slicing). The `cursor` field gives us a position from which
|
||||
we can retrieve further records relative to that one.
|
||||
Example: Retrieve only the first two issues (slicing). The `cursor` field gives
|
||||
us a position from which we can retrieve further records relative to that one.
|
||||
|
||||
```graphql
|
||||
query {
|
||||
|
@ -343,9 +353,10 @@ query {
|
|||
}
|
||||
```
|
||||
|
||||
Example: Retrieve the next 3. (The cursor value
|
||||
Example: Retrieve the next three. (The cursor value
|
||||
`eyJpZCI6IjI3MDM4OTMzIiwiY3JlYXRlZF9hdCI6IjIwMTktMTEtMTQgMDU6NTY6NDQgVVRDIn0`
|
||||
could be different, but it's the `cursor` value returned for the second issue returned above.)
|
||||
could be different, but it's the `cursor` value returned for the second issue
|
||||
returned above.)
|
||||
|
||||
```graphql
|
||||
query {
|
||||
|
@ -367,5 +378,5 @@ query {
|
|||
}
|
||||
```
|
||||
|
||||
More on pagination and cursors:
|
||||
[GraphQL docs](https://graphql.org/learn/pagination/)
|
||||
More about pagination and cursors:
|
||||
[GraphQL documentation](https://graphql.org/learn/pagination/)
|
||||
|
|
|
@ -336,6 +336,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="queryrunnerssearch"></a>`search` | [`String`](#string) | Filter by full token or partial text in description field. |
|
||||
| <a id="queryrunnerssort"></a>`sort` | [`CiRunnerSort`](#cirunnersort) | Sort order of results. |
|
||||
| <a id="queryrunnersstatus"></a>`status` | [`CiRunnerStatus`](#cirunnerstatus) | Filter runners by status. |
|
||||
| <a id="queryrunnerstaglist"></a>`tagList` | [`[String!]`](#string) | Filter by tags associated with the runner (comma-separated or array). |
|
||||
|
@ -1227,6 +1228,10 @@ Input type: `CreateIssueInput`
|
|||
|
||||
### `Mutation.createIteration`
|
||||
|
||||
WARNING:
|
||||
**Deprecated** in 14.0.
|
||||
Use iterationCreate.
|
||||
|
||||
Input type: `CreateIterationInput`
|
||||
|
||||
#### Arguments
|
||||
|
@ -1237,6 +1242,7 @@ Input type: `CreateIterationInput`
|
|||
| <a id="mutationcreateiterationdescription"></a>`description` | [`String`](#string) | The description of the iteration. |
|
||||
| <a id="mutationcreateiterationduedate"></a>`dueDate` | [`String`](#string) | The end date of the iteration. |
|
||||
| <a id="mutationcreateiterationgrouppath"></a>`groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
|
||||
| <a id="mutationcreateiterationiterationscadenceid"></a>`iterationsCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | Global ID of the iterations cadence to be assigned to newly created iteration. |
|
||||
| <a id="mutationcreateiterationprojectpath"></a>`projectPath` | [`ID`](#id) | Full path of the project with which the resource is associated. |
|
||||
| <a id="mutationcreateiterationstartdate"></a>`startDate` | [`String`](#string) | The start date of the iteration. |
|
||||
| <a id="mutationcreateiterationtitle"></a>`title` | [`String`](#string) | The title of the iteration. |
|
||||
|
@ -2657,6 +2663,31 @@ Input type: `IterationCadenceUpdateInput`
|
|||
| <a id="mutationiterationcadenceupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationiterationcadenceupdateiterationcadence"></a>`iterationCadence` | [`IterationCadence`](#iterationcadence) | The updated iteration cadence. |
|
||||
|
||||
### `Mutation.iterationCreate`
|
||||
|
||||
Input type: `iterationCreateInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationiterationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationiterationcreatedescription"></a>`description` | [`String`](#string) | The description of the iteration. |
|
||||
| <a id="mutationiterationcreateduedate"></a>`dueDate` | [`String`](#string) | The end date of the iteration. |
|
||||
| <a id="mutationiterationcreategrouppath"></a>`groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
|
||||
| <a id="mutationiterationcreateiterationscadenceid"></a>`iterationsCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | Global ID of the iterations cadence to be assigned to newly created iteration. |
|
||||
| <a id="mutationiterationcreateprojectpath"></a>`projectPath` | [`ID`](#id) | Full path of the project with which the resource is associated. |
|
||||
| <a id="mutationiterationcreatestartdate"></a>`startDate` | [`String`](#string) | The start date of the iteration. |
|
||||
| <a id="mutationiterationcreatetitle"></a>`title` | [`String`](#string) | The title of the iteration. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationiterationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationiterationcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationiterationcreateiteration"></a>`iteration` | [`Iteration`](#iteration) | The created iteration. |
|
||||
|
||||
### `Mutation.iterationDelete`
|
||||
|
||||
Input type: `IterationDeleteInput`
|
||||
|
|
|
@ -725,6 +725,28 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
### Download a Group avatar
|
||||
|
||||
Get a group avatar. This endpoint can be accessed without authentication if the
|
||||
group is publicly accessible.
|
||||
|
||||
```plaintext
|
||||
GET /groups/:id/avatar
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | -------------- | -------- | --------------------- |
|
||||
| `id` | integer/string | yes | ID of the group |
|
||||
|
||||
Example:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: $GITLAB_LOCAL_TOKEN" \
|
||||
--remote-header-name \
|
||||
--remote-name \
|
||||
"https://gitlab.example.com/api/v4/groups/4/avatar"
|
||||
```
|
||||
|
||||
### Disable the results limit **(FREE SELF)**
|
||||
|
||||
The 100 results limit can break integrations developed using GitLab 12.4 and earlier.
|
||||
|
|
|
@ -41,6 +41,7 @@ GET /runners?scope=active
|
|||
GET /runners?type=project_type
|
||||
GET /runners?status=active
|
||||
GET /runners?tag_list=tag1,tag2
|
||||
GET /runners?search=gitlab
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|
@ -49,6 +50,7 @@ GET /runners?tag_list=tag1,tag2
|
|||
| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
|
||||
| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
|
||||
| `tag_list` | string array | no | List of the runner's tags |
|
||||
| `search` | string | no | The full token or partial description text to match |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners"
|
||||
|
|
|
@ -1296,15 +1296,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.groups_datadog_active`
|
||||
|
||||
Missing description
|
||||
Count of groups with active integrations for Datadog
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182549_groups_datadog_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.groups_discord_active`
|
||||
|
||||
|
@ -1344,15 +1344,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.groups_ewm_active`
|
||||
|
||||
Missing description
|
||||
Count of groups with active integrations for EWM
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182616_groups_ewm_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.groups_external_wiki_active`
|
||||
|
||||
|
@ -1512,15 +1512,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.groups_inheriting_datadog_active`
|
||||
|
||||
Missing description
|
||||
Count of active groups inheriting integrations for Datadog
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182557_groups_inheriting_datadog_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.groups_inheriting_discord_active`
|
||||
|
||||
|
@ -1560,15 +1560,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.groups_inheriting_ewm_active`
|
||||
|
||||
Missing description
|
||||
Count of active groups inheriting integrations for EWM
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182623_groups_inheriting_ewm_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.groups_inheriting_external_wiki_active`
|
||||
|
||||
|
@ -1704,27 +1704,27 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.groups_inheriting_mock_ci_active`
|
||||
|
||||
Missing description
|
||||
Count of active groups inheriting integrations for Mock CI
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182732_groups_inheriting_mock_ci_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.groups_inheriting_mock_monitoring_active`
|
||||
|
||||
Missing description
|
||||
Count of active groups inheriting integrations for Mock Monitoring
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182743_groups_inheriting_mock_monitoring_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.groups_inheriting_packagist_active`
|
||||
|
||||
|
@ -1944,27 +1944,27 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.groups_mock_ci_active`
|
||||
|
||||
Missing description
|
||||
Count of groups with active integrations for Mock CI
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182724_groups_mock_ci_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.groups_mock_monitoring_active`
|
||||
|
||||
Missing description
|
||||
Count of groups with active integrations for Mock Monitoring
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182736_groups_mock_monitoring_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.groups_packagist_active`
|
||||
|
||||
|
@ -2652,15 +2652,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.instances_datadog_active`
|
||||
|
||||
Missing description
|
||||
Count of active instance-level integrations for Datadog
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182553_instances_datadog_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.instances_discord_active`
|
||||
|
||||
|
@ -2700,15 +2700,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.instances_ewm_active`
|
||||
|
||||
Missing description
|
||||
Count of active instance-level integrations for EWM
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182620_instances_ewm_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.instances_external_wiki_active`
|
||||
|
||||
|
@ -2844,27 +2844,27 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.instances_mock_ci_active`
|
||||
|
||||
Missing description
|
||||
Count of active instance-level integrations for Mock CI
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182728_instances_mock_ci_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.instances_mock_monitoring_active`
|
||||
|
||||
Missing description
|
||||
Count of active instance-level integrations for Mock Monitoring
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182739_instances_mock_monitoring_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.instances_packagist_active`
|
||||
|
||||
|
@ -4308,15 +4308,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.projects_datadog_active`
|
||||
|
||||
Missing description
|
||||
Count of projects with active integrations for Datadog
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182547_projects_datadog_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.projects_discord_active`
|
||||
|
||||
|
@ -4356,15 +4356,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.projects_ewm_active`
|
||||
|
||||
Missing description
|
||||
Count of projects with active integrations for EWM
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182614_projects_ewm_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.projects_external_wiki_active`
|
||||
|
||||
|
@ -4536,15 +4536,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.projects_inheriting_datadog_active`
|
||||
|
||||
Missing description
|
||||
Count of active projects inheriting integrations for Datadog
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182555_projects_inheriting_datadog_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.projects_inheriting_discord_active`
|
||||
|
||||
|
@ -4584,15 +4584,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.projects_inheriting_ewm_active`
|
||||
|
||||
Missing description
|
||||
Count of active projects inheriting integrations for EWM
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182622_projects_inheriting_ewm_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.projects_inheriting_external_wiki_active`
|
||||
|
||||
|
@ -4728,27 +4728,27 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.projects_inheriting_mock_ci_active`
|
||||
|
||||
Missing description
|
||||
Count of active projects inheriting integrations for Mock CI
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182730_projects_inheriting_mock_ci_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.projects_inheriting_mock_monitoring_active`
|
||||
|
||||
Missing description
|
||||
Count of active projects inheriting integrations for Mock Monitoring
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182741_projects_inheriting_mock_monitoring_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.projects_inheriting_packagist_active`
|
||||
|
||||
|
@ -5040,27 +5040,27 @@ Tiers: `premium`, `ultimate`
|
|||
|
||||
### `counts.projects_mock_ci_active`
|
||||
|
||||
Missing description
|
||||
Count of projects with active integrations for Mock CI
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182722_projects_mock_ci_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.projects_mock_monitoring_active`
|
||||
|
||||
Missing description
|
||||
Count of projects with active integrations for Mock Monitoring
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182734_projects_mock_monitoring_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.projects_packagist_active`
|
||||
|
||||
|
@ -5988,15 +5988,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.templates_datadog_active`
|
||||
|
||||
Missing description
|
||||
Count of active service templates for Datadog
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182551_templates_datadog_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.templates_discord_active`
|
||||
|
||||
|
@ -6036,15 +6036,15 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.templates_ewm_active`
|
||||
|
||||
Missing description
|
||||
Count of active service templates for EWM
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182618_templates_ewm_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.templates_external_wiki_active`
|
||||
|
||||
|
@ -6180,27 +6180,27 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `counts.templates_mock_ci_active`
|
||||
|
||||
Missing description
|
||||
Count of active service templates for Mock CI
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182726_templates_mock_ci_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.templates_mock_monitoring_active`
|
||||
|
||||
Missing description
|
||||
Count of active service templates for Mock Monitoring
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_all/20210216182738_templates_mock_monitoring_active.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
Status: `removed`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `counts.templates_packagist_active`
|
||||
|
||||
|
@ -10440,123 +10440,123 @@ Tiers: `free`, `premium`, `ultimate`
|
|||
|
||||
### `redis_hll_counters.ecosystem.ecosystem_total_unique_counts_monthly`
|
||||
|
||||
Missing description
|
||||
Number of users performing actions on Jira issues by month
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210216184957_ecosystem_total_unique_counts_monthly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.ecosystem_total_unique_counts_weekly`
|
||||
|
||||
Missing description
|
||||
Number of users performing actions on Jira issues by week
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210216184955_ecosystem_total_unique_counts_weekly.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210216184955_ecosystem_total_unique_counts_weekly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers:
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_jira_service_close_issue_monthly`
|
||||
|
||||
Missing description
|
||||
Number of users closing Jira issues by month
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210216184941_i_ecosystem_jira_service_close_issue_monthly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_jira_service_close_issue_weekly`
|
||||
|
||||
Missing description
|
||||
Number of users closing Jira issues by week
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210216184939_i_ecosystem_jira_service_close_issue_weekly.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210216184939_i_ecosystem_jira_service_close_issue_weekly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers:
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_jira_service_create_issue_monthly`
|
||||
|
||||
Missing description
|
||||
Number of users creating Jira issues by month
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210216184953_i_ecosystem_jira_service_create_issue_monthly.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210216184953_i_ecosystem_jira_service_create_issue_monthly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_jira_service_create_issue_weekly`
|
||||
|
||||
Missing description
|
||||
Number of users creating Jira issues by week
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210216184951_i_ecosystem_jira_service_create_issue_weekly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers:
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_jira_service_cross_reference_monthly`
|
||||
|
||||
Missing description
|
||||
Number of users that cross-referenced Jira issues by month
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210216184945_i_ecosystem_jira_service_cross_reference_monthly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_jira_service_cross_reference_weekly`
|
||||
|
||||
Missing description
|
||||
Number of users that cross-referenced Jira issues by week
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210216184943_i_ecosystem_jira_service_cross_reference_weekly.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210216184943_i_ecosystem_jira_service_cross_reference_weekly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers:
|
||||
Tiers: `free`, `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_jira_service_list_issues_monthly`
|
||||
|
||||
Missing description
|
||||
Count of Jira Issue List visits by month
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210216184949_i_ecosystem_jira_service_list_issues_monthly.yml)
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210216184949_i_ecosystem_jira_service_list_issues_monthly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `free`
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_jira_service_list_issues_weekly`
|
||||
|
||||
Missing description
|
||||
Count of Jira Issue List visits by week
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210216184947_i_ecosystem_jira_service_list_issues_weekly.yml)
|
||||
|
||||
Group: ``
|
||||
Group: `group::ecosystem`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers:
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `redis_hll_counters.ecosystem.i_ecosystem_slack_service_confidential_issue_notification_monthly`
|
||||
|
||||
|
@ -16260,6 +16260,18 @@ Status: `data_available`
|
|||
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage.enablement.counts.geo_node_usage.git_push_event_count_weekly`
|
||||
|
||||
Number of Git push events from Prometheus on the Geo secondary
|
||||
|
||||
[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210604110603_git_push_event_count_weekly.yml)
|
||||
|
||||
Group: `group::geo`
|
||||
|
||||
Status: `data_available`
|
||||
|
||||
Tiers: `premium`, `ultimate`
|
||||
|
||||
### `usage_activity_by_stage.enablement.geo_secondary_web_oauth_users`
|
||||
|
||||
Missing description
|
||||
|
|
|
@ -40,14 +40,17 @@ collected before this feature is available.
|
|||
|
||||
The DevOps Adoption tab shows you which groups within your organization are using the most essential features of GitLab:
|
||||
|
||||
- Approvals
|
||||
- Code owners
|
||||
- Deployments
|
||||
- Issues
|
||||
- Merge Requests
|
||||
- Pipelines
|
||||
- Runners
|
||||
- Scans
|
||||
- Dev
|
||||
- Issues
|
||||
- Merge Requests
|
||||
- Approvals
|
||||
- Code owners
|
||||
- Sec
|
||||
- Scans
|
||||
- Ops
|
||||
- Runners
|
||||
- Pipelines
|
||||
- Deployments
|
||||
|
||||
Buttons to manage your groups appear in the DevOps Adoption section of the page.
|
||||
|
||||
|
@ -57,6 +60,8 @@ DevOps Adoption allows you to:
|
|||
- Identify specific groups that are lagging in their adoption of GitLab so you can help them along in their DevOps journey.
|
||||
- Find the groups that have adopted certain features and can provide guidance to other groups on how to use those features.
|
||||
|
||||
![DevOps Report](img/admin_devops_adoption_v14_0.png)
|
||||
|
||||
### Disable or enable DevOps Adoption
|
||||
|
||||
DevOps Adoption is deployed behind a feature flag that is **enabled by default**.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
|
@ -24,14 +24,17 @@ To access Group DevOps Adoption, go to your group and select **Analytics > DevOp
|
|||
|
||||
Group DevOps Adoption shows you how individual groups and sub-groups within your organization use the following features:
|
||||
|
||||
- Approvals
|
||||
- Code owners
|
||||
- Deployments
|
||||
- Issues
|
||||
- Merge Requests
|
||||
- Pipelines
|
||||
- Runners
|
||||
- Scans
|
||||
- Dev
|
||||
- Issues
|
||||
- Merge Requests
|
||||
- Approvals
|
||||
- Code owners
|
||||
- Sec
|
||||
- Scans
|
||||
- Ops
|
||||
- Runners
|
||||
- Pipelines
|
||||
- Deployments
|
||||
|
||||
When managing groups in the UI, you can manage your sub-groups with the **Add/Remove sub-groups**
|
||||
button, in the top right hand section of your Groups pages.
|
||||
|
@ -42,7 +45,7 @@ With DevOps Adoption you can:
|
|||
- Identify specific sub-groups that are lagging in their adoption of GitLab so you can help them along in their DevOps journey.
|
||||
- Find the sub-groups that have adopted certain features and can provide guidance to other sub-groups on how to use those features.
|
||||
|
||||
![DevOps Report](img/group_devops_adoption_v13_11.png)
|
||||
![DevOps Report](img/group_devops_adoption_v14_0.png)
|
||||
|
||||
## Enable data processing
|
||||
|
||||
|
|
|
@ -173,6 +173,7 @@ module API
|
|||
mount ::API::Features
|
||||
mount ::API::Files
|
||||
mount ::API::FreezePeriods
|
||||
mount ::API::GroupAvatar
|
||||
mount ::API::GroupBoards
|
||||
mount ::API::GroupClusters
|
||||
mount ::API::GroupExport
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
class GroupAvatar < ::API::Base
|
||||
helpers Helpers::GroupsHelpers
|
||||
|
||||
feature_category :subgroups
|
||||
|
||||
resource :groups do
|
||||
desc 'Download the group avatar' do
|
||||
detail 'This feature was introduced in GitLab 14.0'
|
||||
end
|
||||
params do
|
||||
requires :id, type: String, desc: 'The group id'
|
||||
end
|
||||
get ':id/avatar' do
|
||||
present_carrierwave_file!(user_group.avatar)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -854,9 +854,6 @@ msgstr ""
|
|||
msgid "%{state} epics"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{strongStart}Deletes%{strongEnd} source branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -12562,9 +12559,6 @@ msgstr ""
|
|||
msgid "Environments|Logs from %{start} to %{end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Managed apps"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|More information"
|
||||
msgstr ""
|
||||
|
||||
|
@ -32784,6 +32778,9 @@ msgstr ""
|
|||
msgid "The snippet is visible to any logged in user except external users."
|
||||
msgstr ""
|
||||
|
||||
msgid "The source branch will be deleted"
|
||||
msgstr ""
|
||||
|
||||
msgid "The specified tab is invalid, please select another"
|
||||
msgstr ""
|
||||
|
||||
|
@ -34369,6 +34366,9 @@ msgstr ""
|
|||
msgid "Too many projects enabled. You will need to manage them via the console or the API."
|
||||
msgstr ""
|
||||
|
||||
msgid "TopNav|Go back"
|
||||
msgstr ""
|
||||
|
||||
msgid "Topics (optional)"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -432,7 +432,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
|
|||
|
||||
it 'user cannot remove source branch', :sidekiq_might_not_need_inline do
|
||||
expect(page).not_to have_field('remove-source-branch-input')
|
||||
expect(page).to have_content('Deletes source branch')
|
||||
expect(page).to have_content('The source branch will be deleted')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ RSpec.describe 'top nav responsive', :js do
|
|||
include MobileHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:responsive_menu_text) { 'Placeholder for responsive top nav' }
|
||||
|
||||
before do
|
||||
stub_feature_flags(combined_menu: true)
|
||||
|
@ -20,7 +19,9 @@ RSpec.describe 'top nav responsive', :js do
|
|||
context 'before opened' do
|
||||
it 'has page content and hides responsive menu', :aggregate_failures do
|
||||
expect(page).to have_css('.page-title', text: 'Projects')
|
||||
expect(page).to have_no_text(responsive_menu_text)
|
||||
expect(page).to have_link('Dashboard', id: 'logo')
|
||||
|
||||
expect(page).to have_no_css('.top-nav-responsive')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -31,8 +32,22 @@ RSpec.describe 'top nav responsive', :js do
|
|||
|
||||
it 'hides everything and shows responsive menu', :aggregate_failures do
|
||||
expect(page).to have_no_css('.page-title', text: 'Projects')
|
||||
expect(page).to have_link('Dashboard', id: 'logo')
|
||||
expect(page).to have_text(responsive_menu_text)
|
||||
expect(page).to have_no_link('Dashboard', id: 'logo')
|
||||
|
||||
within '.top-nav-responsive' do
|
||||
expect(page).to have_link(nil, href: search_path)
|
||||
expect(page).to have_button('Projects')
|
||||
expect(page).to have_button('Groups')
|
||||
expect(page).to have_link('Snippets', href: dashboard_snippets_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'has new dropdown', :aggregate_failures do
|
||||
click_button('New...')
|
||||
|
||||
expect(page).to have_link('New project', href: new_project_path)
|
||||
expect(page).to have_link('New group', href: new_group_path)
|
||||
expect(page).to have_link('New snippet', href: new_snippet_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,7 +40,7 @@ RSpec.describe 'Environment > Pod Logs', :js, :kubeclient do
|
|||
|
||||
dropdown_items = find(".dropdown-menu").all(".dropdown-item")
|
||||
expect(dropdown_items.first).to have_content(environment.name)
|
||||
expect(dropdown_items.size).to eq(3)
|
||||
expect(dropdown_items.size).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
mockTrace,
|
||||
mockEnvironmentsEndpoint,
|
||||
mockDocumentationPath,
|
||||
mockManagedAppsEndpoint,
|
||||
} from '../mock_data';
|
||||
|
||||
jest.mock('~/lib/utils/scroll_utils');
|
||||
|
@ -35,7 +34,7 @@ describe('EnvironmentLogs', () => {
|
|||
environmentName: mockEnvName,
|
||||
environmentsPath: mockEnvironmentsEndpoint,
|
||||
clusterApplicationsDocumentationPath: mockDocumentationPath,
|
||||
clustersPath: mockManagedAppsEndpoint,
|
||||
clustersPath: '/gitlab-org',
|
||||
};
|
||||
|
||||
const updateControlBtnsMock = jest.fn();
|
||||
|
|
|
@ -7,8 +7,6 @@ export const mockDocumentationPath = '/documentation.md';
|
|||
export const mockLogsEndpoint = '/dummy_logs_path.json';
|
||||
export const mockCursor = 'MOCK_CURSOR';
|
||||
export const mockNextCursor = 'MOCK_NEXT_CURSOR';
|
||||
export const mockManagedAppName = 'kubernetes-cluster-1';
|
||||
export const mockManagedAppsEndpoint = `${mockProjectPath}/clusters.json`;
|
||||
|
||||
const makeMockEnvironment = (id, name, advancedQuerying) => ({
|
||||
id,
|
||||
|
@ -25,31 +23,6 @@ export const mockEnvironments = [
|
|||
makeMockEnvironment(102, 'review/a-feature', false),
|
||||
];
|
||||
|
||||
export const mockManagedApps = [
|
||||
{
|
||||
cluster_type: 'project_type',
|
||||
enabled: true,
|
||||
environment_scope: '*',
|
||||
name: 'kubernetes-cluster-1',
|
||||
provider_type: 'user',
|
||||
status: 'connected',
|
||||
path: '/root/autodevops-deploy/-/clusters/15',
|
||||
gitlab_managed_apps_logs_path: '/root/autodevops-deploy/-/logs?cluster_id=15',
|
||||
enable_advanced_logs_querying: true,
|
||||
},
|
||||
{
|
||||
cluster_type: 'project_type',
|
||||
enabled: true,
|
||||
environment_scope: '*',
|
||||
name: 'kubernetes-cluster-2',
|
||||
provider_type: 'user',
|
||||
status: 'connected',
|
||||
path: '/root/autodevops-deploy/-/clusters/16',
|
||||
gitlab_managed_apps_logs_path: null,
|
||||
enable_advanced_logs_querying: false,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockPodName = 'production-764c58d697-aaaaa';
|
||||
export const mockPods = [
|
||||
mockPodName,
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
fetchEnvironments,
|
||||
fetchLogs,
|
||||
fetchMoreLogsPrepend,
|
||||
fetchManagedApps,
|
||||
} from '~/logs/stores/actions';
|
||||
import * as types from '~/logs/stores/mutation_types';
|
||||
import logsPageState from '~/logs/stores/state';
|
||||
|
@ -31,8 +30,6 @@ import {
|
|||
mockResponse,
|
||||
mockCursor,
|
||||
mockNextCursor,
|
||||
mockManagedApps,
|
||||
mockManagedAppsEndpoint,
|
||||
} from '../mock_data';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
@ -219,30 +216,6 @@ describe('Logs Store actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('fetchManagedApps', () => {
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
});
|
||||
|
||||
it('should commit RECEIVE_MANAGED_APPS_DATA_SUCCESS mutation on succesful fetch', () => {
|
||||
mock.onGet(mockManagedAppsEndpoint).replyOnce(200, { clusters: mockManagedApps });
|
||||
return testAction(fetchManagedApps, mockManagedAppsEndpoint, state, [
|
||||
{ type: types.RECEIVE_MANAGED_APPS_DATA_SUCCESS, payload: mockManagedApps },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should commit RECEIVE_MANAGED_APPS_DATA_ERROR on wrong data', () => {
|
||||
mock.onGet(mockManagedAppsEndpoint).replyOnce(500);
|
||||
return testAction(
|
||||
fetchManagedApps,
|
||||
mockManagedAppsEndpoint,
|
||||
state,
|
||||
[{ type: types.RECEIVE_MANAGED_APPS_DATA_ERROR }],
|
||||
[],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the backend responds succesfully', () => {
|
||||
let expectedMutations;
|
||||
let expectedActions;
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
import { trace, showAdvancedFilters } from '~/logs/stores/getters';
|
||||
import logsPageState from '~/logs/stores/state';
|
||||
|
||||
import {
|
||||
mockLogsResult,
|
||||
mockTrace,
|
||||
mockEnvName,
|
||||
mockEnvironments,
|
||||
mockManagedApps,
|
||||
mockManagedAppName,
|
||||
} from '../mock_data';
|
||||
import { mockLogsResult, mockTrace, mockEnvName, mockEnvironments } from '../mock_data';
|
||||
|
||||
describe('Logs Store getters', () => {
|
||||
let state;
|
||||
|
@ -79,43 +72,4 @@ describe('Logs Store getters', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when no managedApps are set', () => {
|
||||
beforeEach(() => {
|
||||
state.environments.current = null;
|
||||
state.environments.options = [];
|
||||
state.managedApps.current = mockManagedAppName;
|
||||
state.managedApps.options = [];
|
||||
});
|
||||
|
||||
it('returns false', () => {
|
||||
expect(showAdvancedFilters(state)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the managedApp supports filters', () => {
|
||||
beforeEach(() => {
|
||||
state.environments.current = null;
|
||||
state.environments.options = mockEnvironments;
|
||||
state.managedApps.current = mockManagedAppName;
|
||||
state.managedApps.options = mockManagedApps;
|
||||
});
|
||||
|
||||
it('returns true', () => {
|
||||
expect(showAdvancedFilters(state)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the managedApp does not support filters', () => {
|
||||
beforeEach(() => {
|
||||
state.environments.current = null;
|
||||
state.environments.options = mockEnvironments;
|
||||
state.managedApps.options = mockManagedApps;
|
||||
state.managedApps.current = mockManagedApps[1].name;
|
||||
});
|
||||
|
||||
it('returns false', () => {
|
||||
expect(showAdvancedFilters(state)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,8 +11,6 @@ import {
|
|||
mockSearch,
|
||||
mockCursor,
|
||||
mockNextCursor,
|
||||
mockManagedApps,
|
||||
mockManagedAppName,
|
||||
} from '../mock_data';
|
||||
|
||||
describe('Logs Store Mutations', () => {
|
||||
|
@ -32,15 +30,6 @@ describe('Logs Store Mutations', () => {
|
|||
it('sets the environment', () => {
|
||||
mutations[types.SET_PROJECT_ENVIRONMENT](state, mockEnvName);
|
||||
expect(state.environments.current).toEqual(mockEnvName);
|
||||
expect(state.managedApps.current).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SET_MANAGED_APP', () => {
|
||||
it('sets the managed app', () => {
|
||||
mutations[types.SET_MANAGED_APP](state, mockManagedAppName);
|
||||
expect(state.managedApps.current).toBe(mockManagedAppName);
|
||||
expect(state.environments.current).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -265,29 +254,4 @@ describe('Logs Store Mutations', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RECEIVE_MANAGED_APPS_DATA_SUCCESS', () => {
|
||||
it('receives managed apps data success', () => {
|
||||
expect(state.managedApps.options).toEqual([]);
|
||||
|
||||
mutations[types.RECEIVE_MANAGED_APPS_DATA_SUCCESS](state, mockManagedApps);
|
||||
|
||||
expect(state.managedApps.options.length).toEqual(1);
|
||||
expect(state.managedApps.options).toEqual([mockManagedApps[0]]);
|
||||
expect(state.managedApps.isLoading).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RECEIVE_MANAGED_APPS_DATA_ERROR', () => {
|
||||
it('received managed apps data error', () => {
|
||||
mutations[types.RECEIVE_MANAGED_APPS_DATA_ERROR](state);
|
||||
|
||||
expect(state.managedApps).toEqual({
|
||||
options: [],
|
||||
isLoading: false,
|
||||
current: null,
|
||||
fetchError: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { range } from 'lodash';
|
||||
import ResponsiveApp from '~/nav/components/responsive_app.vue';
|
||||
import ResponsiveHeader from '~/nav/components/responsive_header.vue';
|
||||
import ResponsiveHome from '~/nav/components/responsive_home.vue';
|
||||
import TopNavContainerView from '~/nav/components/top_nav_container_view.vue';
|
||||
import eventHub, { EVENT_RESPONSIVE_TOGGLE } from '~/nav/event_hub';
|
||||
import { resetMenuItemsActive } from '~/nav/utils/reset_menu_items_active';
|
||||
import KeepAliveSlots from '~/vue_shared/components/keep_alive_slots.vue';
|
||||
import { TEST_NAV_DATA } from '../mock_data';
|
||||
|
||||
describe('~/nav/components/responsive_app.vue', () => {
|
||||
|
@ -12,11 +17,19 @@ describe('~/nav/components/responsive_app.vue', () => {
|
|||
propsData: {
|
||||
navData: TEST_NAV_DATA,
|
||||
},
|
||||
stubs: {
|
||||
KeepAliveSlots,
|
||||
},
|
||||
});
|
||||
};
|
||||
const triggerResponsiveToggle = () => eventHub.$emit(EVENT_RESPONSIVE_TOGGLE);
|
||||
|
||||
const findHome = () => wrapper.findComponent(ResponsiveHome);
|
||||
const findMobileOverlay = () => wrapper.find('[data-testid="mobile-overlay"]');
|
||||
const findSubviewHeader = () => wrapper.findComponent(ResponsiveHeader);
|
||||
const findSubviewContainer = () => wrapper.findComponent(TopNavContainerView);
|
||||
const hasBodyResponsiveOpen = () => document.body.classList.contains('top-nav-responsive-open');
|
||||
const hasMobileOverlayVisible = () => findMobileOverlay().classes('mobile-nav-open');
|
||||
|
||||
beforeEach(() => {
|
||||
// Add test class to reset state + assert that we're adding classes correctly
|
||||
|
@ -32,6 +45,13 @@ describe('~/nav/components/responsive_app.vue', () => {
|
|||
createComponent();
|
||||
});
|
||||
|
||||
it('shows home by default', () => {
|
||||
expect(findHome().isVisible()).toBe(true);
|
||||
expect(findHome().props()).toEqual({
|
||||
navData: resetMenuItemsActive(TEST_NAV_DATA),
|
||||
});
|
||||
});
|
||||
|
||||
it.each`
|
||||
times | expectation
|
||||
${0} | ${false}
|
||||
|
@ -45,6 +65,78 @@ describe('~/nav/components/responsive_app.vue', () => {
|
|||
expect(hasBodyResponsiveOpen()).toBe(expectation);
|
||||
},
|
||||
);
|
||||
|
||||
it.each`
|
||||
events | expectation
|
||||
${[]} | ${false}
|
||||
${['bv::dropdown::show']} | ${true}
|
||||
${['bv::dropdown::show', 'bv::dropdown::hide']} | ${false}
|
||||
`(
|
||||
'with root events $events, movile overlay visible = $expectation',
|
||||
async ({ events, expectation }) => {
|
||||
// `await...reduce(async` is like doing an `forEach(async (...))` excpet it works
|
||||
await events.reduce(async (acc, evt) => {
|
||||
await acc;
|
||||
|
||||
wrapper.vm.$root.$emit(evt);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
}, Promise.resolve());
|
||||
|
||||
expect(hasMobileOverlayVisible()).toBe(expectation);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const projectsContainerProps = {
|
||||
containerClass: 'gl-px-3',
|
||||
frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.namespace,
|
||||
frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.vuexModule,
|
||||
linksPrimary: TEST_NAV_DATA.views.projects.linksPrimary,
|
||||
linksSecondary: TEST_NAV_DATA.views.projects.linksSecondary,
|
||||
};
|
||||
const groupsContainerProps = {
|
||||
containerClass: 'gl-px-3',
|
||||
frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_GROUPS.namespace,
|
||||
frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_GROUPS.vuexModule,
|
||||
linksPrimary: TEST_NAV_DATA.views.groups.linksPrimary,
|
||||
linksSecondary: TEST_NAV_DATA.views.groups.linksSecondary,
|
||||
};
|
||||
|
||||
describe.each`
|
||||
view | header | containerProps
|
||||
${'projects'} | ${'Projects'} | ${projectsContainerProps}
|
||||
${'groups'} | ${'Groups'} | ${groupsContainerProps}
|
||||
`('when menu item with $view is clicked', ({ view, header, containerProps }) => {
|
||||
beforeEach(async () => {
|
||||
createComponent();
|
||||
|
||||
findHome().vm.$emit('menu-item-click', { view });
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('shows header', () => {
|
||||
expect(findSubviewHeader().text()).toBe(header);
|
||||
});
|
||||
|
||||
it('shows container subview', () => {
|
||||
expect(findSubviewContainer().props()).toEqual(containerProps);
|
||||
});
|
||||
|
||||
it('hides home', () => {
|
||||
expect(findHome().isVisible()).toBe(false);
|
||||
});
|
||||
|
||||
describe('when header back button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
findSubviewHeader().vm.$emit('menu-item-click', { view: 'home' });
|
||||
});
|
||||
|
||||
it('shows home', () => {
|
||||
expect(findHome().isVisible()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when destroyed', () => {
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import ResponsiveHeader from '~/nav/components/responsive_header.vue';
|
||||
import TopNavMenuItem from '~/nav/components/top_nav_menu_item.vue';
|
||||
|
||||
const TEST_SLOT_CONTENT = 'Test slot content';
|
||||
|
||||
describe('~/nav/components/top_nav_menu_sections.vue', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(ResponsiveHeader, {
|
||||
slots: {
|
||||
default: TEST_SLOT_CONTENT,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: createMockDirective(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findMenuItem = () => wrapper.findComponent(TopNavMenuItem);
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders slot', () => {
|
||||
expect(wrapper.text()).toBe(TEST_SLOT_CONTENT);
|
||||
});
|
||||
|
||||
it('renders back button', () => {
|
||||
const button = findMenuItem();
|
||||
|
||||
const tooltip = getBinding(button.element, 'gl-tooltip').value.title;
|
||||
|
||||
expect(tooltip).toBe('Go back');
|
||||
expect(button.props()).toEqual({
|
||||
menuItem: {
|
||||
id: 'home',
|
||||
view: 'home',
|
||||
icon: 'angle-left',
|
||||
},
|
||||
iconOnly: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('emits nothing', () => {
|
||||
expect(wrapper.emitted()).toEqual({});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
findMenuItem().vm.$emit('click');
|
||||
});
|
||||
|
||||
it('emits menu-item-click', () => {
|
||||
expect(wrapper.emitted()).toEqual({
|
||||
'menu-item-click': [[{ id: 'home', view: 'home', icon: 'angle-left' }]],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,137 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import ResponsiveHome from '~/nav/components/responsive_home.vue';
|
||||
import TopNavMenuItem from '~/nav/components/top_nav_menu_item.vue';
|
||||
import TopNavMenuSections from '~/nav/components/top_nav_menu_sections.vue';
|
||||
import TopNavNewDropdown from '~/nav/components/top_nav_new_dropdown.vue';
|
||||
import { TEST_NAV_DATA } from '../mock_data';
|
||||
|
||||
const TEST_SEARCH_MENU_ITEM = {
|
||||
id: 'search',
|
||||
title: 'search',
|
||||
icon: 'search',
|
||||
href: '/search',
|
||||
};
|
||||
|
||||
const TEST_NEW_DROPDOWN_VIEW_MODEL = {
|
||||
title: 'new',
|
||||
menu_sections: [],
|
||||
};
|
||||
|
||||
describe('~/nav/components/responsive_home.vue', () => {
|
||||
let wrapper;
|
||||
let menuItemClickListener;
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(ResponsiveHome, {
|
||||
propsData: {
|
||||
navData: TEST_NAV_DATA,
|
||||
...props,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: createMockDirective(),
|
||||
},
|
||||
listeners: {
|
||||
'menu-item-click': menuItemClickListener,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findSearchMenuItem = () => wrapper.findComponent(TopNavMenuItem);
|
||||
const findNewDropdown = () => wrapper.findComponent(TopNavNewDropdown);
|
||||
const findMenuSections = () => wrapper.findComponent(TopNavMenuSections);
|
||||
|
||||
beforeEach(() => {
|
||||
menuItemClickListener = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('default', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it.each`
|
||||
desc | fn
|
||||
${'does not show search menu item'} | ${findSearchMenuItem}
|
||||
${'does not show new dropdown'} | ${findNewDropdown}
|
||||
`('$desc', ({ fn }) => {
|
||||
expect(fn().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('shows menu sections', () => {
|
||||
expect(findMenuSections().props('sections')).toEqual([
|
||||
{ id: 'primary', menuItems: TEST_NAV_DATA.primary },
|
||||
{ id: 'secondary', menuItems: TEST_NAV_DATA.secondary },
|
||||
]);
|
||||
});
|
||||
|
||||
it('emits when menu sections emits', () => {
|
||||
expect(menuItemClickListener).not.toHaveBeenCalled();
|
||||
|
||||
findMenuSections().vm.$emit('menu-item-click', TEST_NAV_DATA.primary[0]);
|
||||
|
||||
expect(menuItemClickListener).toHaveBeenCalledWith(TEST_NAV_DATA.primary[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('without secondary', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ navData: { ...TEST_NAV_DATA, secondary: null } });
|
||||
});
|
||||
|
||||
it('shows menu sections', () => {
|
||||
expect(findMenuSections().props('sections')).toEqual([
|
||||
{ id: 'primary', menuItems: TEST_NAV_DATA.primary },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with search view', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
navData: {
|
||||
...TEST_NAV_DATA,
|
||||
views: { search: TEST_SEARCH_MENU_ITEM },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('shows search menu item', () => {
|
||||
expect(findSearchMenuItem().props()).toEqual({
|
||||
menuItem: TEST_SEARCH_MENU_ITEM,
|
||||
iconOnly: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('shows tooltip for search', () => {
|
||||
const tooltip = getBinding(findSearchMenuItem().element, 'gl-tooltip');
|
||||
expect(tooltip.value).toEqual({ title: TEST_SEARCH_MENU_ITEM.title });
|
||||
});
|
||||
});
|
||||
|
||||
describe('with new view', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
navData: {
|
||||
...TEST_NAV_DATA,
|
||||
views: { new: TEST_NEW_DROPDOWN_VIEW_MODEL },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('shows new dropdown', () => {
|
||||
expect(findNewDropdown().props()).toEqual({
|
||||
viewModel: TEST_NEW_DROPDOWN_VIEW_MODEL,
|
||||
});
|
||||
});
|
||||
|
||||
it('shows tooltip for new dropdown', () => {
|
||||
const tooltip = getBinding(findNewDropdown().element, 'gl-tooltip');
|
||||
expect(tooltip.value).toEqual({ title: TEST_NEW_DROPDOWN_VIEW_MODEL.title });
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,6 +13,7 @@ const DEFAULT_PROPS = {
|
|||
frequentItemsVuexModule: FREQUENT_ITEMS_PROJECTS.vuexModule,
|
||||
linksPrimary: TEST_NAV_DATA.primary,
|
||||
linksSecondary: TEST_NAV_DATA.secondary,
|
||||
containerClass: 'test-frequent-items-container-class',
|
||||
};
|
||||
const TEST_OTHER_PROPS = {
|
||||
namespace: 'projects',
|
||||
|
@ -44,6 +45,7 @@ describe('~/nav/components/top_nav_container_view.vue', () => {
|
|||
attributes: parent.findComponent(FrequentItemsApp).attributes(),
|
||||
};
|
||||
};
|
||||
const findFrequentItemsContainer = () => wrapper.find('[data-testid="frequent-items-container"]');
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
|
@ -85,6 +87,10 @@ describe('~/nav/components/top_nav_container_view.vue', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('renders given container class', () => {
|
||||
expect(findFrequentItemsContainer().classes(DEFAULT_PROPS.containerClass)).toBe(true);
|
||||
});
|
||||
|
||||
it('renders menu sections', () => {
|
||||
const sections = [
|
||||
{ id: 'primary', menuItems: TEST_NAV_DATA.primary },
|
||||
|
|
|
@ -30,7 +30,10 @@ describe('~/nav/components/top_nav_menu_item.vue', () => {
|
|||
const findButtonIcons = () =>
|
||||
findButton()
|
||||
.findAllComponents(GlIcon)
|
||||
.wrappers.map((x) => x.props('name'));
|
||||
.wrappers.map((x) => ({
|
||||
name: x.props('name'),
|
||||
classes: x.classes(),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
listener = jest.fn();
|
||||
|
@ -65,11 +68,42 @@ describe('~/nav/components/top_nav_menu_item.vue', () => {
|
|||
|
||||
expect(listener).toHaveBeenCalledWith('TEST');
|
||||
});
|
||||
|
||||
it('renders expected icons', () => {
|
||||
expect(findButtonIcons()).toEqual([
|
||||
{
|
||||
name: TEST_MENU_ITEM.icon,
|
||||
classes: ['gl-mr-2!'],
|
||||
},
|
||||
{
|
||||
name: 'chevron-right',
|
||||
classes: ['gl-ml-auto'],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with icon-only', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ iconOnly: true });
|
||||
});
|
||||
|
||||
it('does not render title or view icon', () => {
|
||||
expect(wrapper.text()).toBe('');
|
||||
});
|
||||
|
||||
it('only renders menuItem icon', () => {
|
||||
expect(findButtonIcons()).toEqual([
|
||||
{
|
||||
name: TEST_MENU_ITEM.icon,
|
||||
classes: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
desc | menuItem | expectedIcons
|
||||
${'default'} | ${TEST_MENU_ITEM} | ${[TEST_MENU_ITEM.icon, 'chevron-right']}
|
||||
${'with no icon'} | ${{ ...TEST_MENU_ITEM, icon: null }} | ${['chevron-right']}
|
||||
${'with no view'} | ${{ ...TEST_MENU_ITEM, view: null }} | ${[TEST_MENU_ITEM.icon]}
|
||||
${'with no icon or view'} | ${{ ...TEST_MENU_ITEM, view: null, icon: null }} | ${[]}
|
||||
|
@ -79,7 +113,7 @@ describe('~/nav/components/top_nav_menu_item.vue', () => {
|
|||
});
|
||||
|
||||
it(`renders expected icons ${JSON.stringify(expectedIcons)}`, () => {
|
||||
expect(findButtonIcons()).toEqual(expectedIcons);
|
||||
expect(findButtonIcons().map((x) => x.name)).toEqual(expectedIcons);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -51,11 +51,11 @@ describe('~/nav/components/top_nav_menu_sections.vue', () => {
|
|||
menuItems: [
|
||||
{
|
||||
menuItem: TEST_SECTIONS[0].menuItems[0],
|
||||
classes: [],
|
||||
classes: ['gl-w-full'],
|
||||
},
|
||||
...TEST_SECTIONS[0].menuItems.slice(1).map((menuItem) => ({
|
||||
menuItem,
|
||||
classes: ['gl-mt-1'],
|
||||
classes: ['gl-w-full', 'gl-mt-1'],
|
||||
})),
|
||||
],
|
||||
},
|
||||
|
@ -64,11 +64,11 @@ describe('~/nav/components/top_nav_menu_sections.vue', () => {
|
|||
menuItems: [
|
||||
{
|
||||
menuItem: TEST_SECTIONS[1].menuItems[0],
|
||||
classes: [],
|
||||
classes: ['gl-w-full'],
|
||||
},
|
||||
...TEST_SECTIONS[1].menuItems.slice(1).map((menuItem) => ({
|
||||
menuItem,
|
||||
classes: ['gl-mt-1'],
|
||||
classes: ['gl-w-full', 'gl-mt-1'],
|
||||
})),
|
||||
],
|
||||
},
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
import { GlDropdown } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import TopNavNewDropdown from '~/nav/components/top_nav_new_dropdown.vue';
|
||||
|
||||
const TEST_VIEW_MODEL = {
|
||||
title: 'Dropdown',
|
||||
menu_sections: [
|
||||
{
|
||||
title: 'Section 1',
|
||||
menu_items: [
|
||||
{ id: 'foo-1', title: 'Foo 1', href: '/foo/1' },
|
||||
{ id: 'foo-2', title: 'Foo 2', href: '/foo/2' },
|
||||
{ id: 'foo-3', title: 'Foo 3', href: '/foo/3' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Section 2',
|
||||
menu_items: [
|
||||
{ id: 'bar-1', title: 'Bar 1', href: '/bar/1' },
|
||||
{ id: 'bar-2', title: 'Bar 2', href: '/bar/2' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('~/nav/components/top_nav_menu_sections.vue', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(TopNavNewDropdown, {
|
||||
propsData: {
|
||||
viewModel: TEST_VIEW_MODEL,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findDropdownContents = () =>
|
||||
findDropdown()
|
||||
.findAll('[data-testid]')
|
||||
.wrappers.map((child) => {
|
||||
const type = child.attributes('data-testid');
|
||||
|
||||
if (type === 'divider') {
|
||||
return { type };
|
||||
} else if (type === 'header') {
|
||||
return { type, text: child.text() };
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
text: child.text(),
|
||||
href: child.attributes('href'),
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('default', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders dropdown parent', () => {
|
||||
expect(findDropdown().props()).toMatchObject({
|
||||
text: TEST_VIEW_MODEL.title,
|
||||
textSrOnly: true,
|
||||
icon: 'plus',
|
||||
});
|
||||
});
|
||||
|
||||
it('renders dropdown content', () => {
|
||||
expect(findDropdownContents()).toEqual([
|
||||
{
|
||||
type: 'header',
|
||||
text: TEST_VIEW_MODEL.menu_sections[0].title,
|
||||
},
|
||||
...TEST_VIEW_MODEL.menu_sections[0].menu_items.map(({ title, href }) => ({
|
||||
type: 'item',
|
||||
href,
|
||||
text: title,
|
||||
})),
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
type: 'header',
|
||||
text: TEST_VIEW_MODEL.menu_sections[1].title,
|
||||
},
|
||||
...TEST_VIEW_MODEL.menu_sections[1].menu_items.map(({ title, href }) => ({
|
||||
type: 'item',
|
||||
href,
|
||||
text: title,
|
||||
})),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with only 1 section', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
viewModel: {
|
||||
...TEST_VIEW_MODEL,
|
||||
menu_sections: TEST_VIEW_MODEL.menu_sections.slice(0, 1),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders dropdown content without headers and dividers', () => {
|
||||
expect(findDropdownContents()).toEqual(
|
||||
TEST_VIEW_MODEL.menu_sections[0].menu_items.map(({ title, href }) => ({
|
||||
type: 'item',
|
||||
href,
|
||||
text: title,
|
||||
})),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -25,11 +25,15 @@ export const TEST_NAV_DATA = {
|
|||
namespace: 'projects',
|
||||
currentUserName: '',
|
||||
currentItem: {},
|
||||
linksPrimary: [{ id: 'project-link', href: '/path/to/projects', title: 'Project Link' }],
|
||||
linksSecondary: [],
|
||||
},
|
||||
groups: {
|
||||
namespace: 'groups',
|
||||
currentUserName: '',
|
||||
currentItem: {},
|
||||
linksPrimary: [],
|
||||
linksSecondary: [{ id: 'group-link', href: '/path/to/groups', title: 'Group Link' }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -532,7 +532,7 @@ describe('MrWidgetOptions', () => {
|
|||
nextTick(() => {
|
||||
const tooltip = wrapper.find('[data-testid="question-o-icon"]');
|
||||
|
||||
expect(wrapper.text()).toContain('Deletes source branch');
|
||||
expect(wrapper.text()).toContain('The source branch will be deleted');
|
||||
expect(tooltip.attributes('title')).toBe(
|
||||
'A user with write access to the source branch selected this option',
|
||||
);
|
||||
|
@ -548,7 +548,7 @@ describe('MrWidgetOptions', () => {
|
|||
|
||||
nextTick(() => {
|
||||
expect(wrapper.text()).toContain('The source branch has been deleted');
|
||||
expect(wrapper.text()).not.toContain('Deletes source branch');
|
||||
expect(wrapper.text()).not.toContain('The source branch will be deleted');
|
||||
|
||||
done();
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue