Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4b074c5f63
commit
5f5f492fe2
|
|
@ -1 +1 @@
|
|||
a7bc2f86b507daaaf9f18e0ea189b062d6149720
|
||||
c2c12e3152bfc6c899b5ad08974659ac7b450232
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlBadge } from '@gitlab/ui';
|
||||
import { mapState } from 'vuex';
|
||||
import { GROUP_BADGE } from '../constants';
|
||||
import BadgeListRow from './badge_list_row.vue';
|
||||
|
|
@ -9,6 +9,7 @@ export default {
|
|||
components: {
|
||||
BadgeListRow,
|
||||
GlLoadingIcon,
|
||||
GlBadge,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['badges', 'isLoading', 'kind']),
|
||||
|
|
@ -26,7 +27,7 @@ export default {
|
|||
<div class="card">
|
||||
<div class="card-header">
|
||||
{{ s__('Badges|Your badges') }}
|
||||
<span v-show="!isLoading" class="badge badge-pill">{{ badges.length }}</span>
|
||||
<gl-badge v-show="!isLoading" size="sm">{{ badges.length }}</gl-badge>
|
||||
</div>
|
||||
<gl-loading-icon v-show="isLoading" size="lg" class="card-body" />
|
||||
<div v-if="hasNoBadges" class="card-body">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import eventHub from '../eventhub';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlLoadingIcon,
|
||||
GlButton,
|
||||
},
|
||||
props: {
|
||||
deployKey: {
|
||||
|
|
@ -15,10 +15,20 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
btnCssClass: {
|
||||
category: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'btn-default',
|
||||
default: 'tertiary',
|
||||
},
|
||||
variant: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'default',
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
|
@ -39,13 +49,14 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
:class="[{ disabled: isLoading }, btnCssClass]"
|
||||
:disabled="isLoading"
|
||||
<gl-button
|
||||
:category="category"
|
||||
:variant="variant"
|
||||
:icon="icon"
|
||||
:loading="isLoading"
|
||||
class="btn"
|
||||
@click="doAction"
|
||||
>
|
||||
<slot></slot>
|
||||
<gl-loading-icon v-if="isLoading" :inline="true" />
|
||||
</button>
|
||||
</gl-button>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
|
|||
import eventHub from '../eventhub';
|
||||
import DeployKeysService from '../service';
|
||||
import DeployKeysStore from '../store';
|
||||
import ConfirmModal from './confirm_modal.vue';
|
||||
import KeysPanel from './keys_panel.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ConfirmModal,
|
||||
KeysPanel,
|
||||
NavigationTabs,
|
||||
GlLoadingIcon,
|
||||
|
|
@ -30,6 +32,9 @@ export default {
|
|||
currentTab: 'enabled_keys',
|
||||
isLoading: false,
|
||||
store: new DeployKeysStore(),
|
||||
removeKey: () => {},
|
||||
cancel: () => {},
|
||||
confirmModalVisible: false,
|
||||
};
|
||||
},
|
||||
scopes: {
|
||||
|
|
@ -61,16 +66,16 @@ export default {
|
|||
this.service = new DeployKeysService(this.endpoint);
|
||||
|
||||
eventHub.$on('enable.key', this.enableKey);
|
||||
eventHub.$on('remove.key', this.disableKey);
|
||||
eventHub.$on('disable.key', this.disableKey);
|
||||
eventHub.$on('remove.key', this.confirmRemoveKey);
|
||||
eventHub.$on('disable.key', this.confirmRemoveKey);
|
||||
},
|
||||
mounted() {
|
||||
this.fetchKeys();
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off('enable.key', this.enableKey);
|
||||
eventHub.$off('remove.key', this.disableKey);
|
||||
eventHub.$off('disable.key', this.disableKey);
|
||||
eventHub.$off('remove.key', this.confirmRemoveKey);
|
||||
eventHub.$off('disable.key', this.confirmRemoveKey);
|
||||
},
|
||||
methods: {
|
||||
onChangeTab(tab) {
|
||||
|
|
@ -97,19 +102,20 @@ export default {
|
|||
.then(this.fetchKeys)
|
||||
.catch(() => new Flash(s__('DeployKeys|Error enabling deploy key')));
|
||||
},
|
||||
disableKey(deployKey, callback) {
|
||||
if (
|
||||
// eslint-disable-next-line no-alert
|
||||
window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))
|
||||
) {
|
||||
confirmRemoveKey(deployKey, callback) {
|
||||
const hideModal = () => {
|
||||
this.confirmModalVisible = false;
|
||||
callback?.();
|
||||
};
|
||||
this.removeKey = () => {
|
||||
this.service
|
||||
.disableKey(deployKey.id)
|
||||
.then(this.fetchKeys)
|
||||
.then(callback)
|
||||
.then(hideModal)
|
||||
.catch(() => new Flash(s__('DeployKeys|Error removing deploy key')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
this.cancel = hideModal;
|
||||
this.confirmModalVisible = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -117,6 +123,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="gl-mb-3 deploy-keys">
|
||||
<confirm-modal :visible="confirmModalVisible" @remove="removeKey" @cancel="cancel" />
|
||||
<gl-loading-icon
|
||||
v-if="isLoading && !hasKeys"
|
||||
:label="s__('DeployKeys|Loading deploy keys')"
|
||||
|
|
@ -124,8 +131,12 @@ export default {
|
|||
/>
|
||||
<template v-else-if="hasKeys">
|
||||
<div class="top-area scrolling-tabs-container inner-page-scroll-tabs">
|
||||
<div class="fade-left"><gl-icon name="chevron-lg-left" :size="12" /></div>
|
||||
<div class="fade-right"><gl-icon name="chevron-lg-right" :size="12" /></div>
|
||||
<div class="fade-left">
|
||||
<gl-icon name="chevron-lg-left" :size="12" />
|
||||
</div>
|
||||
<div class="fade-right">
|
||||
<gl-icon name="chevron-lg-right" :size="12" />
|
||||
</div>
|
||||
|
||||
<navigation-tabs :tabs="tabs" scope="deployKeys" @onChangeTab="onChangeTab" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
<script>
|
||||
import { GlModal } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlModal,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
body: __(
|
||||
'Are you sure you want to remove this deploy key? If anything is still using this key, it will stop working.',
|
||||
),
|
||||
},
|
||||
modalOptions: {
|
||||
title: __('Do you want to remove this deploy key?'),
|
||||
actionPrimary: {
|
||||
text: __('Remove deploy key'),
|
||||
attributes: [{ variant: 'danger' }],
|
||||
},
|
||||
actionSecondary: {
|
||||
text: __('Cancel'),
|
||||
attributes: [{ category: 'tertiary' }],
|
||||
},
|
||||
static: true,
|
||||
modalId: 'confirm-remove-deploy-key',
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-modal
|
||||
v-bind="$options.modalOptions"
|
||||
:visible="visible"
|
||||
@primary="$emit('remove')"
|
||||
@secondary="$emit('cancel')"
|
||||
@hidden="$emit('cancel')"
|
||||
>
|
||||
{{ $options.i18n.body }}
|
||||
</gl-modal>
|
||||
</template>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { GlIcon, GlLink, GlTooltipDirective, GlButton } from '@gitlab/ui';
|
||||
import { head, tail } from 'lodash';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
|
|
@ -9,7 +9,9 @@ import actionBtn from './action_btn.vue';
|
|||
export default {
|
||||
components: {
|
||||
actionBtn,
|
||||
GlButton,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
|
@ -123,15 +125,15 @@ export default {
|
|||
<div role="rowheader" class="table-mobile-header">{{ s__('DeployKeys|Project usage') }}</div>
|
||||
<div class="table-mobile-content deploy-project-list">
|
||||
<template v-if="projects.length > 0">
|
||||
<a
|
||||
<gl-link
|
||||
v-gl-tooltip
|
||||
:title="projectTooltipTitle(firstProject)"
|
||||
class="label deploy-project-label"
|
||||
>
|
||||
<span> {{ firstProject.project.full_name }} </span>
|
||||
<gl-icon :name="firstProject.can_push ? 'lock-open' : 'lock'" />
|
||||
</a>
|
||||
<a
|
||||
</gl-link>
|
||||
<gl-link
|
||||
v-if="isExpandable"
|
||||
v-gl-tooltip
|
||||
:title="restProjectsTooltip"
|
||||
|
|
@ -139,8 +141,8 @@ export default {
|
|||
@click="toggleExpanded"
|
||||
>
|
||||
<span>{{ restProjectsLabel }}</span>
|
||||
</a>
|
||||
<a
|
||||
</gl-link>
|
||||
<gl-link
|
||||
v-for="deployKeysProject in restProjects"
|
||||
v-else-if="isExpanded"
|
||||
:key="deployKeysProject.project.full_path"
|
||||
|
|
@ -151,7 +153,7 @@ export default {
|
|||
>
|
||||
<span> {{ deployKeysProject.project.full_name }} </span>
|
||||
<gl-icon :name="deployKeysProject.can_push ? 'lock-open' : 'lock'" />
|
||||
</a>
|
||||
</gl-link>
|
||||
</template>
|
||||
<span v-else class="text-secondary">{{ __('None') }}</span>
|
||||
</div>
|
||||
|
|
@ -166,41 +168,43 @@ export default {
|
|||
</div>
|
||||
<div class="table-section section-15 table-button-footer deploy-key-actions">
|
||||
<div class="btn-group table-action-buttons">
|
||||
<action-btn v-if="!isEnabled" :deploy-key="deployKey" type="enable">
|
||||
<action-btn v-if="!isEnabled" :deploy-key="deployKey" type="enable" category="secondary">
|
||||
{{ __('Enable') }}
|
||||
</action-btn>
|
||||
<a
|
||||
<gl-button
|
||||
v-if="deployKey.can_edit"
|
||||
v-gl-tooltip
|
||||
:href="editDeployKeyPath"
|
||||
:title="__('Edit')"
|
||||
class="btn btn-default text-secondary"
|
||||
:aria-label="__('Edit')"
|
||||
data-container="body"
|
||||
>
|
||||
<gl-icon name="pencil" />
|
||||
</a>
|
||||
icon="pencil"
|
||||
category="secondary"
|
||||
/>
|
||||
<action-btn
|
||||
v-if="isRemovable"
|
||||
v-gl-tooltip
|
||||
:deploy-key="deployKey"
|
||||
:title="__('Remove')"
|
||||
btn-css-class="btn-danger"
|
||||
:aria-label="__('Remove')"
|
||||
category="primary"
|
||||
variant="danger"
|
||||
icon="remove"
|
||||
type="remove"
|
||||
data-container="body"
|
||||
>
|
||||
<gl-icon name="remove" />
|
||||
</action-btn>
|
||||
/>
|
||||
<action-btn
|
||||
v-else-if="isEnabled"
|
||||
v-gl-tooltip
|
||||
:deploy-key="deployKey"
|
||||
:title="__('Disable')"
|
||||
btn-css-class="btn-warning"
|
||||
:aria-label="__('Disable')"
|
||||
type="disable"
|
||||
data-container="body"
|
||||
>
|
||||
<gl-icon name="cancel" />
|
||||
</action-btn>
|
||||
icon="cancel"
|
||||
category="primary"
|
||||
variant="danger"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { GlEmptyState } from '@gitlab/ui';
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
const ERROR_FETCHING_DATA_HEADER = __('Could not get the data properly');
|
||||
const ERROR_FETCHING_DATA_DESCRIPTION = __(
|
||||
export const ERROR_FETCHING_DATA_HEADER = __('Could not get the data properly');
|
||||
export const ERROR_FETCHING_DATA_DESCRIPTION = __(
|
||||
'Please try and refresh the page. If the problem persists please contact support.',
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,21 @@ export const fetchPolicies = {
|
|||
};
|
||||
|
||||
export default (resolvers = {}, config = {}) => {
|
||||
let uri = `${gon.relative_url_root || ''}/api/graphql`;
|
||||
const {
|
||||
assumeImmutableResults,
|
||||
baseUrl,
|
||||
batchMax = 10,
|
||||
cacheConfig,
|
||||
fetchPolicy = fetchPolicies.CACHE_FIRST,
|
||||
typeDefs,
|
||||
path = '/api/graphql',
|
||||
useGet = false,
|
||||
} = config;
|
||||
let uri = `${gon.relative_url_root || ''}${path}`;
|
||||
|
||||
if (config.baseUrl) {
|
||||
if (baseUrl) {
|
||||
// Prepend baseUrl and ensure that `///` are replaced with `/`
|
||||
uri = `${config.baseUrl}${uri}`.replace(/\/{3,}/g, '/');
|
||||
uri = `${baseUrl}${uri}`.replace(/\/{3,}/g, '/');
|
||||
}
|
||||
|
||||
const httpOptions = {
|
||||
|
|
@ -34,7 +44,7 @@ export default (resolvers = {}, config = {}) => {
|
|||
// We set to `same-origin` which is default value in modern browsers.
|
||||
// See https://github.com/whatwg/fetch/pull/585 for more information.
|
||||
credentials: 'same-origin',
|
||||
batchMax: config.batchMax || 10,
|
||||
batchMax,
|
||||
};
|
||||
|
||||
const requestCounterLink = new ApolloLink((operation, forward) => {
|
||||
|
|
@ -50,7 +60,7 @@ export default (resolvers = {}, config = {}) => {
|
|||
const uploadsLink = ApolloLink.split(
|
||||
(operation) => operation.getContext().hasUpload || operation.getContext().isSingleRequest,
|
||||
createUploadLink(httpOptions),
|
||||
config.useGet ? createHttpLink(httpOptions) : new BatchHttpLink(httpOptions),
|
||||
useGet ? createHttpLink(httpOptions) : new BatchHttpLink(httpOptions),
|
||||
);
|
||||
|
||||
const performanceBarLink = new ApolloLink((operation, forward) => {
|
||||
|
|
@ -74,7 +84,7 @@ export default (resolvers = {}, config = {}) => {
|
|||
});
|
||||
|
||||
return new ApolloClient({
|
||||
typeDefs: config.typeDefs,
|
||||
typeDefs,
|
||||
link: ApolloLink.from([
|
||||
requestCounterLink,
|
||||
performanceBarLink,
|
||||
|
|
@ -83,14 +93,14 @@ export default (resolvers = {}, config = {}) => {
|
|||
uploadsLink,
|
||||
]),
|
||||
cache: new InMemoryCache({
|
||||
...config.cacheConfig,
|
||||
freezeResults: config.assumeImmutableResults,
|
||||
...cacheConfig,
|
||||
freezeResults: assumeImmutableResults,
|
||||
}),
|
||||
resolvers,
|
||||
assumeImmutableResults: config.assumeImmutableResults,
|
||||
assumeImmutableResults,
|
||||
defaultOptions: {
|
||||
query: {
|
||||
fetchPolicy: config.fetchPolicy || fetchPolicies.CACHE_FIRST,
|
||||
fetchPolicy,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -134,6 +134,9 @@ export default {
|
|||
show-empty
|
||||
:tbody-tr-attr="tbodyTrAttr"
|
||||
>
|
||||
<template #head()="{ label }">
|
||||
{{ label }}
|
||||
</template>
|
||||
<template #cell(account)="{ item: member }">
|
||||
<members-table-cell #default="{ memberType, isCurrentUser }" :member="member">
|
||||
<member-avatar
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
<script>
|
||||
import {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlDropdownSectionHeader,
|
||||
GlSprintf,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
artifacts: __('Artifacts'),
|
||||
downloadArtifact: __('Download %{name} artifact'),
|
||||
artifactSectionHeader: __('Download artifacts'),
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlDropdownSectionHeader,
|
||||
GlSprintf,
|
||||
},
|
||||
props: {
|
||||
artifacts: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-dropdown
|
||||
v-gl-tooltip
|
||||
:title="$options.i18n.artifacts"
|
||||
:text="$options.i18n.artifacts"
|
||||
:aria-label="$options.i18n.artifacts"
|
||||
icon="ellipsis_v"
|
||||
data-testid="pipeline-multi-actions-dropdown"
|
||||
right
|
||||
lazy
|
||||
text-sr-only
|
||||
no-caret
|
||||
>
|
||||
<gl-dropdown-section-header>{{
|
||||
$options.i18n.artifactSectionHeader
|
||||
}}</gl-dropdown-section-header>
|
||||
|
||||
<gl-dropdown-item
|
||||
v-for="(artifact, i) in artifacts"
|
||||
:key="i"
|
||||
:href="artifact.path"
|
||||
rel="nofollow"
|
||||
download
|
||||
data-testid="artifact-item"
|
||||
>
|
||||
<gl-sprintf :message="$options.i18n.downloadArtifact">
|
||||
<template #name>{{ artifact.name }}</template>
|
||||
</gl-sprintf>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</template>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import eventHub from '../../event_hub';
|
||||
import PipelinesArtifactsComponent from './pipelines_artifacts.vue';
|
||||
import PipelineMultiActions from './pipeline_multi_actions.vue';
|
||||
import PipelinesManualActions from './pipelines_manual_actions.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -16,8 +16,8 @@ export default {
|
|||
},
|
||||
components: {
|
||||
GlButton,
|
||||
PipelineMultiActions,
|
||||
PipelinesManualActions,
|
||||
PipelinesArtifactsComponent,
|
||||
},
|
||||
props: {
|
||||
pipeline: {
|
||||
|
|
@ -80,11 +80,6 @@ export default {
|
|||
<div class="btn-group">
|
||||
<pipelines-manual-actions v-if="actions.length > 0" :actions="actions" />
|
||||
|
||||
<pipelines-artifacts-component
|
||||
v-if="pipeline.details.artifacts.length"
|
||||
:artifacts="pipeline.details.artifacts"
|
||||
/>
|
||||
|
||||
<gl-button
|
||||
v-if="pipeline.flags.retryable"
|
||||
v-gl-tooltip.hover
|
||||
|
|
@ -114,6 +109,11 @@ export default {
|
|||
class="js-pipelines-cancel-button"
|
||||
@click="handleCancelClick"
|
||||
/>
|
||||
|
||||
<pipeline-multi-actions
|
||||
v-if="pipeline.details.artifacts.length"
|
||||
:artifacts="pipeline.details.artifacts"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class Projects::BlameController < Projects::ApplicationController
|
|||
|
||||
environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
|
||||
environment_params[:find_latest] = true
|
||||
@environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
|
||||
@environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
|
||||
|
||||
@blame = Gitlab::Blame.new(@blob, @commit)
|
||||
@blame = Gitlab::View::Presenter::Factory.new(@blame, project: @project, path: @path).fabricate!
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
def show_html
|
||||
environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
|
||||
environment_params[:find_latest] = true
|
||||
@environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
|
||||
@environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
|
||||
@last_commit = @repository.last_commit_for_path(@commit.id, @blob.path, literal_pathspec: true)
|
||||
@code_navigation_path = Gitlab::CodeNavigationPath.new(@project, @blob.commit_id).full_json_path_for(@blob.path)
|
||||
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
@diffs = commit.diffs(opts)
|
||||
@notes_count = commit.notes.count
|
||||
|
||||
@environment = Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, commit: @commit, find_latest: true).execute.last
|
||||
@environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, commit: @commit, find_latest: true).execute.last
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ class Projects::CompareController < Projects::ApplicationController
|
|||
if compare
|
||||
environment_params = source_project.repository.branch_exists?(head_ref) ? { ref: head_ref } : { commit: compare.commit }
|
||||
environment_params[:find_latest] = true
|
||||
@environment = Environments::EnvironmentsByDeploymentsFinder.new(source_project, current_user, environment_params).execute.last
|
||||
@environment = ::Environments::EnvironmentsByDeploymentsFinder.new(source_project, current_user, environment_params).execute.last
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,12 @@ module Types
|
|||
description: 'Indicates the job is active.'
|
||||
field :coverage, GraphQL::FLOAT_TYPE, null: true,
|
||||
description: 'Coverage level of the job.'
|
||||
field :created_by_tag, GraphQL::BOOLEAN_TYPE, null: false,
|
||||
description: 'Whether the job was created by a tag.'
|
||||
field :manual_job, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Whether the job has a manual action.'
|
||||
field :triggered, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Whether the job was triggered.'
|
||||
|
||||
def pipeline
|
||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find
|
||||
|
|
@ -123,6 +129,18 @@ module Types
|
|||
def coverage
|
||||
object&.coverage
|
||||
end
|
||||
|
||||
def created_by_tag
|
||||
object.tag?
|
||||
end
|
||||
|
||||
def manual_job
|
||||
object.try(:action?)
|
||||
end
|
||||
|
||||
def triggered
|
||||
object.try(:trigger_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -98,6 +98,14 @@ module AvatarsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def avatar_without_link(resource, options = {})
|
||||
if resource.is_a?(User)
|
||||
user_avatar_without_link(options.merge(user: resource))
|
||||
elsif resource.is_a?(Group)
|
||||
group_icon(resource, options.merge(class: 'avatar'))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def avatar_icon_by_user_email_or_gravatar(email, size, scale, only_path:)
|
||||
|
|
@ -136,9 +144,10 @@ module AvatarsHelper
|
|||
|
||||
def source_identicon(source, options = {})
|
||||
bg_key = (source.id % 7) + 1
|
||||
size_class = "s#{options[:size]}" if options[:size]
|
||||
|
||||
options[:class] =
|
||||
[*options[:class], "identicon bg#{bg_key}"].join(' ')
|
||||
[*options[:class], "identicon bg#{bg_key}", size_class].compact.join(' ')
|
||||
|
||||
content_tag(:div, class: options[:class].strip) do
|
||||
source.name[0, 1].upcase
|
||||
|
|
|
|||
|
|
@ -416,6 +416,14 @@ class Service < ApplicationRecord
|
|||
project_id.present?
|
||||
end
|
||||
|
||||
def group_level?
|
||||
group_id.present?
|
||||
end
|
||||
|
||||
def instance_level?
|
||||
instance?
|
||||
end
|
||||
|
||||
def parent
|
||||
project || group
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add generic avatar method for users and groups
|
||||
merge_request: 59758
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add group_level? and instance_level? helpers to Service model
|
||||
merge_request: 59838
|
||||
author: Amit Patel @amit.savani
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use GlModal for Confirmation of Deploy Key Delete
|
||||
merge_request: 59697
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Search projects by topic via API
|
||||
merge_request: 59900
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Upgrade Bootstrap to v4.5.3
|
||||
merge_request: 59501
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Move badge to vue component in project badges
|
||||
merge_request: 58045
|
||||
author: Yogi (@yo)
|
||||
type: changed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add missing data to CiJob type
|
||||
merge_request: 59805
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change artifacts download button to a vertical ellipsis menu
|
||||
merge_request: 59667
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -4,7 +4,7 @@ group: Project Management
|
|||
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
|
||||
---
|
||||
|
||||
# Incoming email
|
||||
# Incoming email **(FREE SELF)**
|
||||
|
||||
GitLab has several features based on receiving incoming emails:
|
||||
|
||||
|
|
|
|||
|
|
@ -7075,10 +7075,12 @@ Represents the total number of issues and their weights for a particular day.
|
|||
| <a id="cijobcommitpath"></a>`commitPath` | [`String`](#string) | Path to the commit that triggered the job. |
|
||||
| <a id="cijobcoverage"></a>`coverage` | [`Float`](#float) | Coverage level of the job. |
|
||||
| <a id="cijobcreatedat"></a>`createdAt` | [`Time!`](#time) | When the job was created. |
|
||||
| <a id="cijobcreatedbytag"></a>`createdByTag` | [`Boolean!`](#boolean) | Whether the job was created by a tag. |
|
||||
| <a id="cijobdetailedstatus"></a>`detailedStatus` | [`DetailedStatus`](#detailedstatus) | Detailed status of the job. |
|
||||
| <a id="cijobduration"></a>`duration` | [`Int`](#int) | Duration of the job in seconds. |
|
||||
| <a id="cijobfinishedat"></a>`finishedAt` | [`Time`](#time) | When a job has finished running. |
|
||||
| <a id="cijobid"></a>`id` | [`JobID`](#jobid) | ID of the job. |
|
||||
| <a id="cijobmanualjob"></a>`manualJob` | [`Boolean`](#boolean) | Whether the job has a manual action. |
|
||||
| <a id="cijobname"></a>`name` | [`String`](#string) | Name of the job. |
|
||||
| <a id="cijobneeds"></a>`needs` | [`CiBuildNeedConnection`](#cibuildneedconnection) | References to builds that must complete before the jobs run. |
|
||||
| <a id="cijobpipeline"></a>`pipeline` | [`Pipeline`](#pipeline) | Pipeline the job belongs to. |
|
||||
|
|
@ -7094,6 +7096,7 @@ Represents the total number of issues and their weights for a particular day.
|
|||
| <a id="cijobstartedat"></a>`startedAt` | [`Time`](#time) | When the job was started. |
|
||||
| <a id="cijobstatus"></a>`status` | [`CiJobStatus`](#cijobstatus) | Status of the job. |
|
||||
| <a id="cijobtags"></a>`tags` | [`[String!]`](#string) | Tags for the current job. |
|
||||
| <a id="cijobtriggered"></a>`triggered` | [`Boolean`](#boolean) | Whether the job was triggered. |
|
||||
|
||||
### `CiJobArtifact`
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ GET /projects
|
|||
| `sort` | string | **{dotted-circle}** No | Return projects sorted in `asc` or `desc` order. Default is `desc`. |
|
||||
| `starred` | boolean | **{dotted-circle}** No | Limit by projects starred by the current user. |
|
||||
| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. |
|
||||
| `topic` | string | **{dotted-circle}** No | Comma-separated topic names. Limit results to projects that match all of given topics. See `tag_list` attribute. |
|
||||
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
|
||||
| `wiki_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2). |
|
||||
| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
|
||||
|
|
|
|||
|
|
@ -118,6 +118,8 @@ To distinguish queries from mutations and fragments, the following naming conven
|
|||
- `add_user.mutation.graphql` for mutations;
|
||||
- `basic_user.fragment.graphql` for fragments.
|
||||
|
||||
If you are using queries for the [CustomersDot GraphQL endpoint](https://gitlab.com/gitlab-org/gitlab/-/blob/be78ccd832fd40315c5e63bb48ee1596ae146f56/app/controllers/customers_dot/proxy_controller.rb), end the filename with `.customer.query.graphql`, `.customer.mutation.graphql`, or `.customer.fragment.graphql`.
|
||||
|
||||
### Fragments
|
||||
|
||||
[Fragments](https://graphql.org/learn/queries/#fragments) are a way to make your complex GraphQL queries more readable and re-usable. Here is an example of GraphQL fragment:
|
||||
|
|
|
|||
|
|
@ -114,13 +114,13 @@ The current method provides several attributes that are sent on each click event
|
|||
|
||||
| category* | label | action | property** | value |
|
||||
|-------------|------------------|-----------------------|----------|:-----:|
|
||||
| [root:index] | main_navigation | click_navigation_link | `[link_label]` | - |
|
||||
| [groups:boards:show] | toggle_swimlanes | click_toggle_button | - | `[is_active]` |
|
||||
| [projects:registry:index] | registry_delete | click_button | - | - |
|
||||
| [projects:registry:index] | registry_delete | confirm_deletion | - | - |
|
||||
| [projects:blob:show] | congratulate_first_pipeline | click_button | `[human_access]` | - |
|
||||
| [projects:clusters:new] | chart_options | generate_link | `[chart_link]` | - |
|
||||
| [projects:clusters:new] | chart_options | click_add_label_button | `[label_id]` | - |
|
||||
| `[root:index]` | `main_navigation` | `click_navigation_link` | `[link_label]` | - |
|
||||
| `[groups:boards:show]` | `toggle_swimlanes` | `click_toggle_button` | - | `[is_active]` |
|
||||
| `[projects:registry:index]` | `registry_delete` | `click_button` | - | - |
|
||||
| `[projects:registry:index]` | `registry_delete` | `confirm_deletion` | - | - |
|
||||
| `[projects:blob:show]` | `congratulate_first_pipeline` | `click_button` | `[human_access]` | - |
|
||||
| `[projects:clusters:new]` | `chart_options` | `generate_link` | `[chart_link]` | - |
|
||||
| `[projects:clusters:new]` | `chart_options` | `click_add_label_button` | `[label_id]` | - |
|
||||
|
||||
_* It's ok to omit the category, and use the default._<br>
|
||||
_** Property is usually the best place for variable strings._
|
||||
|
|
|
|||
|
|
@ -913,9 +913,9 @@ On GitLab.com, the Product Intelligence team regularly monitors Usage Ping. They
|
|||
|
||||
To set up Usage Ping locally, you must:
|
||||
|
||||
1. [Set up local repositories]#(set-up-local-repositories)
|
||||
1. [Test local setup](#test-local-setup)
|
||||
1. (Optional) [Test Prometheus-based usage ping](#test-prometheus-based-usage-ping)
|
||||
1. [Set up local repositories](#set-up-local-repositories).
|
||||
1. [Test local setup](#test-local-setup).
|
||||
1. (Optional) [Test Prometheus-based usage ping](#test-prometheus-based-usage-ping).
|
||||
|
||||
#### Set up local repositories
|
||||
|
||||
|
|
|
|||
|
|
@ -167,22 +167,19 @@ difficult to track who is investigating and working on it. Assigning alerts ease
|
|||
|
||||
To assign an alert:
|
||||
|
||||
1. To display the list of current alerts, navigate to **Operations > Alerts**:
|
||||
1. To display the list of current alerts, navigate to **Operations > Alerts**.
|
||||
|
||||

|
||||
|
||||
1. Select your desired alert to display its **Alert Details View**:
|
||||
1. Select your desired alert to display its details.
|
||||
|
||||

|
||||
|
||||
1. If the right sidebar is not expanded, select
|
||||
**{angle-double-right}** **Expand sidebar** to expand it.
|
||||
|
||||
1. In the right sidebar, locate the **Assignee**, and then select **Edit**.
|
||||
From the dropdown menu, select each user you want to assign to the alert.
|
||||
GitLab creates a [to-do item](../../user/todos.md) for each user.
|
||||
|
||||

|
||||
|
||||
After completing their portion of investigating or fixing the alert, users can
|
||||
unassign themselves from the alert. To remove an assignee, select **Edit** next to the **Assignee** dropdown menu
|
||||
and deselect the user from the list of assignees, or select **Unassigned**.
|
||||
|
|
@ -203,8 +200,6 @@ add a to-do item:
|
|||
|
||||
Select the **To-Do List** **{todo-done}** in the navigation bar to view your current to-do list.
|
||||
|
||||

|
||||
|
||||
## Link runbooks to alerts
|
||||
|
||||
> Runbook URLs [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39315) in GitLab 13.3.
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.9 KiB |
|
|
@ -207,7 +207,7 @@ To use SSH with GitLab, copy your public key to your GitLab account.
|
|||
|
||||
1. Sign in to GitLab.
|
||||
1. In the top right corner, select your avatar.
|
||||
1. Select **Settings**.
|
||||
1. Select **Preferences**.
|
||||
1. From the left sidebar, select **SSH Keys**.
|
||||
1. In the **Key** box, paste the contents of your public key.
|
||||
If you manually copied the key, make sure you copy the entire key,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ You can tune the notifications you receive by combining your notification settin
|
|||
|
||||
To edit your notification settings:
|
||||
|
||||
1. Click on your profile picture and select **Settings**.
|
||||
1. Click on your profile picture and select **Preferences**.
|
||||
1. Click **Notifications** in the left sidebar.
|
||||
1. Edit the desired notification settings. Edited settings are automatically saved and enabled.
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ You can select a notification level for each project to help you closely monitor
|
|||
|
||||
To select a notification level for a project, use either of these methods:
|
||||
|
||||
1. Click on your profile picture and select **Settings**.
|
||||
1. Click on your profile picture and select **Preferences**.
|
||||
1. Click **Notifications** in the left sidebar.
|
||||
1. Locate the project in the **Projects** section.
|
||||
1. Select the desired [notification level](#notification-levels).
|
||||
|
|
@ -109,7 +109,7 @@ You can select a notification level and email address for each group.
|
|||
|
||||
To select a notification level for a group, use either of these methods:
|
||||
|
||||
1. Click on your profile picture and select **Settings**.
|
||||
1. Click on your profile picture and select **Preferences**.
|
||||
1. Click **Notifications** in the left sidebar.
|
||||
1. Locate the project in the **Groups** section.
|
||||
1. Select the desired [notification level](#notification-levels).
|
||||
|
|
@ -126,7 +126,7 @@ To select a notification level for a group, use either of these methods:
|
|||
|
||||
You can select an email address to receive notifications for each group you belong to. This could be useful, for example, if you work freelance, and want to keep email about clients' projects separate.
|
||||
|
||||
1. Click on your profile picture and select **Settings**.
|
||||
1. Click on your profile picture and select **Preferences**.
|
||||
1. Click **Notifications** in the left sidebar.
|
||||
1. Locate the project in the **Groups** section.
|
||||
1. Select the desired email address.
|
||||
|
|
|
|||
|
|
@ -151,6 +151,24 @@ Adding a public deploy key does not immediately expose any repository to it. Pub
|
|||
deploy keys enable access from other systems, but access is not given to any project
|
||||
until a project maintainer chooses to make use of it.
|
||||
|
||||
## How to disable deploy keys
|
||||
|
||||
[Project maintainers and owners](../../permissions.md#project-members-permissions)
|
||||
can remove or disable a deploy key for a project repository:
|
||||
|
||||
1. Navigate to the project's **Settings > Repository** page.
|
||||
1. Expand the **Deploy keys** section.
|
||||
1. Select the **{remove}** or **{cancel}** button.
|
||||
|
||||
NOTE:
|
||||
If anything relies on the removed deploy key, it will stop working once removed.
|
||||
|
||||
If the key is **publicly accessible**, it will be removed from the project, but still available under **Publicly accessible deploy keys**.
|
||||
|
||||
If the key is **privately accessible** and only in use by this project, it will deleted.
|
||||
|
||||
If the key is **privately accessible** and in use by other projects, it will be removed from the project, but still available under **Privately accesible deploy keys**.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Deploy key cannot push to a protected branch
|
||||
|
|
|
|||
|
|
@ -74,8 +74,9 @@ To visit the issue tracker for all projects in your group:
|
|||
1. Go to the group dashboard.
|
||||
1. In the left sidebar, select **Issues**.
|
||||
1. In the top-right, select the **Select project to create issue** button.
|
||||
1. Select the project you'd like to create an issue for. The button now appears as **New issue in <selected project>**.
|
||||
1. Select **New issue in <selected project>**.
|
||||
1. Select the project you'd like to create an issue for. The button now reflects the selected
|
||||
project.
|
||||
1. Select the button to create an issue in the selected project.
|
||||
|
||||

|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ group: Project Management
|
|||
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
|
||||
---
|
||||
|
||||
# Labels
|
||||
# Labels **(FREE)**
|
||||
|
||||
As your count of issues, merge requests, and epics grows in GitLab, it's more and more challenging
|
||||
to keep track of those items. Especially as your organization grows from just a few people to
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
mutation updatePlans($tags: [PlanTag!]) {
|
||||
plans(planTags: $tags) {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
query getPlans($tags: [PlanTag!]) {
|
||||
plans(planTags: $tags) {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
|
@ -592,18 +592,26 @@ module API
|
|||
|
||||
def project_finder_params_ce
|
||||
finder_params = project_finder_params_visibility_ce
|
||||
|
||||
finder_params.merge!(
|
||||
params
|
||||
.slice(:search,
|
||||
:custom_attributes,
|
||||
:last_activity_after,
|
||||
:last_activity_before,
|
||||
:repository_storage)
|
||||
.symbolize_keys
|
||||
.compact
|
||||
)
|
||||
|
||||
finder_params[:with_issues_enabled] = true if params[:with_issues_enabled].present?
|
||||
finder_params[:with_merge_requests_enabled] = true if params[:with_merge_requests_enabled].present?
|
||||
finder_params[:without_deleted] = true
|
||||
finder_params[:search] = params[:search] if params[:search]
|
||||
finder_params[:search_namespaces] = true if params[:search_namespaces].present?
|
||||
finder_params[:user] = params.delete(:user) if params[:user]
|
||||
finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
|
||||
finder_params[:id_after] = sanitize_id_param(params[:id_after]) if params[:id_after]
|
||||
finder_params[:id_before] = sanitize_id_param(params[:id_before]) if params[:id_before]
|
||||
finder_params[:last_activity_after] = params[:last_activity_after] if params[:last_activity_after]
|
||||
finder_params[:last_activity_before] = params[:last_activity_before] if params[:last_activity_before]
|
||||
finder_params[:repository_storage] = params[:repository_storage] if params[:repository_storage]
|
||||
finder_params[:tag] = params[:topic] if params[:topic].present?
|
||||
finder_params
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ module API
|
|||
optional :last_activity_after, type: DateTime, desc: 'Limit results to projects with last_activity after specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
|
||||
optional :last_activity_before, type: DateTime, desc: 'Limit results to projects with last_activity before specified time. Format: ISO 8601 YYYY-MM-DDTHH:MM:SSZ'
|
||||
optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins'
|
||||
optional :topic, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of topics. Limit results to projects having all topics'
|
||||
|
||||
use :optional_filter_params_ee
|
||||
end
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ module Gitlab
|
|||
definitions = []
|
||||
|
||||
::Find.find(root.to_s) do |path|
|
||||
definitions << Definition.new(path, fragments) if query?(path)
|
||||
definitions << Definition.new(path, fragments) if query_for_gitlab_schema?(path)
|
||||
end
|
||||
|
||||
definitions
|
||||
|
|
@ -288,10 +288,11 @@ module Gitlab
|
|||
@known_failures.fetch('filenames', []).any? { |known_failure| path.to_s.ends_with?(known_failure) }
|
||||
end
|
||||
|
||||
def self.query?(path)
|
||||
def self.query_for_gitlab_schema?(path)
|
||||
path.ends_with?('.graphql') &&
|
||||
!path.ends_with?('.fragment.graphql') &&
|
||||
!path.ends_with?('typedefs.graphql')
|
||||
!path.ends_with?('typedefs.graphql') &&
|
||||
!/.*\.customer\.(query|mutation)\.graphql$/.match?(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4265,6 +4265,9 @@ msgstr ""
|
|||
msgid "Are you sure you want to remove the license?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to remove this deploy key? If anything is still using this key, it will stop working."
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to remove this identity?"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -10816,9 +10819,6 @@ msgstr ""
|
|||
msgid "DeployKeys|Read access only"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployKeys|You are going to remove this deploy key. Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -11483,6 +11483,9 @@ msgstr ""
|
|||
msgid "Do not display offers from third parties within GitLab"
|
||||
msgstr ""
|
||||
|
||||
msgid "Do you want to remove this deploy key?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dockerfile"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -26466,6 +26469,9 @@ msgstr ""
|
|||
msgid "Remove child epic from an epic"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove deploy key"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove description history"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "1.189.0",
|
||||
"@gitlab/tributejs": "1.0.0",
|
||||
"@gitlab/ui": "29.7.3",
|
||||
"@gitlab/ui": "29.8.1",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-4",
|
||||
"@rails/ujs": "^6.0.3-4",
|
||||
|
|
@ -70,14 +70,14 @@
|
|||
"axios": "^0.20.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"bootstrap": "4.4.1",
|
||||
"bootstrap": "4.5.3",
|
||||
"cache-loader": "^4.1.0",
|
||||
"clipboard": "^1.7.1",
|
||||
"codemirror": "^5.48.4",
|
||||
"codesandbox-api": "0.0.23",
|
||||
"compression-webpack-plugin": "^5.0.2",
|
||||
"copy-webpack-plugin": "^5.1.2",
|
||||
"core-js": "^3.10.2",
|
||||
"core-js": "^3.11.0",
|
||||
"cron-validator": "^1.1.1",
|
||||
"cropper": "^2.3.0",
|
||||
"css-loader": "^2.1.1",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ RSpec.describe 'Project deploy keys', :js do
|
|||
page.within(find('.qa-deploy-keys-settings')) do
|
||||
expect(page).to have_selector('.deploy-key', count: 1)
|
||||
|
||||
accept_confirm { find('[data-testid="remove-icon"]').click }
|
||||
click_button 'Remove'
|
||||
click_button 'Remove deploy key'
|
||||
|
||||
wait_for_requests
|
||||
|
||||
|
|
|
|||
|
|
@ -457,20 +457,20 @@ RSpec.describe 'Pipelines', :js do
|
|||
visit_project_pipelines
|
||||
end
|
||||
|
||||
it 'has artifacts' do
|
||||
expect(page).to have_selector('.build-artifacts')
|
||||
it 'has artifacts dropdown' do
|
||||
expect(page).to have_selector('[data-testid="pipeline-multi-actions-dropdown"]')
|
||||
end
|
||||
|
||||
it 'has artifacts download dropdown' do
|
||||
find('.js-pipeline-dropdown-download').click
|
||||
find('[data-testid="pipeline-multi-actions-dropdown"]').click
|
||||
|
||||
expect(page).to have_link(with_artifacts.file_type)
|
||||
end
|
||||
|
||||
it 'has download attribute on download links' do
|
||||
find('.js-pipeline-dropdown-download').click
|
||||
find('[data-testid="pipeline-multi-actions-dropdown"]').click
|
||||
expect(page).to have_selector('a', text: 'Download')
|
||||
page.all('.build-artifacts a', text: 'Download').each do |link|
|
||||
page.all('[data-testid="artifact-item"]', text: 'Download').each do |link|
|
||||
expect(link[:download]).to eq ''
|
||||
end
|
||||
end
|
||||
|
|
@ -488,7 +488,7 @@ RSpec.describe 'Pipelines', :js do
|
|||
visit_project_pipelines
|
||||
end
|
||||
|
||||
it { expect(page).not_to have_selector('.build-artifacts') }
|
||||
it { expect(page).not_to have_selector('[data-testid="artifact-item"]') }
|
||||
end
|
||||
|
||||
context 'without artifacts' do
|
||||
|
|
@ -503,7 +503,7 @@ RSpec.describe 'Pipelines', :js do
|
|||
visit_project_pipelines
|
||||
end
|
||||
|
||||
it { expect(page).not_to have_selector('.build-artifacts') }
|
||||
it { expect(page).not_to have_selector('[data-testid="artifact-item"]') }
|
||||
end
|
||||
|
||||
context 'with trace artifact' do
|
||||
|
|
@ -514,7 +514,7 @@ RSpec.describe 'Pipelines', :js do
|
|||
end
|
||||
|
||||
it 'does not show trace artifact as artifacts' do
|
||||
expect(page).not_to have_selector('.build-artifacts')
|
||||
expect(page).not_to have_selector('[data-testid="artifact-item"]')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -117,7 +117,8 @@ RSpec.describe 'Projects > Settings > Repository settings' do
|
|||
project.deploy_keys << private_deploy_key
|
||||
visit project_settings_repository_path(project)
|
||||
|
||||
accept_confirm { find('.deploy-key', text: private_deploy_key.title).find('[data-testid="remove-icon"]').click }
|
||||
click_button 'Remove'
|
||||
click_button 'Remove deploy key'
|
||||
|
||||
expect(page).not_to have_content(private_deploy_key.title)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
import { createMockClient } from 'mock-apollo-client';
|
||||
import { createMockClient as createMockApolloClient } from 'mock-apollo-client';
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
||||
const defaultCacheOptions = {
|
||||
|
|
@ -7,13 +7,13 @@ const defaultCacheOptions = {
|
|||
addTypename: false,
|
||||
};
|
||||
|
||||
export default (handlers = [], resolvers = {}, cacheOptions = {}) => {
|
||||
export function createMockClient(handlers = [], resolvers = {}, cacheOptions = {}) {
|
||||
const cache = new InMemoryCache({
|
||||
...defaultCacheOptions,
|
||||
...cacheOptions,
|
||||
});
|
||||
|
||||
const mockClient = createMockClient({ cache, resolvers });
|
||||
const mockClient = createMockApolloClient({ cache, resolvers });
|
||||
|
||||
if (Array.isArray(handlers)) {
|
||||
handlers.forEach(([query, value]) => mockClient.setRequestHandler(query, value));
|
||||
|
|
@ -21,7 +21,12 @@ export default (handlers = [], resolvers = {}, cacheOptions = {}) => {
|
|||
throw new Error('You should pass an array of handlers to mock Apollo client');
|
||||
}
|
||||
|
||||
return mockClient;
|
||||
}
|
||||
|
||||
export default function createMockApollo(handlers, resolvers, cacheOptions) {
|
||||
const mockClient = createMockClient(handlers, resolvers, cacheOptions);
|
||||
const apolloProvider = new VueApollo({ defaultClient: mockClient });
|
||||
|
||||
return apolloProvider;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`content_editor/components/toolbar_button displays tertiary, small button with a provided label and icon 1`] = `
|
||||
"<b-button-stub event=\\"click\\" routertag=\\"a\\" size=\\"sm\\" variant=\\"default\\" type=\\"button\\" tag=\\"button\\" aria-label=\\"Bold\\" title=\\"Bold\\" class=\\"gl-mx-2 gl-button btn-default-tertiary btn-icon\\">
|
||||
"<b-button-stub size=\\"sm\\" variant=\\"default\\" type=\\"button\\" tag=\\"button\\" aria-label=\\"Bold\\" title=\\"Bold\\" class=\\"gl-mx-2 gl-button btn-default-tertiary btn-icon\\">
|
||||
<!---->
|
||||
<gl-icon-stub name=\\"bold\\" size=\\"16\\" class=\\"gl-button-icon\\"></gl-icon-stub>
|
||||
<!---->
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import actionBtn from '~/deploy_keys/components/action_btn.vue';
|
||||
import eventHub from '~/deploy_keys/eventhub';
|
||||
|
|
@ -8,13 +8,16 @@ describe('Deploy keys action btn', () => {
|
|||
const deployKey = data.enabled_keys[0];
|
||||
let wrapper;
|
||||
|
||||
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
|
||||
const findButton = () => wrapper.findComponent(GlButton);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallowMount(actionBtn, {
|
||||
propsData: {
|
||||
deployKey,
|
||||
type: 'enable',
|
||||
category: 'primary',
|
||||
variant: 'confirm',
|
||||
icon: 'edit',
|
||||
},
|
||||
slots: {
|
||||
default: 'Enable',
|
||||
|
|
@ -26,10 +29,18 @@ describe('Deploy keys action btn', () => {
|
|||
expect(wrapper.text()).toBe('Enable');
|
||||
});
|
||||
|
||||
it('passes the button props on', () => {
|
||||
expect(findButton().props()).toMatchObject({
|
||||
category: 'primary',
|
||||
variant: 'confirm',
|
||||
icon: 'edit',
|
||||
});
|
||||
});
|
||||
|
||||
it('sends eventHub event with btn type', () => {
|
||||
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
|
||||
|
||||
wrapper.trigger('click');
|
||||
findButton().vm.$emit('click');
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', deployKey, expect.anything());
|
||||
|
|
@ -37,18 +48,10 @@ describe('Deploy keys action btn', () => {
|
|||
});
|
||||
|
||||
it('shows loading spinner after click', () => {
|
||||
wrapper.trigger('click');
|
||||
findButton().vm.$emit('click');
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(findLoadingIcon().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('disables button after click', () => {
|
||||
wrapper.trigger('click');
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.attributes('disabled')).toBe('disabled');
|
||||
expect(findButton().props('loading')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { TEST_HOST } from 'spec/test_constants';
|
||||
import deployKeysApp from '~/deploy_keys/components/app.vue';
|
||||
import ConfirmModal from '~/deploy_keys/components/confirm_modal.vue';
|
||||
import eventHub from '~/deploy_keys/eventhub';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ describe('Deploy keys app component', () => {
|
|||
|
||||
const findLoadingIcon = () => wrapper.find('.gl-spinner');
|
||||
const findKeyPanels = () => wrapper.findAll('.deploy-keys .gl-tabs-nav li');
|
||||
const findModal = () => wrapper.findComponent(ConfirmModal);
|
||||
|
||||
it('renders loading icon while waiting for request', () => {
|
||||
mock.onGet(TEST_ENDPOINT).reply(() => new Promise());
|
||||
|
|
@ -94,11 +96,16 @@ describe('Deploy keys app component', () => {
|
|||
const key = data.public_keys[0];
|
||||
return mountComponent()
|
||||
.then(() => {
|
||||
jest.spyOn(window, 'confirm').mockReturnValue(true);
|
||||
jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
|
||||
jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve());
|
||||
|
||||
eventHub.$emit('disable.key', key);
|
||||
eventHub.$emit('disable.key', key, () => {});
|
||||
|
||||
return wrapper.vm.$nextTick();
|
||||
})
|
||||
.then(() => {
|
||||
expect(findModal().props('visible')).toBe(true);
|
||||
findModal().vm.$emit('remove');
|
||||
|
||||
return wrapper.vm.$nextTick();
|
||||
})
|
||||
|
|
@ -112,11 +119,16 @@ describe('Deploy keys app component', () => {
|
|||
const key = data.public_keys[0];
|
||||
return mountComponent()
|
||||
.then(() => {
|
||||
jest.spyOn(window, 'confirm').mockReturnValue(true);
|
||||
jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
|
||||
jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve());
|
||||
|
||||
eventHub.$emit('remove.key', key);
|
||||
eventHub.$emit('remove.key', key, () => {});
|
||||
|
||||
return wrapper.vm.$nextTick();
|
||||
})
|
||||
.then(() => {
|
||||
expect(findModal().props('visible')).toBe(true);
|
||||
findModal().vm.$emit('remove');
|
||||
|
||||
return wrapper.vm.$nextTick();
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
import { GlModal } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import ConfirmModal from '~/deploy_keys/components/confirm_modal.vue';
|
||||
|
||||
describe('~/deploy_keys/components/confirm_modal.vue', () => {
|
||||
let wrapper;
|
||||
let modal;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(ConfirmModal, { propsData: { modalId: 'test', visible: true } });
|
||||
modal = extendedWrapper(wrapper.findComponent(GlModal));
|
||||
});
|
||||
|
||||
it('emits a remove event if the primary button is clicked', () => {
|
||||
modal.findByText('Remove deploy key').trigger('click');
|
||||
expect(wrapper.emitted('remove')).toEqual([[]]);
|
||||
});
|
||||
|
||||
it('emits a cancel event if the secondary button is clicked', () => {
|
||||
modal.findByText('Cancel').trigger('click');
|
||||
expect(wrapper.emitted('cancel')).toEqual([[]]);
|
||||
});
|
||||
|
||||
it('displays the warning about removing the deploy key', () => {
|
||||
expect(modal.text()).toContain('Are you sure you want to remove this deploy key?');
|
||||
});
|
||||
});
|
||||
|
|
@ -24,7 +24,9 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
|
|||
role="columnheader"
|
||||
scope="col"
|
||||
>
|
||||
Jira display name
|
||||
<div>
|
||||
Jira display name
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
aria-colindex="2"
|
||||
|
|
@ -32,14 +34,18 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
|
|||
class=""
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
/>
|
||||
>
|
||||
<div />
|
||||
</th>
|
||||
<th
|
||||
aria-colindex="3"
|
||||
class=""
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
>
|
||||
GitLab username
|
||||
<div>
|
||||
GitLab username
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
import { GlDropdown, GlSprintf } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import PipelineMultiActions from '~/pipelines/components/pipelines_list/pipeline_multi_actions.vue';
|
||||
|
||||
describe('Pipeline Multi Actions Dropdown', () => {
|
||||
let wrapper;
|
||||
|
||||
const artifactItemTestId = 'artifact-item';
|
||||
|
||||
const defaultProps = {
|
||||
artifacts: [
|
||||
{
|
||||
name: 'job my-artifact',
|
||||
path: '/download/path',
|
||||
},
|
||||
{
|
||||
name: 'job-2 my-artifact-2',
|
||||
path: '/download/path-two',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const createComponent = (props = defaultProps) => {
|
||||
wrapper = extendedWrapper(
|
||||
shallowMount(PipelineMultiActions, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
stubs: {
|
||||
GlSprintf,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findAllArtifactItems = () => wrapper.findAllByTestId(artifactItemTestId);
|
||||
const findFirstArtifactItem = () => wrapper.findByTestId(artifactItemTestId);
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('should render the dropdown', () => {
|
||||
expect(findDropdown().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('Artifacts', () => {
|
||||
it('should render all the provided artifacts', () => {
|
||||
expect(findAllArtifactItems()).toHaveLength(defaultProps.artifacts.length);
|
||||
});
|
||||
|
||||
it('should render the correct artifact name and path', () => {
|
||||
expect(findFirstArtifactItem().attributes('href')).toBe(defaultProps.artifacts[0].path);
|
||||
|
||||
expect(findFirstArtifactItem().text()).toBe(
|
||||
`Download ${defaultProps.artifacts[0].name} artifact`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -15,10 +15,12 @@ RSpec.describe Types::Ci::JobType do
|
|||
commitPath
|
||||
coverage
|
||||
created_at
|
||||
created_by_tag
|
||||
detailedStatus
|
||||
duration
|
||||
finished_at
|
||||
id
|
||||
manual_job
|
||||
name
|
||||
needs
|
||||
pipeline
|
||||
|
|
@ -34,6 +36,7 @@ RSpec.describe Types::Ci::JobType do
|
|||
started_at
|
||||
status
|
||||
tags
|
||||
triggered
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
|
|
|
|||
|
|
@ -409,4 +409,33 @@ RSpec.describe AvatarsHelper do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#avatar_without_link' do
|
||||
let(:options) { { size: 32 } }
|
||||
|
||||
subject { helper.avatar_without_link(resource, options) }
|
||||
|
||||
context 'with users' do
|
||||
let(:resource) { user }
|
||||
|
||||
it 'displays user avatar' do
|
||||
is_expected.to eq tag(
|
||||
:img,
|
||||
alt: "#{user.name}'s avatar",
|
||||
src: avatar_icon_for_user(user, 32),
|
||||
data: { container: 'body' },
|
||||
class: 'avatar s32 has-tooltip',
|
||||
title: user.name
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with groups' do
|
||||
let(:resource) { build_stubbed(:group, name: 'foo') }
|
||||
|
||||
it 'displays group avatar' do
|
||||
is_expected.to match(%r{<div class="avatar identicon bg\d+ s32">F</div>})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'fast_spec_helper'
|
||||
require "test_prof/recipes/rspec/let_it_be"
|
||||
|
||||
|
|
@ -124,6 +125,18 @@ RSpec.describe Gitlab::Graphql::Queries do
|
|||
expect(described_class.find(path)).to be_empty
|
||||
end
|
||||
|
||||
it 'ignores customer.query.graphql' do
|
||||
path = root / 'plans.customer.query.graphql'
|
||||
|
||||
expect(described_class.find(path)).to be_empty
|
||||
end
|
||||
|
||||
it 'ignores customer.mutation.graphql' do
|
||||
path = root / 'plans.customer.mutation.graphql'
|
||||
|
||||
expect(described_class.find(path)).to be_empty
|
||||
end
|
||||
|
||||
it 'finds all query definitions under a root directory' do
|
||||
found = described_class.find(root)
|
||||
|
||||
|
|
@ -137,7 +150,9 @@ RSpec.describe Gitlab::Graphql::Queries do
|
|||
|
||||
expect(found).not_to include(
|
||||
definition_of(root / 'typedefs.graphql'),
|
||||
definition_of(root / 'author.fragment.graphql')
|
||||
definition_of(root / 'author.fragment.graphql'),
|
||||
definition_of(root / 'plans.customer.query.graphql'),
|
||||
definition_of(root / 'plans.customer.mutation.graphql')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -212,6 +212,26 @@ RSpec.describe Service do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#group_level?' do
|
||||
it 'is true when service has a group' do
|
||||
expect(build(:service, group: group)).to be_group_level
|
||||
end
|
||||
|
||||
it 'is false when service has no group' do
|
||||
expect(build(:service, group: nil)).not_to be_group_level
|
||||
end
|
||||
end
|
||||
|
||||
describe '#instance_level?' do
|
||||
it 'is true when service has instance-level integration' do
|
||||
expect(build(:service, :instance)).to be_instance_level
|
||||
end
|
||||
|
||||
it 'is false when service does not have instance-level integration' do
|
||||
expect(build(:service, instance: false)).not_to be_instance_level
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_or_initialize_non_project_specific_integration' do
|
||||
let!(:service1) { create(:jira_service, project_id: nil, group_id: group.id) }
|
||||
let!(:service2) { create(:jira_service) }
|
||||
|
|
|
|||
|
|
@ -223,6 +223,52 @@ RSpec.describe API::Projects do
|
|||
expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count')
|
||||
end
|
||||
|
||||
context 'filter by topic (column tag_list)' do
|
||||
before do
|
||||
project.update!(tag_list: %w(ruby javascript))
|
||||
end
|
||||
|
||||
it 'returns no projects' do
|
||||
get api('/projects', user), params: { topic: 'foo' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_empty
|
||||
end
|
||||
|
||||
it 'returns matching project for a single topic' do
|
||||
get api('/projects', user), params: { topic: 'ruby' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to contain_exactly a_hash_including('id' => project.id)
|
||||
end
|
||||
|
||||
it 'returns matching project for multiple topics' do
|
||||
get api('/projects', user), params: { topic: 'ruby, javascript' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to contain_exactly a_hash_including('id' => project.id)
|
||||
end
|
||||
|
||||
it 'returns no projects if project match only some topic' do
|
||||
get api('/projects', user), params: { topic: 'ruby, foo' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_empty
|
||||
end
|
||||
|
||||
it 'ignores topic if it is empty' do
|
||||
get api('/projects', user), params: { topic: '' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'and with_issues_enabled=true' do
|
||||
it 'only returns projects with issues enabled' do
|
||||
project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
|
||||
|
|
|
|||
36
yarn.lock
36
yarn.lock
|
|
@ -907,14 +907,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
||||
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
||||
|
||||
"@gitlab/ui@29.7.3":
|
||||
version "29.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.7.3.tgz#f7762cc6a20fc8f1c6403822f8cbef821f2438dd"
|
||||
integrity sha512-2pU7t+kFB4ndq3ZW8PzZ26LEj+vAd8AaJLvBmBDba1hEx5KlH4B9U30lGl+UYzgjY3yoe68eObK26vJjJ1rb0g==
|
||||
"@gitlab/ui@29.8.1":
|
||||
version "29.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-29.8.1.tgz#9bac442e0579e7eb5610a3e08c481bb272514559"
|
||||
integrity sha512-Pn1n4K2MR42YoQI+8i9Hs5ljtb2TQWhx1ectEsft/xMLS8TTm8okM553sM9Z7kf1coizFKILQOmz+E5RwcnOsQ==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
bootstrap-vue "2.14.0"
|
||||
bootstrap-vue "2.15.0"
|
||||
copy-to-clipboard "^3.0.8"
|
||||
dompurify "^2.2.7"
|
||||
echarts "^4.9.0"
|
||||
|
|
@ -2480,21 +2480,21 @@ bonjour@^3.5.0:
|
|||
multicast-dns "^6.0.1"
|
||||
multicast-dns-service-types "^1.1.0"
|
||||
|
||||
bootstrap-vue@2.14.0:
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.14.0.tgz#de88b607627431980b707e6f069f13ef8cc897bd"
|
||||
integrity sha512-sqbS7iHYCZEj/dDx4Yaze99HcX6bZjO4bSWZ0xSgJwtWQlbfB2VDJ9Qjzjp9XI8TT32wYNGAMpnXpYjQvv5qyQ==
|
||||
bootstrap-vue@2.15.0:
|
||||
version "2.15.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.15.0.tgz#0dfc12c054496c0f10efed510da1def41697cf3c"
|
||||
integrity sha512-ncxWkDG0mKFVot314wWKJELi+ESO7k6ngV//qvJFs9iVzlFI8Hx3rBVbpcPW2vrJ+0vitH8N2SOwn4fdQ3frMQ==
|
||||
dependencies:
|
||||
"@nuxt/opencollective" "^0.3.0"
|
||||
bootstrap ">=4.4.1 <5.0.0"
|
||||
bootstrap ">=4.5.0 <5.0.0"
|
||||
popper.js "^1.16.1"
|
||||
portal-vue "^2.1.7"
|
||||
vue-functional-data-merge "^3.1.0"
|
||||
|
||||
bootstrap@4.4.1, "bootstrap@>=4.4.1 <5.0.0":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.4.1.tgz#8582960eea0c5cd2bede84d8b0baf3789c3e8b01"
|
||||
integrity sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==
|
||||
bootstrap@4.5.3, "bootstrap@>=4.5.0 <5.0.0":
|
||||
version "4.5.3"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.3.tgz#c6a72b355aaf323920be800246a6e4ef30997fe6"
|
||||
integrity sha512-o9ppKQioXGqhw8Z7mah6KdTYpNQY//tipnkxppWhPbiSWdD+1raYsnhwEZjkTHYbGee4cVQ0Rx65EhOY/HNLcQ==
|
||||
|
||||
boxen@^4.2.0:
|
||||
version "4.2.0"
|
||||
|
|
@ -3387,10 +3387,10 @@ core-js-pure@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
||||
|
||||
core-js@^3.1.3, core-js@^3.10.2:
|
||||
version "3.10.2"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.2.tgz#17cb038ce084522a717d873b63f2b3ee532e2cd5"
|
||||
integrity sha512-W+2oVYeNghuBr3yTzZFQ5rfmjZtYB/Ubg87R5YOmlGrIb+Uw9f7qjUbhsj+/EkXhcV7eOD3jiM4+sgraX3FZUw==
|
||||
core-js@^3.1.3, core-js@^3.11.0:
|
||||
version "3.11.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.11.0.tgz#05dac6aa70c0a4ad842261f8957b961d36eb8926"
|
||||
integrity sha512-bd79DPpx+1Ilh9+30aT5O1sgpQd4Ttg8oqkqi51ZzhedMM1omD2e6IOF48Z/DzDCZ2svp49tN/3vneTK6ZBkXw==
|
||||
|
||||
core-js@~2.3.0:
|
||||
version "2.3.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue