Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
491c773c72
commit
55b5a8778c
|
|
@ -92,6 +92,51 @@
|
|||
/doc/user/search/advanced_global_search.md @marcia
|
||||
/doc/user/search/advanced_search_syntax.md @marcia
|
||||
/doc/user/search/index.md @marcia
|
||||
/doc/administration/file_hooks.md @marcia
|
||||
/doc/administration/git_annex.md @marcia
|
||||
/doc/administration/git_protocol.md @marcia
|
||||
/doc/administration/integration/plantuml.md @marcia
|
||||
/doc/administration/invalidate_markdown_cache.md @marcia
|
||||
/doc/administration/issue_closing_pattern.md @marcia
|
||||
/doc/administration/lfs/index.md @marcia
|
||||
/doc/administration/merge_request_diffs.md @marcia
|
||||
/doc/administration/repository_checks.md @marcia
|
||||
/doc/administration/snippets/index.md @marcia
|
||||
/doc/administration/static_objects_external_storage.md @marcia
|
||||
/doc/api/access_requests.md @marcia
|
||||
/doc/api/branches.md @marcia
|
||||
/doc/api/commits.md @marcia
|
||||
/doc/api/discussions.md @marcia
|
||||
/doc/api/group_wikis.md @marcia
|
||||
/doc/api/keys.md @marcia
|
||||
/doc/api/markdown.md @marcia
|
||||
/doc/api/merge_request_approvals.md @marcia
|
||||
/doc/api/merge_request_context_commits.md @marcia
|
||||
/doc/api/merge_requests.md @marcia
|
||||
/doc/api/project_aliases.md @marcia
|
||||
/doc/api/project_badges.md @marcia
|
||||
/doc/api/project_import_export.md @marcia
|
||||
/doc/api/project_level_variables.md @marcia
|
||||
/doc/api/project_snippets.md @marcia
|
||||
/doc/api/project_statistics.md @marcia
|
||||
/doc/api/project_templates.md @marcia
|
||||
/doc/api/project_vulnerabilities.md @marcia
|
||||
/doc/api/protected_branches.md @marcia
|
||||
/doc/api/protected_tags.md @marcia
|
||||
/doc/api/remote_mirrors.md @marcia
|
||||
/doc/api/repositories.md @marcia
|
||||
/doc/api/repository_files.md @marcia
|
||||
/doc/api/repository_submodules.md @marcia
|
||||
/doc/api/search.md @marcia
|
||||
/doc/api/snippets.md @marcia
|
||||
/doc/api/suggestions.md @marcia
|
||||
/doc/api/tags.md @marcia
|
||||
/doc/api/visual_review_discussions.md @marcia
|
||||
/doc/api/wikis.md @marcia
|
||||
/doc/user/admin_area/settings/account_and_limit_settings.md @marcia
|
||||
/doc/user/admin_area/settings/instance_template_repository.md @marcia
|
||||
/doc/user/admin_area/settings/push_event_activities_limit.md @marcia
|
||||
/doc/user/admin_area/settings/visibility_and_access_controls.md @marcia
|
||||
|
||||
[Frontend]
|
||||
*.scss @annabeldunstone @gitlab-org/maintainers/frontend
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
<script>
|
||||
import { GlDrawer, GlLabel, GlAvatarLink, GlAvatarLabeled, GlLink } from '@gitlab/ui';
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
import { __ } from '~/locale';
|
||||
import boardsStore from '~/boards/stores/boards_store';
|
||||
import eventHub from '~/sidebar/event_hub';
|
||||
import { isScopedLabel } from '~/lib/utils/common_utils';
|
||||
import { inactiveId } from '~/boards/constants';
|
||||
|
||||
// NOTE: need to revisit how we handle headerHeight, because we have so many different header and footer options.
|
||||
export default {
|
||||
headerHeight: process.env.NODE_ENV === 'development' ? '75px' : '40px',
|
||||
listSettingsText: __('List settings'),
|
||||
assignee: 'assignee',
|
||||
milestone: 'milestone',
|
||||
label: 'label',
|
||||
labelListText: __('Label'),
|
||||
labelMilestoneText: __('Milestone'),
|
||||
labelAssigneeText: __('Assignee'),
|
||||
components: {
|
||||
GlDrawer,
|
||||
GlLabel,
|
||||
GlAvatarLink,
|
||||
GlAvatarLabeled,
|
||||
GlLink,
|
||||
BoardSettingsSidebarWipLimit: () =>
|
||||
import('ee_component/boards/components/board_settings_wip_limit.vue'),
|
||||
},
|
||||
computed: {
|
||||
...mapState(['activeId']),
|
||||
activeList() {
|
||||
/*
|
||||
Warning: Though a computed property it is not reactive because we are
|
||||
referencing a List Model class. Reactivity only applies to plain JS objects
|
||||
*/
|
||||
return boardsStore.state.lists.find(({ id }) => id === this.activeId);
|
||||
},
|
||||
isSidebarOpen() {
|
||||
return this.activeId !== inactiveId;
|
||||
},
|
||||
activeListLabel() {
|
||||
return this.activeList.label;
|
||||
},
|
||||
activeListMilestone() {
|
||||
return this.activeList.milestone;
|
||||
},
|
||||
activeListAssignee() {
|
||||
return this.activeList.assignee;
|
||||
},
|
||||
boardListType() {
|
||||
return this.activeList.type || null;
|
||||
},
|
||||
listTypeTitle() {
|
||||
switch (this.boardListType) {
|
||||
case this.$options.milestone: {
|
||||
return this.$options.labelMilestoneText;
|
||||
}
|
||||
case this.$options.label: {
|
||||
return this.$options.labelListText;
|
||||
}
|
||||
case this.$options.assignee: {
|
||||
return this.$options.labelAssigneeText;
|
||||
}
|
||||
default: {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
eventHub.$on('sidebar.closeAll', this.closeSidebar);
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off('sidebar.closeAll', this.closeSidebar);
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setActiveId']),
|
||||
closeSidebar() {
|
||||
this.setActiveId(inactiveId);
|
||||
},
|
||||
showScopedLabels(label) {
|
||||
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-drawer
|
||||
class="js-board-settings-sidebar"
|
||||
:open="isSidebarOpen"
|
||||
:header-height="$options.headerHeight"
|
||||
@close="closeSidebar"
|
||||
>
|
||||
<template #header>{{ $options.listSettingsText }}</template>
|
||||
<template v-if="isSidebarOpen">
|
||||
<div class="d-flex flex-column align-items-start">
|
||||
<label class="js-list-label">{{ listTypeTitle }}</label>
|
||||
<template v-if="boardListType === $options.label">
|
||||
<gl-label
|
||||
:title="activeListLabel.title"
|
||||
:background-color="activeListLabel.color"
|
||||
:scoped="showScopedLabels(activeListLabel)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="boardListType === $options.assignee">
|
||||
<gl-avatar-link class="js-assignee" :href="activeListAssignee.webUrl">
|
||||
<gl-avatar-labeled
|
||||
:size="32"
|
||||
:label="activeListAssignee.name"
|
||||
:sub-label="`@${activeListAssignee.username}`"
|
||||
:src="activeListAssignee.avatar"
|
||||
/>
|
||||
</gl-avatar-link>
|
||||
</template>
|
||||
<template v-else-if="boardListType === $options.milestone">
|
||||
<gl-link class="js-milestone" :href="activeListMilestone.webUrl">
|
||||
{{ activeListMilestone.title }}
|
||||
</gl-link>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<board-settings-sidebar-wip-limit :max-issue-count="activeList.maxIssueCount" />
|
||||
</template>
|
||||
</gl-drawer>
|
||||
</template>
|
||||
|
|
@ -83,8 +83,7 @@ export default () => {
|
|||
Board: () => import('ee_else_ce/boards/components/board_column.vue'),
|
||||
BoardSidebar,
|
||||
BoardAddIssuesModal,
|
||||
BoardSettingsSidebar: () =>
|
||||
import('ee_component/boards/components/board_settings_sidebar.vue'),
|
||||
BoardSettingsSidebar: () => import('~/boards/components/board_settings_sidebar.vue'),
|
||||
},
|
||||
store,
|
||||
apolloProvider,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ export default {
|
|||
commit(types.SET_ENDPOINTS, endpoints);
|
||||
},
|
||||
|
||||
setActiveId({ commit }, id) {
|
||||
commit(types.SET_ACTIVE_ID, id);
|
||||
},
|
||||
|
||||
fetchLists: () => {
|
||||
notImplemented();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,3 +19,4 @@ export const RECEIVE_UPDATE_ISSUE_SUCCESS = 'RECEIVE_UPDATE_ISSUE_SUCCESS';
|
|||
export const RECEIVE_UPDATE_ISSUE_ERROR = 'RECEIVE_UPDATE_ISSUE_ERROR';
|
||||
export const SET_CURRENT_PAGE = 'SET_CURRENT_PAGE';
|
||||
export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE';
|
||||
export const SET_ACTIVE_ID = 'SET_ACTIVE_ID';
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ export default {
|
|||
state.endpoints = endpoints;
|
||||
},
|
||||
|
||||
[mutationTypes.SET_ACTIVE_ID](state, id) {
|
||||
state.activeId = id;
|
||||
},
|
||||
|
||||
[mutationTypes.REQUEST_ADD_LIST]: () => {
|
||||
notImplemented();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default {
|
|||
};
|
||||
},
|
||||
discussionParticipants() {
|
||||
return extractParticipants(this.issue.participants);
|
||||
return extractParticipants(this.issue.participants.nodes);
|
||||
},
|
||||
resolvedDiscussions() {
|
||||
return this.discussions.filter(discussion => discussion.resolved);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default {
|
|||
if (!this.queryVersion) return 0;
|
||||
|
||||
const idx = this.allVersions.findIndex(
|
||||
version => this.findVersionId(version.node.id) === this.queryVersion,
|
||||
version => this.findVersionId(version.id) === this.queryVersion,
|
||||
);
|
||||
|
||||
// if the currentVersionId isn't a valid version (i.e. not in allVersions)
|
||||
|
|
@ -29,7 +29,7 @@ export default {
|
|||
if (this.queryVersion) return this.queryVersion;
|
||||
|
||||
const currentVersion = this.allVersions[this.currentVersionIdx];
|
||||
return this.findVersionId(currentVersion.node.id);
|
||||
return this.findVersionId(currentVersion.id);
|
||||
},
|
||||
dropdownText() {
|
||||
if (this.isLatestVersion) {
|
||||
|
|
@ -51,23 +51,21 @@ export default {
|
|||
|
||||
<template>
|
||||
<gl-new-dropdown :text="dropdownText" size="small" class="design-version-dropdown">
|
||||
<gl-new-dropdown-item v-for="(version, index) in allVersions" :key="version.node.id">
|
||||
<gl-new-dropdown-item v-for="(version, index) in allVersions" :key="version.id">
|
||||
<router-link
|
||||
class="d-flex js-version-link"
|
||||
:to="{ path: $route.path, query: { version: findVersionId(version.node.id) } }"
|
||||
:to="{ path: $route.path, query: { version: findVersionId(version.id) } }"
|
||||
>
|
||||
<div class="flex-grow-1 ml-2">
|
||||
<div>
|
||||
<strong
|
||||
>{{ __('Version') }} {{ allVersions.length - index }}
|
||||
<span v-if="findVersionId(version.node.id) === latestVersionId"
|
||||
>({{ __('latest') }})</span
|
||||
>
|
||||
<span v-if="findVersionId(version.id) === latestVersionId">({{ __('latest') }})</span>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<i
|
||||
v-if="findVersionId(version.node.id) === currentVersionId"
|
||||
v-if="findVersionId(version.id) === currentVersionId"
|
||||
class="fa fa-check float-right gl-mr-2"
|
||||
></i>
|
||||
</router-link>
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@ mutation createImageDiffNote($input: CreateImageDiffNoteInput!) {
|
|||
id
|
||||
replyId
|
||||
notes {
|
||||
edges {
|
||||
node {
|
||||
...DesignNote
|
||||
}
|
||||
nodes {
|
||||
...DesignNote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ mutation uploadDesign($files: [Upload!]!, $projectPath: ID!, $iid: ID!) {
|
|||
designs {
|
||||
...DesignItem
|
||||
versions {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
sha
|
||||
}
|
||||
nodes {
|
||||
id
|
||||
sha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,15 @@ query getDesign($fullPath: ID!, $iid: String!, $atVersion: ID, $filenames: [Stri
|
|||
issue(iid: $iid) {
|
||||
designCollection {
|
||||
designs(atVersion: $atVersion, filenames: $filenames) {
|
||||
edges {
|
||||
node {
|
||||
...DesignItem
|
||||
issue {
|
||||
title
|
||||
webPath
|
||||
webUrl
|
||||
participants {
|
||||
edges {
|
||||
node {
|
||||
...Author
|
||||
}
|
||||
}
|
||||
nodes {
|
||||
...DesignItem
|
||||
issue {
|
||||
title
|
||||
webPath
|
||||
webUrl
|
||||
participants {
|
||||
nodes {
|
||||
...Author
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,17 +7,13 @@ query getDesignList($fullPath: ID!, $iid: String!, $atVersion: ID) {
|
|||
issue(iid: $iid) {
|
||||
designCollection {
|
||||
designs(atVersion: $atVersion) {
|
||||
edges {
|
||||
node {
|
||||
...DesignListItem
|
||||
}
|
||||
nodes {
|
||||
...DesignListItem
|
||||
}
|
||||
}
|
||||
versions {
|
||||
edges {
|
||||
node {
|
||||
...VersionListItem
|
||||
}
|
||||
nodes {
|
||||
...VersionListItem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { propertyOf } from 'lodash';
|
|||
import createFlash from '~/flash';
|
||||
import { s__ } from '~/locale';
|
||||
import getDesignListQuery from '../graphql/queries/get_design_list.query.graphql';
|
||||
import { extractNodes } from '../utils/design_management_utils';
|
||||
import allVersionsMixin from './all_versions';
|
||||
import { DESIGNS_ROUTE_NAME } from '../router/constants';
|
||||
|
||||
|
|
@ -19,9 +18,15 @@ export default {
|
|||
};
|
||||
},
|
||||
update: data => {
|
||||
const designEdges = propertyOf(data)(['project', 'issue', 'designCollection', 'designs']);
|
||||
if (designEdges) {
|
||||
return extractNodes(designEdges);
|
||||
const designNodes = propertyOf(data)([
|
||||
'project',
|
||||
'issue',
|
||||
'designCollection',
|
||||
'designs',
|
||||
'nodes',
|
||||
]);
|
||||
if (designNodes) {
|
||||
return designNodes;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default {
|
|||
atVersion: null,
|
||||
};
|
||||
},
|
||||
update: data => data.project.issue.designCollection.versions.edges,
|
||||
update: data => data.project.issue.designCollection.versions.nodes,
|
||||
},
|
||||
},
|
||||
inject: {
|
||||
|
|
@ -28,7 +28,7 @@ export default {
|
|||
return (
|
||||
this.$route.query.version &&
|
||||
this.allVersions &&
|
||||
this.allVersions.some(version => version.node.id.endsWith(this.$route.query.version))
|
||||
this.allVersions.some(version => version.id.endsWith(this.$route.query.version))
|
||||
);
|
||||
},
|
||||
designsVersion() {
|
||||
|
|
@ -38,7 +38,7 @@ export default {
|
|||
},
|
||||
latestVersionId() {
|
||||
const latestVersion = this.allVersions[0];
|
||||
return latestVersion && findVersionId(latestVersion.node.id);
|
||||
return latestVersion && findVersionId(latestVersion.id);
|
||||
},
|
||||
isLatestVersion() {
|
||||
if (this.allVersions.length > 0) {
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import {
|
|||
const deleteDesignsFromStore = (store, query, selectedDesigns) => {
|
||||
const data = store.readQuery(query);
|
||||
|
||||
const changedDesigns = data.project.issue.designCollection.designs.edges.filter(
|
||||
({ node }) => !selectedDesigns.includes(node.filename),
|
||||
const changedDesigns = data.project.issue.designCollection.designs.nodes.filter(
|
||||
node => !selectedDesigns.includes(node.filename),
|
||||
);
|
||||
data.project.issue.designCollection.designs.edges = [...changedDesigns];
|
||||
data.project.issue.designCollection.designs.nodes = [...changedDesigns];
|
||||
|
||||
store.writeQuery({
|
||||
...query,
|
||||
|
|
@ -34,11 +34,10 @@ const addNewVersionToStore = (store, query, version) => {
|
|||
if (!version) return;
|
||||
|
||||
const data = store.readQuery(query);
|
||||
const newEdge = { node: version, __typename: 'DesignVersionEdge' };
|
||||
|
||||
data.project.issue.designCollection.versions.edges = [
|
||||
newEdge,
|
||||
...data.project.issue.designCollection.versions.edges,
|
||||
data.project.issue.designCollection.versions.nodes = [
|
||||
version,
|
||||
...data.project.issue.designCollection.versions.nodes,
|
||||
];
|
||||
|
||||
store.writeQuery({
|
||||
|
|
@ -59,18 +58,15 @@ const addDiscussionCommentToStore = (store, createNote, query, queryVariables, d
|
|||
|
||||
design.notesCount += 1;
|
||||
if (
|
||||
!design.issue.participants.edges.some(
|
||||
participant => participant.node.username === createNote.note.author.username,
|
||||
!design.issue.participants.nodes.some(
|
||||
participant => participant.username === createNote.note.author.username,
|
||||
)
|
||||
) {
|
||||
design.issue.participants.edges = [
|
||||
...design.issue.participants.edges,
|
||||
design.issue.participants.nodes = [
|
||||
...design.issue.participants.nodes,
|
||||
{
|
||||
__typename: 'UserEdge',
|
||||
node: {
|
||||
__typename: 'User',
|
||||
...createNote.note.author,
|
||||
},
|
||||
__typename: 'User',
|
||||
...createNote.note.author,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -108,18 +104,15 @@ const addImageDiffNoteToStore = (store, createImageDiffNote, query, variables) =
|
|||
const notesCount = design.notesCount + 1;
|
||||
design.discussions.nodes = [...design.discussions.nodes, newDiscussion];
|
||||
if (
|
||||
!design.issue.participants.edges.some(
|
||||
participant => participant.node.username === createImageDiffNote.note.author.username,
|
||||
!design.issue.participants.nodes.some(
|
||||
participant => participant.username === createImageDiffNote.note.author.username,
|
||||
)
|
||||
) {
|
||||
design.issue.participants.edges = [
|
||||
...design.issue.participants.edges,
|
||||
design.issue.participants.nodes = [
|
||||
...design.issue.participants.nodes,
|
||||
{
|
||||
__typename: 'UserEdge',
|
||||
node: {
|
||||
__typename: 'User',
|
||||
...createImageDiffNote.note.author,
|
||||
},
|
||||
__typename: 'User',
|
||||
...createImageDiffNote.note.author,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -166,9 +159,9 @@ const updateImageDiffNoteInStore = (store, updateImageDiffNote, query, variables
|
|||
const addNewDesignToStore = (store, designManagementUpload, query) => {
|
||||
const data = store.readQuery(query);
|
||||
|
||||
const newDesigns = data.project.issue.designCollection.designs.edges.reduce((acc, design) => {
|
||||
if (!acc.find(d => d.filename === design.node.filename)) {
|
||||
acc.push(design.node);
|
||||
const newDesigns = data.project.issue.designCollection.designs.nodes.reduce((acc, design) => {
|
||||
if (!acc.find(d => d.filename === design.filename)) {
|
||||
acc.push(design);
|
||||
}
|
||||
|
||||
return acc;
|
||||
|
|
@ -178,30 +171,27 @@ const addNewDesignToStore = (store, designManagementUpload, query) => {
|
|||
const findNewVersions = designManagementUpload.designs.find(design => design.versions);
|
||||
|
||||
if (findNewVersions) {
|
||||
const findNewVersionsEdges = findNewVersions.versions.edges;
|
||||
const findNewVersionsNodes = findNewVersions.versions.nodes;
|
||||
|
||||
if (findNewVersionsEdges && findNewVersionsEdges.length) {
|
||||
newVersionNode = [findNewVersionsEdges[0]];
|
||||
if (findNewVersionsNodes && findNewVersionsNodes.length) {
|
||||
newVersionNode = [findNewVersionsNodes[0]];
|
||||
}
|
||||
}
|
||||
|
||||
const newVersions = [
|
||||
...(newVersionNode || []),
|
||||
...data.project.issue.designCollection.versions.edges,
|
||||
...data.project.issue.designCollection.versions.nodes,
|
||||
];
|
||||
|
||||
const updatedDesigns = {
|
||||
__typename: 'DesignCollection',
|
||||
designs: {
|
||||
__typename: 'DesignConnection',
|
||||
edges: newDesigns.map(design => ({
|
||||
__typename: 'DesignEdge',
|
||||
node: design,
|
||||
})),
|
||||
nodes: newDesigns,
|
||||
},
|
||||
versions: {
|
||||
__typename: 'DesignVersionConnection',
|
||||
edges: newVersions,
|
||||
nodes: newVersions,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,17 +5,7 @@ export const isValidDesignFile = ({ type }) =>
|
|||
(type.match(VALID_DESIGN_FILE_MIMETYPE.regex) || []).length > 0;
|
||||
|
||||
/**
|
||||
* Returns formatted array that doesn't contain
|
||||
* `edges`->`node` nesting
|
||||
*
|
||||
* @param {Array} elements
|
||||
*/
|
||||
|
||||
export const extractNodes = elements => elements.edges.map(({ node }) => node);
|
||||
|
||||
/**
|
||||
* Returns formatted array of discussions that doesn't contain
|
||||
* `edges`->`node` nesting for child notes
|
||||
* Returns formatted array of discussions
|
||||
*
|
||||
* @param {Array} discussions
|
||||
*/
|
||||
|
|
@ -40,9 +30,9 @@ export const findVersionId = id => (id.match('::Version/(.+$)') || [])[1];
|
|||
|
||||
export const findNoteId = id => (id.match('DiffNote/(.+$)') || [])[1];
|
||||
|
||||
export const extractDesigns = data => data.project.issue.designCollection.designs.edges;
|
||||
export const extractDesigns = data => data.project.issue.designCollection.designs.nodes;
|
||||
|
||||
export const extractDesign = data => (extractDesigns(data) || [])[0]?.node;
|
||||
export const extractDesign = data => (extractDesigns(data) || [])[0];
|
||||
|
||||
/**
|
||||
* Generates optimistic response for a design upload mutation
|
||||
|
|
@ -72,13 +62,10 @@ export const designUploadOptimisticResponse = files => {
|
|||
},
|
||||
versions: {
|
||||
__typename: 'DesignVersionConnection',
|
||||
edges: {
|
||||
__typename: 'DesignVersionEdge',
|
||||
node: {
|
||||
__typename: 'DesignVersion',
|
||||
id: -uniqueId(),
|
||||
sha: -uniqueId(),
|
||||
},
|
||||
nodes: {
|
||||
__typename: 'DesignVersion',
|
||||
id: -uniqueId(),
|
||||
sha: -uniqueId(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
|
@ -123,6 +110,6 @@ const normalizeAuthor = author => ({
|
|||
avatar_url: author.avatarUrl,
|
||||
});
|
||||
|
||||
export const extractParticipants = users => users.edges.map(({ node }) => normalizeAuthor(node));
|
||||
export const extractParticipants = users => users.map(node => normalizeAuthor(node));
|
||||
|
||||
export const getPageLayoutElement = () => document.querySelector('.layout-page');
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from '@gitlab/ui';
|
||||
import Tracking from '~/tracking';
|
||||
import PackageActivity from './activity.vue';
|
||||
import PackageHistory from './package_history.vue';
|
||||
import PackageInformation from './information.vue';
|
||||
import PackageTitle from './package_title.vue';
|
||||
import ConanInstallation from './conan_installation.vue';
|
||||
|
|
@ -57,6 +58,7 @@ export default {
|
|||
PackagesListLoader,
|
||||
PackageListRow,
|
||||
DependencyRow,
|
||||
PackageHistory,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
|
@ -66,6 +68,7 @@ export default {
|
|||
trackingActions: { ...TrackingActions },
|
||||
computed: {
|
||||
...mapState([
|
||||
'projectName',
|
||||
'packageEntity',
|
||||
'packageFiles',
|
||||
'isLoading',
|
||||
|
|
@ -74,6 +77,7 @@ export default {
|
|||
'svgPath',
|
||||
'npmPath',
|
||||
'npmHelpPath',
|
||||
'oneColumnView',
|
||||
]),
|
||||
installationComponent() {
|
||||
switch (this.packageEntity.package_type) {
|
||||
|
|
@ -219,29 +223,37 @@ export default {
|
|||
|
||||
<gl-tabs>
|
||||
<gl-tab :title="__('Detail')">
|
||||
<div class="row" data-qa-selector="package_information_content">
|
||||
<div class="col-sm-6">
|
||||
<package-information :information="packageInformation" />
|
||||
<package-information
|
||||
v-if="packageMetadata"
|
||||
:heading="packageMetadataTitle"
|
||||
:information="packageMetadata"
|
||||
:show-copy="true"
|
||||
/>
|
||||
<template v-if="!oneColumnView">
|
||||
<div
|
||||
class="row"
|
||||
data-qa-selector="package_information_content"
|
||||
data-testid="old-package-info"
|
||||
>
|
||||
<div class="col-sm-6">
|
||||
<package-information :information="packageInformation" />
|
||||
<package-information
|
||||
v-if="packageMetadata"
|
||||
:heading="packageMetadataTitle"
|
||||
:information="packageMetadata"
|
||||
:show-copy="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<component
|
||||
:is="installationComponent"
|
||||
v-if="installationComponent"
|
||||
:name="packageEntity.name"
|
||||
:registry-url="npmPath"
|
||||
:help-url="npmHelpPath"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<component
|
||||
:is="installationComponent"
|
||||
v-if="installationComponent"
|
||||
:name="packageEntity.name"
|
||||
:registry-url="npmPath"
|
||||
:help-url="npmHelpPath"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<package-activity />
|
||||
</template>
|
||||
|
||||
<package-activity />
|
||||
<package-history v-else :package-entity="packageEntity" :project-name="projectName" />
|
||||
|
||||
<h3 class="gl-font-lg">{{ __('Files') }}</h3>
|
||||
<gl-table
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<script>
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
|
||||
export default {
|
||||
name: 'HistoryElement',
|
||||
components: {
|
||||
GlIcon,
|
||||
TimelineEntryItem,
|
||||
},
|
||||
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<timeline-entry-item class="system-note note-wrapper gl-my-6!">
|
||||
<div class="timeline-icon">
|
||||
<gl-icon :name="icon" />
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="note-header">
|
||||
<span>
|
||||
<slot></slot>
|
||||
</span>
|
||||
</div>
|
||||
<div class="note-body"></div>
|
||||
</div>
|
||||
</timeline-entry-item>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
<script>
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import HistoryElement from './history_element.vue';
|
||||
|
||||
export default {
|
||||
name: 'PackageHistory',
|
||||
i18n: {
|
||||
createdOn: s__('PackageRegistry|%{name} version %{version} was created %{datetime}'),
|
||||
updatedAtText: s__('PackageRegistry|%{name} version %{version} was updated %{datetime}'),
|
||||
commitText: s__('PackageRegistry|Commit %{link} on branch %{branch}'),
|
||||
pipelineText: s__('PackageRegistry|Pipeline %{link} triggered %{datetime} by %{author}'),
|
||||
publishText: s__('PackageRegistry|Published to the %{project} Package Registry %{datetime}'),
|
||||
},
|
||||
components: {
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
HistoryElement,
|
||||
TimeAgoTooltip,
|
||||
},
|
||||
props: {
|
||||
packageEntity: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
projectName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDescription: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
packagePipeline() {
|
||||
return this.packageEntity.pipeline?.id ? this.packageEntity.pipeline : null;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="issuable-discussion">
|
||||
<h3 class="gl-ml-6" data-testid="title">{{ __('History') }}</h3>
|
||||
<ul class="timeline main-notes-list notes gl-my-4" data-testid="timeline">
|
||||
<history-element icon="clock" data-testid="created-on">
|
||||
<gl-sprintf :message="$options.i18n.createdOn">
|
||||
<template #name>
|
||||
<strong>{{ packageEntity.name }}</strong>
|
||||
</template>
|
||||
<template #version>
|
||||
<strong>{{ packageEntity.version }}</strong>
|
||||
</template>
|
||||
<template #datetime>
|
||||
<time-ago-tooltip :time="packageEntity.created_at" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
<history-element icon="pencil" data-testid="updated-at">
|
||||
<gl-sprintf :message="$options.i18n.updatedAtText">
|
||||
<template #name>
|
||||
<strong>{{ packageEntity.name }}</strong>
|
||||
</template>
|
||||
<template #version>
|
||||
<strong>{{ packageEntity.version }}</strong>
|
||||
</template>
|
||||
<template #datetime>
|
||||
<time-ago-tooltip :time="packageEntity.updated_at" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
<template v-if="packagePipeline">
|
||||
<history-element icon="commit" data-testid="commit">
|
||||
<gl-sprintf :message="$options.i18n.commitText">
|
||||
<template #link>
|
||||
<gl-link :href="`../../commit/${packagePipeline.sha}`">{{
|
||||
packagePipeline.sha
|
||||
}}</gl-link>
|
||||
</template>
|
||||
<template #branch>
|
||||
<strong>{{ packagePipeline.ref }}</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
<history-element icon="pipeline" data-testid="pipeline">
|
||||
<gl-sprintf :message="$options.i18n.pipelineText">
|
||||
<template #link>
|
||||
<gl-link :href="`../../pipelines/${packagePipeline.id}`"
|
||||
>#{{ packagePipeline.id }}</gl-link
|
||||
>
|
||||
</template>
|
||||
<template #datetime>
|
||||
<time-ago-tooltip :time="packagePipeline.created_at" />
|
||||
</template>
|
||||
<template #author>{{ packagePipeline.user.name }}</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
</template>
|
||||
<history-element icon="package" data-testid="published">
|
||||
<gl-sprintf :message="$options.i18n.publishText">
|
||||
<template #project>
|
||||
<strong>{{ projectName }}</strong>
|
||||
</template>
|
||||
<template #datetime>
|
||||
<time-ago-tooltip :time="packageEntity.created_at" />
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</history-element>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import PackagesApp from './components/app.vue';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
import createStore from './store';
|
||||
|
|
@ -7,7 +8,7 @@ Vue.use(Translate);
|
|||
|
||||
export default () => {
|
||||
const el = document.querySelector('#js-vue-packages-detail');
|
||||
const { package: packageJson, canDelete: canDeleteStr, ...rest } = el.dataset;
|
||||
const { package: packageJson, canDelete: canDeleteStr, oneColumnView, ...rest } = el.dataset;
|
||||
const packageEntity = JSON.parse(packageJson);
|
||||
const canDelete = canDeleteStr === 'true';
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ export default () => {
|
|||
packageEntity,
|
||||
packageFiles: packageEntity.package_files,
|
||||
canDelete,
|
||||
oneColumnView: parseBoolean(oneColumnView),
|
||||
...rest,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default {
|
|||
<div class="js-file-title file-title-flex-parent">
|
||||
<div class="file-header-content">
|
||||
<i aria-hidden="true" class="fa fa-file-text-o fa-fw"></i>
|
||||
<gl-link :href="blob.webUrl">
|
||||
<gl-link :href="blob.webPath">
|
||||
<strong>{{ blob.name }}</strong>
|
||||
</gl-link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) {
|
|||
titleHtml
|
||||
descriptionHtml
|
||||
message
|
||||
webUrl
|
||||
webPath
|
||||
authoredDate
|
||||
authorName
|
||||
authorGravatar
|
||||
author {
|
||||
name
|
||||
avatarUrl
|
||||
webUrl
|
||||
webPath
|
||||
}
|
||||
signatureHtml
|
||||
pipelines(ref: $ref, first: 1) {
|
||||
|
|
|
|||
|
|
@ -114,4 +114,15 @@ class SearchController < ApplicationController
|
|||
|
||||
Gitlab::UsageDataCounters::SearchCounter.count(:navbar_searches)
|
||||
end
|
||||
|
||||
def append_info_to_payload(payload)
|
||||
super
|
||||
|
||||
# Merging to :metadata will ensure these are logged as top level keys
|
||||
payload[:metadata] || {}
|
||||
payload[:metadata]['meta.search.group_id'] = params[:group_id]
|
||||
payload[:metadata]['meta.search.project_id'] = params[:project_id]
|
||||
payload[:metadata]['meta.search.search'] = params[:search]
|
||||
payload[:metadata]['meta.search.scope'] = params[:scope]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -524,8 +524,6 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
CI_REGISTRY_USER = 'gitlab-ci-token'
|
||||
|
||||
def persisted_variables
|
||||
Gitlab::Ci::Variables::Collection.new.tap do |variables|
|
||||
break variables unless persisted?
|
||||
|
|
@ -537,7 +535,7 @@ module Ci
|
|||
.append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false, masked: true)
|
||||
.append(key: 'CI_BUILD_ID', value: id.to_s)
|
||||
.append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false, masked: true)
|
||||
.append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER)
|
||||
.append(key: 'CI_REGISTRY_USER', value: ::Gitlab::Auth::CI_REGISTRY_USER)
|
||||
.append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false, masked: true)
|
||||
.append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false)
|
||||
.concat(deploy_token_variables)
|
||||
|
|
@ -596,7 +594,7 @@ module Ci
|
|||
def repo_url
|
||||
return unless token
|
||||
|
||||
auth = "gitlab-ci-token:#{token}@"
|
||||
auth = "#{::Gitlab::Auth::CI_JOB_USER}:#{token}@"
|
||||
project.http_url_to_repo.sub(%r{^https?://}) do |prefix|
|
||||
prefix + auth
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,4 +19,6 @@
|
|||
nuget_help_path: help_page_path('user/packages/nuget_repository/index'),
|
||||
pypi_path: pypi_registry_url(@project.id),
|
||||
pypi_setup_path: package_registry_project_url(@project.id, :pypi),
|
||||
pypi_help_path: help_page_path('user/packages/pypi_repository/index') } }
|
||||
pypi_help_path: help_page_path('user/packages/pypi_repository/index'),
|
||||
project_name: @project.name,
|
||||
one_column_view: Feature.enabled?(:packages_details_one_column, @project).to_s } }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Improve Elasticsearch Reindexing documentation
|
||||
merge_request: 29788
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove per-web-transaction redis metrics
|
||||
merge_request: 38101
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -147,7 +147,6 @@ if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && d
|
|||
Gitlab::Application.configure do |config|
|
||||
config.middleware.use(Gitlab::Metrics::RackMiddleware)
|
||||
config.middleware.use(Gitlab::Middleware::RailsQueueDuration)
|
||||
config.middleware.use(Gitlab::Metrics::RedisRackMiddleware)
|
||||
config.middleware.use(Gitlab::Metrics::ElasticsearchRackMiddleware)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
# File hooks
|
||||
|
||||
> - Introduced in GitLab 10.6.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, howto
|
||||
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/git_annex.html'
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
description: "Set and configure Git protocol v2"
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, howto
|
||||
---
|
||||
|
||||
# PlantUML & GitLab
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8537) in GitLab 8.16.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
# Invalidate Markdown Cache
|
||||
|
||||
For performance reasons, GitLab caches the HTML version of Markdown text
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, howto
|
||||
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/lfs/lfs_administration.html'
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Editor
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
# Merge request diffs storage **(CORE ONLY)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52568) in GitLab 11.8.
|
||||
|
|
|
|||
|
|
@ -97,8 +97,6 @@ The following metrics are available:
|
|||
| `gitlab_transaction_db_count_total` | Counter | 13.1 | Counter for total number of SQL calls | `controller`, `action` |
|
||||
| `gitlab_transaction_db_write_count_total` | Counter | 13.1 | Counter for total number of write SQL calls | `controller`, `action` |
|
||||
| `gitlab_transaction_db_cached_count_total` | Counter | 13.1 | Counter for total number of cached SQL calls | `controller`, `action` |
|
||||
| `http_redis_requests_duration_seconds` | Histogram | 13.1 | Redis requests duration during web transactions | `controller`, `action` |
|
||||
| `http_redis_requests_total` | Counter | 13.1 | Redis requests count during web transactions | `controller`, `action` |
|
||||
| `http_elasticsearch_requests_duration_seconds` **(STARTER)** | Histogram | 13.1 | Elasticsearch requests duration during web transactions | `controller`, `action` |
|
||||
| `http_elasticsearch_requests_total` **(STARTER)** | Counter | 13.1 | Elasticsearch requests count during web transactions | `controller`, `action` |
|
||||
| `pipelines_created_total` | Counter | 9.4 | Counter of pipelines created | |
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ This is why you will need:
|
|||
|
||||
When using default setup, minimum configuration requires:
|
||||
|
||||
- `CONSUL_USERNAME`. Defaults to `gitlab-consul`
|
||||
- `CONSUL_USERNAME`. The default user for Omnibus GitLab is `gitlab-consul`
|
||||
- `CONSUL_DATABASE_PASSWORD`. Password for the database user.
|
||||
- `CONSUL_PASSWORD_HASH`. This is a hash generated out of Consul username/password pair.
|
||||
Can be generated with:
|
||||
|
|
@ -140,7 +140,7 @@ server nodes.
|
|||
|
||||
We will need the following password information for the application's database user:
|
||||
|
||||
- `POSTGRESQL_USERNAME`. Defaults to `gitlab`
|
||||
- `POSTGRESQL_USERNAME`. The default user for Omnibus GitLab is `gitlab`
|
||||
- `POSTGRESQL_USER_PASSWORD`. The password for the database user
|
||||
- `POSTGRESQL_PASSWORD_HASH`. This is a hash generated out of the username/password pair.
|
||||
Can be generated with:
|
||||
|
|
@ -153,7 +153,7 @@ We will need the following password information for the application's database u
|
|||
|
||||
When using default setup, minimum configuration requires:
|
||||
|
||||
- `PGBOUNCER_USERNAME`. Defaults to `pgbouncer`
|
||||
- `PGBOUNCER_USERNAME`. The default user for Omnibus GitLab is `pgbouncer`
|
||||
- `PGBOUNCER_PASSWORD`. This is a password for PgBouncer service.
|
||||
- `PGBOUNCER_PASSWORD_HASH`. This is a hash generated out of PgBouncer username/password pair.
|
||||
Can be generated with:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Editor
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
# Repository checks
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/3232) in GitLab 8.7.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
type: reference, howto
|
||||
stage: Create
|
||||
group: Editor
|
||||
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/#designated-technical-writers
|
||||
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/#designated-technical-writers"
|
||||
---
|
||||
|
||||
# Snippets settings **(CORE ONLY)**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Editor
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
# Static objects external storage
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31025) in GitLab 12.3.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Group and project access requests API
|
||||
|
||||
> Introduced in GitLab 8.11.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Branches API
|
||||
|
||||
This API operates on [repository branches](../user/project/repository/branches/index.md).
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Commits API
|
||||
|
||||
## List repository commits
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
stage: Plan
|
||||
group: Project Management
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Discussions API
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Knowledge
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Wikis API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/212199) in GitLab 13.2.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Keys API
|
||||
|
||||
## Get SSH key with user by ID of an SSH key
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
stage: Plan
|
||||
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/#designated-technical-writers
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Markdown API
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Merge request approvals API **(STARTER)**
|
||||
|
||||
Configuration for approvals on all Merge Requests (MR) in the project. Must be authenticated for all endpoints.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Merge request context commits API
|
||||
|
||||
## List MR context commits
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Merge requests API
|
||||
|
||||
Every API call to merge requests must be authenticated.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project Aliases API **(PREMIUM ONLY)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3264) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.1.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project badges API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17082) in GitLab 10.6.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project import/export API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41899) in GitLab 10.6.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project-level Variables API
|
||||
|
||||
## List project variables
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Editor
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project snippets
|
||||
|
||||
## Snippet visibility level
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project statistics API
|
||||
|
||||
Every API call to [project](../user/project/index.md) statistics must be authenticated.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project templates API
|
||||
|
||||
This API is a project-specific version of these endpoints:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project Vulnerabilities API **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10242) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Protected branches API
|
||||
|
||||
> Introduced in GitLab 9.5.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Protected tags API
|
||||
|
||||
> Introduced in GitLab 11.3.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Project remote mirrors API
|
||||
|
||||
[Push mirrors](../user/project/repository/repository_mirroring.md#pushing-to-a-remote-repository-core)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Repositories API
|
||||
|
||||
## List repository tree
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Repository files API
|
||||
|
||||
**CRUD for repository files**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Repository submodules API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41213) in GitLab 11.5
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Search API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41763) in GitLab 10.5.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Editor
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Snippets API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6373) in GitLab 8.15.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Suggest Changes API
|
||||
|
||||
Every API call to suggestions must be authenticated.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Tags API
|
||||
|
||||
## List project repository tags
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Visual Review discussions API **(STARTER)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18710) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.5.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Knowledge
|
||||
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/#designated-technical-writers"
|
||||
type: reference, api
|
||||
---
|
||||
|
||||
# Wikis API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13372) in GitLab 10.0.
|
||||
|
|
|
|||
|
|
@ -121,6 +121,9 @@ Patterns:
|
|||
|
||||
## Zero downtime reindexing with multiple indices
|
||||
|
||||
NOTE: **Note:**
|
||||
This is not applicable yet as multiple indices functionality is not fully implemented.
|
||||
|
||||
Currently GitLab can only handle a single version of setting. Any setting/schema changes would require reindexing everything from scratch. Since reindexing can take a long time, this can cause search functionality downtime.
|
||||
|
||||
To avoid downtime, GitLab is working to support multiple indices that
|
||||
|
|
|
|||
|
|
@ -423,6 +423,140 @@ or creating [extra Sidekiq processes](../administration/operations/extra_sidekiq
|
|||
|
||||
For repository and snippet files, GitLab will only index up to 1 MiB of content, in order to avoid indexing timeouts.
|
||||
|
||||
## Zero downtime reindexing
|
||||
|
||||
The idea behind this reindexing method is to leverage Elasticsearch index alias feature to atomically swap between two indices.
|
||||
We will refer to each index as `primary` (online and used by GitLab for read/writes) and `secondary` (offline, for reindexing purpose).
|
||||
|
||||
Instead of connecting directly to the `primary` index, we'll setup an index alias such as we can change the underlying index at will.
|
||||
|
||||
NOTE: **Note:**
|
||||
Any index attached to the production alias is deemed a `primary` and will end up being used by the GitLab Elasticsearch integration.
|
||||
|
||||
### Pause the indexing
|
||||
|
||||
Under **Admin Area > Integration > Elasticsearch**, check the **Pause Elasticsearch Indexing** setting and save.
|
||||
|
||||
With this, all updates that should happen on your Elasticsearch index will be buffered and caught up once unpaused.
|
||||
|
||||
### Setup
|
||||
|
||||
TIP: **Tip:**
|
||||
If your index has been created with GitLab v13.0+ you can skip directly to [trigger the reindex](#trigger-the-reindex-via-the-elasticsearch-administration).
|
||||
|
||||
This process involves multiple shell commands and curl invocations, so a good initial setup will help down the road:
|
||||
|
||||
```shell
|
||||
# You can find this value under Admin Area > Integration > Elasticsearch > URL
|
||||
export CLUSTER_URL="http://localhost:9200"
|
||||
export PRIMARY_INDEX="gitlab-production"
|
||||
export SECONDARY_INDEX="gitlab-production-$(date +%s)"
|
||||
```
|
||||
|
||||
### Reclaiming the `gitlab-production` index name
|
||||
|
||||
CAUTION: **Caution:**
|
||||
It is highly recommended that you take a snapshot of your cluster to make sure there is a recovery path if anything goes wrong.
|
||||
|
||||
NOTE: **Note:**
|
||||
Due to a technical limitation, there will be a slight downtime because of the fact that we need to reclaim the current `primary` index to be used as the alias.
|
||||
|
||||
To reclaim the `gitlab-production` index name, you need to first create a `secondary` index and then trigger the re-index from `primary`.
|
||||
|
||||
#### Creating a secondary index
|
||||
|
||||
To create a secondary index, run the following Rake task. The `SKIP_ALIAS`
|
||||
environment variable will disable the automatic creation of the Elasticsearch
|
||||
alias, which would conflict with the existing index under `$PRIMARY_INDEX`:
|
||||
|
||||
```shell
|
||||
# Omnibus installation
|
||||
sudo SKIP_ALIAS=1 gitlab-rake "gitlab:elastic:create_empty_index[$SECONDARY_INDEX]"
|
||||
|
||||
# Source installation
|
||||
SKIP_ALIAS=1 bundle exec rake "gitlab:elastic:create_empty_index[$SECONDARY_INDEX]"
|
||||
```
|
||||
|
||||
The index should be created successfully, with the latest index options and mappings.
|
||||
|
||||
#### Trigger the re-index from `primary`
|
||||
|
||||
To trigger the re-index from `primary` index:
|
||||
|
||||
1. Use the Elasticsearch [Reindex API](https://www.elastic.co/guide/en/elasticsearch/reference/7.6/docs-reindex.html):
|
||||
|
||||
```shell
|
||||
curl --request POST \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{ \"source\": { \"index\": \"$PRIMARY_INDEX\" }, \"dest\": { \"index\": \"$SECONDARY_INDEX\" } }" \
|
||||
"$CLUSTER_URL/_reindex?slices=auto&wait_for_completion=false"
|
||||
```
|
||||
|
||||
There will be an output like:
|
||||
|
||||
```plaintext
|
||||
{"task":"3qw_Tr0YQLq7PF16Xek8YA:1012"}
|
||||
```
|
||||
|
||||
Note the `task` value here as it will be useful to follow the reindex progress.
|
||||
|
||||
1. Wait for the reindex process to complete, by checking the `completed` value.
|
||||
Using the `task` value form the previous step:
|
||||
|
||||
```shell
|
||||
export TASK_ID=3qw_Tr0YQLq7PF16Xek8YA:1012
|
||||
curl "$CLUSTER_URL/_tasks/$TASK_ID?pretty"
|
||||
```
|
||||
|
||||
The output will be like:
|
||||
|
||||
```plaintext
|
||||
{"completed":false, …}
|
||||
```
|
||||
|
||||
Once the returned value is `true`, you may continue to the next step.
|
||||
|
||||
1. Make sure that the secondary index has data in it. You can use the Elasticsearch
|
||||
API to look for the index size and compare our two indices:
|
||||
|
||||
```shell
|
||||
curl $CLUSTER_URL/$PRIMARY_INDEX/_count => 123123
|
||||
curl $CLUSTER_URL/$SECONDARY_INDEX/_count => 123123
|
||||
```
|
||||
|
||||
TIP: **Tip:**
|
||||
Comparing the document count is more accurate than using the index size, as improvements to the storage might cause the new index to be smaller than the original one.
|
||||
|
||||
1. Once you are confident your `secondary` index is valid, you can process to the creation of the alias.
|
||||
|
||||
```shell
|
||||
# Delete the original index
|
||||
curl --request DELETE $CLUSTER_URL/$PRIMARY_INDEX
|
||||
|
||||
# Create the alias and add the `secondary` index to it
|
||||
curl --request POST \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data "{\"actions\":[{\"add\":{\"index\":\"$SECONDARY_INDEX\",\"alias\":\"$PRIMARY_INDEX\"}}]}}" \
|
||||
$CLUSTER_URL/_aliases
|
||||
```
|
||||
|
||||
The reindexing is now completed. Your GitLab instance is now ready to use the [automated in-cluster reindexing](#trigger-the-reindex-via-the-elasticsearch-administration) feature for future reindexing.
|
||||
|
||||
1. Unpause the indexing
|
||||
|
||||
Under **Admin Area > Integration > Elasticsearch**, uncheck the **Pause Elasticsearch Indexing** setting and save.
|
||||
|
||||
### Trigger the reindex via the Elasticsearch administration
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34069) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.2.
|
||||
|
||||
Under **Admin Area > Integration > Elasticsearch zero-downtime reindexing**, click on **Trigger cluster reindexing**.
|
||||
|
||||
NOTE: **Note:**
|
||||
Reindexing can be a lengthy process depending on the size of your Elasticsearch cluster.
|
||||
|
||||
While the reindexing is running, you will be able to follow its progress under that same section.
|
||||
|
||||
## GitLab Elasticsearch Rake tasks
|
||||
|
||||
Rake tasks are available to:
|
||||
|
|
@ -586,7 +720,7 @@ Here are some common pitfalls and how to overcome them:
|
|||
|
||||
- **I indexed all the repositories but then switched Elasticsearch servers and now I can't find anything**
|
||||
|
||||
You will need to re-run all the Rake tasks to re-index the database, repositories, and wikis.
|
||||
You will need to re-run all the Rake tasks to reindex the database, repositories, and wikis.
|
||||
|
||||
- **The indexing process is taking a very long time**
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Gitaly
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
type: reference
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
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/#designated-technical-writers"
|
||||
type: reference
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ file path fragments to start seeing results.
|
|||
|
||||
## Syntax highlighting
|
||||
|
||||
> Support for `.gitlab.ci.yml` validation [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218472) in GitLab 13.2.
|
||||
> Support for `.gitlab-ci.yml` validation [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218472) in GitLab 13.2.
|
||||
|
||||
As expected from an IDE, syntax highlighting for many languages within
|
||||
the Web IDE will make your direct editing even easier.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ module Gitlab
|
|||
# Default scopes for OAuth applications that don't define their own
|
||||
DEFAULT_SCOPES = [:api].freeze
|
||||
|
||||
CI_JOB_USER = 'gitlab-ci-token'
|
||||
CI_REGISTRY_USER = 'gitlab-ci-token'
|
||||
|
||||
class << self
|
||||
prepend_if_ee('EE::Gitlab::Auth') # rubocop: disable Cop/InjectEnterpriseEditionModule
|
||||
|
||||
|
|
@ -126,7 +129,7 @@ module Gitlab
|
|||
# rubocop:enable Gitlab/RailsLogger
|
||||
|
||||
def skip_rate_limit?(login:)
|
||||
::Ci::Build::CI_REGISTRY_USER == login
|
||||
CI_REGISTRY_USER == login
|
||||
end
|
||||
|
||||
def look_to_limit_user(actor)
|
||||
|
|
@ -254,7 +257,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def build_access_token_check(login, password)
|
||||
return unless login == 'gitlab-ci-token'
|
||||
return unless login == CI_JOB_USER
|
||||
return unless password
|
||||
|
||||
build = find_build_by_token(password)
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ module Gitlab
|
|||
|
||||
login, password = user_name_and_password(current_request)
|
||||
return unless login.present? && password.present?
|
||||
return unless ::Ci::Build::CI_REGISTRY_USER == login
|
||||
return unless ::Gitlab::Auth::CI_REGISTRY_USER == login
|
||||
|
||||
job = ::Ci::Build.find_by_token(password)
|
||||
raise UnauthorizedError unless job
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Metrics
|
||||
# Rack middleware for tracking Redis metrics from Grape and Web requests.
|
||||
class RedisRackMiddleware
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
transaction = Gitlab::Metrics.current_transaction
|
||||
|
||||
@app.call(env)
|
||||
ensure
|
||||
record_metrics(transaction)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def record_metrics(transaction)
|
||||
query_time = Gitlab::Instrumentation::Redis.query_time
|
||||
request_count = Gitlab::Instrumentation::Redis.get_request_count
|
||||
|
||||
transaction.increment(:http_redis_requests_total, request_count) do
|
||||
docstring 'Amount of calls to Redis servers during web requests'
|
||||
end
|
||||
|
||||
transaction.observe(:http_redis_requests_duration_seconds, query_time) do
|
||||
docstring 'Query time for Redis servers during web requests'
|
||||
buckets Gitlab::Instrumentation::Redis::QUERY_TIME_BUCKETS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -16774,12 +16774,21 @@ msgstr ""
|
|||
msgid "Package was removed"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|%{name} version %{version} was created %{datetime}"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|%{name} version %{version} was updated %{datetime}"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Add Conan Remote"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Add NuGet Source"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Commit %{link} on branch %{branch}"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Composer"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -16897,6 +16906,12 @@ msgstr ""
|
|||
msgid "PackageRegistry|Pipeline %{linkStart}%{linkEnd} triggered %{timestamp} by %{author}"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Pipeline %{link} triggered %{datetime} by %{author}"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Published to the %{project} Package Registry %{datetime}"
|
||||
msgstr ""
|
||||
|
||||
msgid "PackageRegistry|Published to the repository at %{timestamp}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -216,4 +216,23 @@ RSpec.describe SearchController do
|
|||
it_behaves_like 'when the user cannot read cross project', :autocomplete, { term: 'hello' }
|
||||
it_behaves_like 'with external authorization service enabled', :autocomplete, { term: 'hello' }
|
||||
end
|
||||
|
||||
describe '#append_info_to_payload' do
|
||||
it 'appends search metadata for logging' do
|
||||
last_payload = nil
|
||||
original_append_info_to_payload = controller.method(:append_info_to_payload)
|
||||
|
||||
expect(controller).to receive(:append_info_to_payload) do |payload|
|
||||
original_append_info_to_payload.call(payload)
|
||||
last_payload = payload
|
||||
end
|
||||
|
||||
get :show, params: { scope: 'issues', search: 'hello world', group_id: '123', project_id: '456' }
|
||||
|
||||
expect(last_payload[:metadata]['meta.search.group_id']).to eq('123')
|
||||
expect(last_payload[:metadata]['meta.search.project_id']).to eq('456')
|
||||
expect(last_payload[:metadata]['meta.search.search']).to eq('hello world')
|
||||
expect(last_payload[:metadata]['meta.search.scope']).to eq('issues')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
import '~/boards/models/list';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from 'axios';
|
||||
import Vuex from 'vuex';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import { GlDrawer, GlLabel, GlAvatarLink, GlAvatarLabeled } from '@gitlab/ui';
|
||||
import BoardSettingsSidebar from '~/boards/components/board_settings_sidebar.vue';
|
||||
import boardsStore from '~/boards/stores/boards_store';
|
||||
import sidebarEventHub from '~/sidebar/event_hub';
|
||||
import { inactiveId } from '~/boards/constants';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
||||
localVue.use(Vuex);
|
||||
|
||||
describe('BoardSettingsSidebar', () => {
|
||||
let wrapper;
|
||||
let mock;
|
||||
let storeActions;
|
||||
const labelTitle = 'test';
|
||||
const labelColor = '#FFFF';
|
||||
const listId = 1;
|
||||
|
||||
const createComponent = (state = { activeId: inactiveId }, actions = {}) => {
|
||||
storeActions = actions;
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state,
|
||||
actions: storeActions,
|
||||
});
|
||||
|
||||
wrapper = shallowMount(BoardSettingsSidebar, {
|
||||
store,
|
||||
localVue,
|
||||
});
|
||||
};
|
||||
const findLabel = () => wrapper.find(GlLabel);
|
||||
const findDrawer = () => wrapper.find(GlDrawer);
|
||||
|
||||
beforeEach(() => {
|
||||
boardsStore.create();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('finds a GlDrawer component', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findDrawer().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('on close', () => {
|
||||
it('calls closeSidebar', async () => {
|
||||
const spy = jest.fn();
|
||||
createComponent({ activeId: inactiveId }, { setActiveId: spy });
|
||||
|
||||
findDrawer().vm.$emit('close');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(storeActions.setActiveId).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
inactiveId,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('calls closeSidebar on sidebar.closeAll event', async () => {
|
||||
createComponent({ activeId: inactiveId }, { setActiveId: jest.fn() });
|
||||
|
||||
sidebarEventHub.$emit('sidebar.closeAll');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(storeActions.setActiveId).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
inactiveId,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when activeId is zero', () => {
|
||||
it('renders GlDrawer with open false', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findDrawer().props('open')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when activeId is greater than zero', () => {
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
|
||||
boardsStore.addList({
|
||||
id: listId,
|
||||
label: { title: labelTitle, color: labelColor },
|
||||
list_type: 'label',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
boardsStore.removeList(listId);
|
||||
});
|
||||
|
||||
it('renders GlDrawer with open false', () => {
|
||||
createComponent({ activeId: 1 });
|
||||
|
||||
expect(findDrawer().props('open')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when activeId is in boardsStore', () => {
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
|
||||
boardsStore.addList({
|
||||
id: listId,
|
||||
label: { title: labelTitle, color: labelColor },
|
||||
list_type: 'label',
|
||||
});
|
||||
|
||||
createComponent({ activeId: listId });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
it('renders label title', () => {
|
||||
expect(findLabel().props('title')).toBe(labelTitle);
|
||||
});
|
||||
|
||||
it('renders label background color', () => {
|
||||
expect(findLabel().props('backgroundColor')).toBe(labelColor);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when activeId is not in boardsStore', () => {
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
|
||||
boardsStore.addList({ id: listId, label: { title: labelTitle, color: labelColor } });
|
||||
|
||||
createComponent({ activeId: inactiveId });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
it('does not render GlLabel', () => {
|
||||
expect(findLabel().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when activeList is present', () => {
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
boardsStore.removeList(listId);
|
||||
});
|
||||
|
||||
describe('when list type is "milestone"', () => {
|
||||
beforeEach(() => {
|
||||
boardsStore.addList({
|
||||
id: 1,
|
||||
milestone: {
|
||||
webUrl: 'https://gitlab.com/h5bp/html5-boilerplate/-/milestones/1',
|
||||
title: 'Backlog',
|
||||
},
|
||||
max_issue_count: 1,
|
||||
list_type: 'milestone',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
boardsStore.removeList(1, 'milestone');
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders the correct milestone text', () => {
|
||||
createComponent({ activeId: 1 });
|
||||
|
||||
expect(wrapper.find('.js-milestone').text()).toBe('Backlog');
|
||||
});
|
||||
|
||||
it('renders the correct list type text', () => {
|
||||
createComponent({ activeId: 1 });
|
||||
|
||||
expect(wrapper.find('.js-list-label').text()).toBe('Milestone');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when list type is "assignee"', () => {
|
||||
beforeEach(() => {
|
||||
boardsStore.addList({
|
||||
id: 1,
|
||||
user: { username: 'root', avatar: '', name: 'Test', webUrl: 'https://gitlab.com/root' },
|
||||
max_issue_count: 1,
|
||||
list_type: 'assignee',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
boardsStore.removeList(1, 'assignee');
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders gl-avatar-link with correct href', () => {
|
||||
createComponent({ activeId: 1 });
|
||||
|
||||
expect(wrapper.find(GlAvatarLink).exists()).toBe(true);
|
||||
expect(wrapper.find(GlAvatarLink).attributes('href')).toBe('https://gitlab.com/root');
|
||||
});
|
||||
|
||||
it('renders gl-avatar-labeled with "root" as username and name as "Test"', () => {
|
||||
createComponent({ activeId: 1 });
|
||||
|
||||
expect(wrapper.find(GlAvatarLabeled).exists()).toBe(true);
|
||||
expect(wrapper.find(GlAvatarLabeled).attributes('label')).toBe('Test');
|
||||
expect(wrapper.find(GlAvatarLabeled).attributes('sublabel')).toBe('@root');
|
||||
});
|
||||
|
||||
it('renders the correct list type text', () => {
|
||||
createComponent({ activeId: 1 });
|
||||
|
||||
expect(wrapper.find('.js-list-label').text()).toBe('Assignee');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import actions from '~/boards/stores/actions';
|
||||
import * as types from '~/boards/stores/mutation_types';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import { inactiveId } from '~/boards/constants';
|
||||
|
||||
const expectNotImplemented = action => {
|
||||
it('is not implemented', () => {
|
||||
|
|
@ -25,6 +26,23 @@ describe('setEndpoints', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('setActiveId', () => {
|
||||
it('should commit mutation SET_ACTIVE_ID', done => {
|
||||
const state = {
|
||||
activeId: inactiveId,
|
||||
};
|
||||
|
||||
testAction(
|
||||
actions.setActiveId,
|
||||
1,
|
||||
state,
|
||||
[{ type: types.SET_ACTIVE_ID, payload: 1 }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchLists', () => {
|
||||
expectNotImplemented(actions.fetchLists);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,6 +32,16 @@ describe('Board Store Mutations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('SET_ACTIVE_ID', () => {
|
||||
it('updates aciveListId to be the value that is passed', () => {
|
||||
const expectedId = 1;
|
||||
|
||||
mutations.SET_ACTIVE_ID(state, expectedId);
|
||||
|
||||
expect(state.activeId).toBe(expectedId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('REQUEST_ADD_LIST', () => {
|
||||
expectNotImplemented(mutations.REQUEST_ADD_LIST);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
export default [
|
||||
{
|
||||
node: {
|
||||
id: 'gid://gitlab/DesignManagement::Version/3',
|
||||
sha: '0945756378e0b1588b9dd40d5a6b99e8b7198f55',
|
||||
},
|
||||
id: 'gid://gitlab/DesignManagement::Version/3',
|
||||
sha: '0945756378e0b1588b9dd40d5a6b99e8b7198f55',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'gid://gitlab/DesignManagement::Version/2',
|
||||
sha: '5b063fef0cd7213b312db65b30e24f057df21b20',
|
||||
},
|
||||
id: 'gid://gitlab/DesignManagement::Version/2',
|
||||
sha: '5b063fef0cd7213b312db65b30e24f057df21b20',
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
export default [
|
||||
{
|
||||
node: {
|
||||
id: 'gid://gitlab/DesignManagement::Version/1',
|
||||
sha: 'b389071a06c153509e11da1f582005b316667001',
|
||||
},
|
||||
id: 'gid://gitlab/DesignManagement::Version/1',
|
||||
sha: 'b389071a06c153509e11da1f582005b316667001',
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -12,14 +12,12 @@ export default {
|
|||
webPath: 'full-issue-path',
|
||||
webUrl: 'full-issue-url',
|
||||
participants: {
|
||||
edges: [
|
||||
nodes: [
|
||||
{
|
||||
node: {
|
||||
name: 'Administrator',
|
||||
username: 'root',
|
||||
webUrl: 'link-to-author',
|
||||
avatarUrl: 'link-to-avatar',
|
||||
},
|
||||
name: 'Administrator',
|
||||
username: 'root',
|
||||
webUrl: 'link-to-author',
|
||||
avatarUrl: 'link-to-avatar',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,11 +5,7 @@ export default {
|
|||
issue: {
|
||||
designCollection: {
|
||||
designs: {
|
||||
edges: [
|
||||
{
|
||||
node: design,
|
||||
},
|
||||
],
|
||||
nodes: [design],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ export default {
|
|||
issue: {
|
||||
designCollection: {
|
||||
designs: {
|
||||
edges: [],
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -57,9 +57,7 @@ const mockDesigns = [
|
|||
];
|
||||
|
||||
const mockVersion = {
|
||||
node: {
|
||||
id: 'gid://gitlab/DesignManagement::Version/1',
|
||||
},
|
||||
id: 'gid://gitlab/DesignManagement::Version/1',
|
||||
};
|
||||
|
||||
describe('Design management index page', () => {
|
||||
|
|
@ -240,13 +238,10 @@ describe('Design management index page', () => {
|
|||
},
|
||||
versions: {
|
||||
__typename: 'DesignVersionConnection',
|
||||
edges: {
|
||||
__typename: 'DesignVersionEdge',
|
||||
node: {
|
||||
__typename: 'DesignVersion',
|
||||
id: expect.anything(),
|
||||
sha: expect.anything(),
|
||||
},
|
||||
nodes: {
|
||||
__typename: 'DesignVersion',
|
||||
id: expect.anything(),
|
||||
sha: expect.anything(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ describe('extractDiscussions', () => {
|
|||
};
|
||||
});
|
||||
|
||||
it('discards the edges.node artifacts of GraphQL', () => {
|
||||
it('discards the node artifacts of GraphQL', () => {
|
||||
expect(extractDiscussions(discussions)).toEqual([
|
||||
{ id: 1, notes: ['a'], index: 1 },
|
||||
{ id: 2, notes: ['b'], index: 2 },
|
||||
|
|
@ -96,10 +96,7 @@ describe('optimistic responses', () => {
|
|||
discussions: { __typename: 'DesignDiscussion', nodes: [] },
|
||||
versions: {
|
||||
__typename: 'DesignVersionConnection',
|
||||
edges: {
|
||||
__typename: 'DesignVersionEdge',
|
||||
node: { __typename: 'DesignVersion', id: -1, sha: -1 },
|
||||
},
|
||||
nodes: { __typename: 'DesignVersion', id: -1, sha: -1 },
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -62,14 +62,14 @@ exports[`PackageActivity render to match the default snapshot when there is a pi
|
|||
<!---->
|
||||
|
||||
<gl-link-stub
|
||||
href="../../commit/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
href="../../commit/sha-baz"
|
||||
>
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
sha-baz
|
||||
</gl-link-stub>
|
||||
|
||||
<clipboard-button-stub
|
||||
cssclass="border-0 text-secondary py-0"
|
||||
text="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
text="sha-baz"
|
||||
title="Copy commit SHA"
|
||||
tooltipplacement="top"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`History Element renders the correct markup 1`] = `
|
||||
<li
|
||||
class="timeline-entry system-note note-wrapper gl-my-6!"
|
||||
>
|
||||
<div
|
||||
class="timeline-entry-inner"
|
||||
>
|
||||
<div
|
||||
class="timeline-icon"
|
||||
>
|
||||
<gl-icon-stub
|
||||
name="pencil"
|
||||
size="16"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="timeline-content"
|
||||
>
|
||||
<div
|
||||
class="note-header"
|
||||
>
|
||||
<span>
|
||||
<div
|
||||
data-testid="default-slot"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="note-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
|
|
@ -16,6 +16,8 @@ import ConanInstallation from '~/packages/details/components/conan_installation.
|
|||
import NugetInstallation from '~/packages/details/components/nuget_installation.vue';
|
||||
import PypiInstallation from '~/packages/details/components/pypi_installation.vue';
|
||||
import DependencyRow from '~/packages/details/components/dependency_row.vue';
|
||||
import PackageHistory from '~/packages/details/components/package_history.vue';
|
||||
import PackageActivity from '~/packages/details/components/activity.vue';
|
||||
import {
|
||||
conanPackage,
|
||||
mavenPackage,
|
||||
|
|
@ -39,6 +41,7 @@ describe('PackagesApp', () => {
|
|||
packageEntity = mavenPackage,
|
||||
packageFiles = mavenFiles,
|
||||
isLoading = false,
|
||||
oneColumnView = false,
|
||||
} = {}) {
|
||||
store = new Vuex.Store({
|
||||
state: {
|
||||
|
|
@ -50,6 +53,8 @@ describe('PackagesApp', () => {
|
|||
emptySvgPath: 'empty-illustration',
|
||||
npmPath: 'foo',
|
||||
npmHelpPath: 'foo',
|
||||
projectName: 'bar',
|
||||
oneColumnView,
|
||||
},
|
||||
actions: {
|
||||
fetchPackageVersions,
|
||||
|
|
@ -93,6 +98,9 @@ describe('PackagesApp', () => {
|
|||
const dependenciesCountBadge = () => wrapper.find('[data-testid="dependencies-badge"]');
|
||||
const noDependenciesMessage = () => wrapper.find('[data-testid="no-dependencies-message"]');
|
||||
const dependencyRows = () => wrapper.findAll(DependencyRow);
|
||||
const findPackageHistory = () => wrapper.find(PackageHistory);
|
||||
const findPackageActivity = () => wrapper.find(PackageActivity);
|
||||
const findOldPackageInfo = () => wrapper.find('[data-testid="old-package-info"]');
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
|
|
@ -286,4 +294,31 @@ describe('PackagesApp', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('one column layout feature flag', () => {
|
||||
describe.each`
|
||||
oneColumnView | history | oldInfo | activity
|
||||
${true} | ${true} | ${false} | ${false}
|
||||
${false} | ${false} | ${true} | ${true}
|
||||
`(
|
||||
'with oneColumnView set to $oneColumnView',
|
||||
({ oneColumnView, history, oldInfo, activity }) => {
|
||||
beforeEach(() => {
|
||||
createComponent({ oneColumnView });
|
||||
});
|
||||
|
||||
it('package history', () => {
|
||||
expect(findPackageHistory().exists()).toBe(history);
|
||||
});
|
||||
|
||||
it('old info block', () => {
|
||||
expect(findOldPackageInfo().exists()).toBe(oldInfo);
|
||||
});
|
||||
|
||||
it('package activity', () => {
|
||||
expect(findPackageActivity().exists()).toBe(activity);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import component from '~/packages/details/components/history_element.vue';
|
||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
|
||||
describe('History Element', () => {
|
||||
let wrapper;
|
||||
const defaultProps = {
|
||||
icon: 'pencil',
|
||||
};
|
||||
|
||||
const mountComponent = () => {
|
||||
wrapper = shallowMount(component, {
|
||||
propsData: { ...defaultProps },
|
||||
stubs: {
|
||||
TimelineEntryItem,
|
||||
},
|
||||
slots: {
|
||||
default: '<div data-testid="default-slot"></div>',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
const findTimelineEntry = () => wrapper.find(TimelineEntryItem);
|
||||
const findGlIcon = () => wrapper.find(GlIcon);
|
||||
const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]');
|
||||
|
||||
it('renders the correct markup', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('has a default slot', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findDefaultSlot().exists()).toBe(true);
|
||||
});
|
||||
it('has a timeline entry', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findTimelineEntry().exists()).toBe(true);
|
||||
});
|
||||
it('has an icon', () => {
|
||||
mountComponent();
|
||||
|
||||
const icon = findGlIcon();
|
||||
|
||||
expect(icon.exists()).toBe(true);
|
||||
expect(icon.attributes('name')).toBe(defaultProps.icon);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import component from '~/packages/details/components/package_history.vue';
|
||||
|
||||
import { mavenPackage, mockPipelineInfo } from '../../mock_data';
|
||||
|
||||
describe('Package History', () => {
|
||||
let wrapper;
|
||||
const defaultProps = {
|
||||
projectName: 'baz project',
|
||||
packageEntity: { ...mavenPackage },
|
||||
};
|
||||
|
||||
const mountComponent = props => {
|
||||
wrapper = shallowMount(component, {
|
||||
propsData: { ...defaultProps, ...props },
|
||||
stubs: {
|
||||
HistoryElement: '<div data-testid="history-element"><slot></slot></div>',
|
||||
GlSprintf,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
const findHistoryElement = testId => wrapper.find(`[data-testid="${testId}"]`);
|
||||
const findElementLink = container => container.find(GlLink);
|
||||
const findElementTimeAgo = container => container.find(TimeAgoTooltip);
|
||||
const findTitle = () => wrapper.find('[data-testid="title"]');
|
||||
const findTimeline = () => wrapper.find('[data-testid="timeline"]');
|
||||
|
||||
it('has the correct title', () => {
|
||||
mountComponent();
|
||||
|
||||
const title = findTitle();
|
||||
|
||||
expect(title.exists()).toBe(true);
|
||||
expect(title.text()).toBe('History');
|
||||
});
|
||||
|
||||
it('has a timeline container', () => {
|
||||
mountComponent();
|
||||
|
||||
const title = findTimeline();
|
||||
|
||||
expect(title.exists()).toBe(true);
|
||||
expect(title.classes()).toEqual(
|
||||
expect.arrayContaining(['timeline', 'main-notes-list', 'notes']),
|
||||
);
|
||||
});
|
||||
|
||||
describe.each`
|
||||
name | icon | text | timeAgoTooltip | link
|
||||
${'created-on'} | ${'clock'} | ${'Test package version 1.0.0 was created'} | ${mavenPackage.created_at} | ${null}
|
||||
${'updated-at'} | ${'pencil'} | ${'Test package version 1.0.0 was updated'} | ${mavenPackage.updated_at} | ${null}
|
||||
${'commit'} | ${'commit'} | ${'Commit sha-baz on branch branch-name'} | ${null} | ${'../../commit/sha-baz'}
|
||||
${'pipeline'} | ${'pipeline'} | ${'Pipeline #1 triggered by foo'} | ${mockPipelineInfo.created_at} | ${'../../pipelines/1'}
|
||||
${'published'} | ${'package'} | ${'Published to the baz project Package Registry'} | ${mavenPackage.created_at} | ${null}
|
||||
`('history element $name', ({ name, icon, text, timeAgoTooltip, link }) => {
|
||||
let element;
|
||||
|
||||
beforeEach(() => {
|
||||
mountComponent({ packageEntity: { ...mavenPackage, pipeline: mockPipelineInfo } });
|
||||
element = findHistoryElement(name);
|
||||
});
|
||||
|
||||
it('has the correct icon', () => {
|
||||
expect(element.props('icon')).toBe(icon);
|
||||
});
|
||||
|
||||
it('has the correct text', () => {
|
||||
expect(element.text()).toBe(text);
|
||||
});
|
||||
|
||||
it('time-ago tooltip', () => {
|
||||
const timeAgo = findElementTimeAgo(element);
|
||||
const exist = Boolean(timeAgoTooltip);
|
||||
|
||||
expect(timeAgo.exists()).toBe(exist);
|
||||
if (exist) {
|
||||
expect(timeAgo.props('time')).toBe(timeAgoTooltip);
|
||||
}
|
||||
});
|
||||
|
||||
it('link', () => {
|
||||
const linkElement = findElementLink(element);
|
||||
const exist = Boolean(link);
|
||||
|
||||
expect(linkElement.exists()).toBe(exist);
|
||||
if (exist) {
|
||||
expect(linkElement.attributes('href')).toBe(link);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('when pipelineInfo is missing', () => {
|
||||
it.each(['commit', 'pipeline'])('%s history element is hidden', name => {
|
||||
mountComponent();
|
||||
expect(findHistoryElement(name).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -6,7 +6,7 @@ const _links = {
|
|||
export const mockPipelineInfo = {
|
||||
id: 1,
|
||||
ref: 'branch-name',
|
||||
sha: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
sha: 'sha-baz',
|
||||
user: {
|
||||
name: 'foo',
|
||||
},
|
||||
|
|
@ -14,6 +14,7 @@ export const mockPipelineInfo = {
|
|||
name: 'foo-project',
|
||||
web_url: 'foo-project-link',
|
||||
},
|
||||
created_at: '2015-12-10',
|
||||
};
|
||||
|
||||
export const mavenPackage = {
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ exports[`publish_method renders 1`] = `
|
|||
|
||||
<gl-link-stub
|
||||
class="mr-1"
|
||||
href="../commit/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
href="../commit/sha-baz"
|
||||
>
|
||||
xxxxxxxx
|
||||
sha-baz
|
||||
</gl-link-stub>
|
||||
|
||||
<clipboard-button-stub
|
||||
cssclass="border-0 text-secondary py-0 px-1"
|
||||
text="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
text="sha-baz"
|
||||
title="Copy commit SHA"
|
||||
tooltipplacement="top"
|
||||
/>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue