Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-06-09 12:11:14 +00:00
parent 8b50d36626
commit 3fd5856144
90 changed files with 985 additions and 14043 deletions

View File

@ -12,6 +12,7 @@
BROWSERSLIST_IGNORE_OLD_DATA: "true"
WEBPACK_COMPILE_LOG_PATH: "tmp/webpack-output.log"
stage: prepare
needs: []
script:
- yarn_install_script
- export GITLAB_ASSETS_HASH=$(bin/rake gitlab:assets:hash_sum)
@ -109,6 +110,7 @@ retrieve-frontend-fixtures:
- .default-retry
- .frontend:rules:default-frontend-jobs
stage: prepare
needs: []
script:
- source scripts/utils.sh
- source scripts/gitlab_component_helpers.sh

View File

@ -9,6 +9,7 @@ setup-test-env:
- .setup-test-env-cache
- .rails:rules:setup-test-env
stage: prepare
needs: []
variables:
SETUP_DB: "false"
script:

View File

@ -17,6 +17,7 @@ retrieve-tests-metadata:
# We use a smaller image for this job only (update-tests-metadata compiles some gems)
image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}-slim
stage: prepare
needs: []
script:
- apt-get update && apt-get install -y curl # Not present in ruby-slim, so we add it manually
- install_gitlab_gem

View File

@ -98,7 +98,7 @@ must be updated before this MR is merged:
If you want to double check that it worked, you can run `bin/rake gitlab:docs:check_deprecations`
to verify that the doc is up to date.
1. Commit the updated file and push the changes.
1. Set the MR to merge when the pipeline succeeds (or merge if the pipeline is already complete).
1. Set the merge request to auto-merge, or if the pipeline is already complete, merge.
If you have trouble running the Rake task, check the [troubleshooting steps](https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecation-rake-task-troubleshooting).

View File

@ -105,6 +105,6 @@ must be updated before this MR is merged:
If you want to double check that it worked, you can run `bin/rake gitlab:docs:check_removals`
to verify that the doc is up to date.
1. Commit the updated file and push the changes.
1. Set the MR to merge when the pipeline succeeds (or merge if the pipeline is already complete).
1. Set the merge request to auto-merge, or if the pipeline is already complete, merge.
If you have trouble running the rake task, check the [troubleshooting steps](https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecation-rake-task-troubleshooting).

View File

@ -1613,28 +1613,6 @@ Layout/ArgumentAlignment:
- 'qa/qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb'
- 'qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb'
- 'qa/qa/specs/features/ee/api/1_manage/integrations/group_webhook_events_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/change_vulnerability_status_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_1_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_2_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/instance/instance_audit_logs_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/policies_list_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerabilities_jira_integration_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/overage_modal_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_ci_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_storage_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/upgrade_group_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/user_registration_billing_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/saas_user_limit_experience_spec.rb'
- 'qa/qa/vendor/jira/jira_api.rb'
- 'qa/spec/support/loglinking_spec.rb'
- 'rubocop/cop/gitlab/finder_with_find_by.rb'

View File

@ -95,7 +95,7 @@ export default {
class="issues-details-filters filtered-search-block gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row row-content-block second-block"
>
<div
class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-flex-grow-1 gl-lg-mb-0 gl-mb-3 gl-w-full"
class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-flex-grow-1 gl-lg-mb-0 gl-mb-3 gl-w-full gl-min-w-0"
>
<boards-selector :board-apollo="board" @switchBoard="$emit('switchBoard', $event)" />
<new-board-button />

View File

@ -22,9 +22,10 @@ export default {
},
computed: {
isScopedLabel() {
return isScopedLabel({ title: this.node.attrs.originalText });
return isScopedLabel({ title: this.node.attrs.originalText || this.node.attrs.text });
},
},
fallbackLabelBackgroundColor: '#ccc',
};
</script>
<template>
@ -32,7 +33,7 @@ export default {
<gl-label
size="sm"
:scoped="isScopedLabel"
:background-color="node.attrs.color"
:background-color="node.attrs.color || $options.fallbackLabelBackgroundColor"
:title="node.attrs.text"
class="gl-pointer-events-none"
/>

View File

@ -40,7 +40,6 @@ export default Link.extend({
},
addAttributes() {
return {
...this.parent?.(),
uploading: {
default: false,
renderHTML: ({ uploading }) => (uploading ? { class: 'with-attachment-icon' } : {}),

View File

@ -1,5 +1,7 @@
import OrderedMap from 'orderedmap';
import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from '@tiptap/pm/state';
import { Schema, DOMParser as ProseMirrorDOMParser, DOMSerializer } from '@tiptap/pm/model';
import { __ } from '~/locale';
import { VARIANT_DANGER } from '~/alert';
import createMarkdownDeserializer from '../services/gl_api_markdown_deserializer';
@ -9,47 +11,55 @@ import Diagram from './diagram';
import Frontmatter from './frontmatter';
const TEXT_FORMAT = 'text/plain';
const GFM_FORMAT = 'text/x-gfm';
const HTML_FORMAT = 'text/html';
const VS_CODE_FORMAT = 'vscode-editor-data';
const CODE_BLOCK_NODE_TYPES = [CodeBlockHighlight.name, Diagram.name, Frontmatter.name];
function parseHTML(schema, html) {
const parser = new DOMParser();
const startTag = '<body>';
const endTag = '</body>';
const { body } = parser.parseFromString(startTag + html + endTag, 'text/html');
return { document: ProseMirrorDOMParser.fromSchema(schema).parse(body) };
}
export default Extension.create({
name: 'pasteMarkdown',
priority: EXTENSION_PRIORITY_HIGHEST,
addOptions() {
return {
renderMarkdown: null,
serializer: null,
};
},
addCommands() {
return {
pasteMarkdown: (markdown) => () => {
pasteContent: (content = '', processMarkdown = true) => async () => {
const { editor, options } = this;
const { renderMarkdown, eventHub } = options;
const deserializer = createMarkdownDeserializer({ render: renderMarkdown });
deserializer
.deserialize({ schema: editor.schema, markdown })
.then(({ document }) => {
if (!document) {
return;
}
const pasteSchemaSpec = { ...editor.schema.spec };
pasteSchemaSpec.marks = OrderedMap.from(pasteSchemaSpec.marks).remove('span');
pasteSchemaSpec.nodes = OrderedMap.from(pasteSchemaSpec.nodes).remove('div').remove('pre');
const schema = new Schema(pasteSchemaSpec);
const promise = processMarkdown
? deserializer.deserialize({ schema, markdown: content })
: Promise.resolve(parseHTML(schema, content));
promise
.then(({ document }) => {
if (!document) return;
const { state, view } = editor;
const { tr, selection } = state;
const { firstChild } = document.content;
const content =
const toPaste =
document.content.childCount === 1 && firstChild.type.name === 'paragraph'
? firstChild.content
: document.content;
if (selection.to - selection.from > 0) {
tr.replaceWith(selection.from, selection.to, content);
} else {
tr.insert(selection.from, content);
}
view.dispatch(tr);
editor.commands.insertContent(toPaste.toJSON());
})
.catch(() => {
eventHub.$emit(ALERT_EVENT, {
@ -65,24 +75,57 @@ export default Extension.create({
addProseMirrorPlugins() {
let pasteRaw = false;
const handleCutAndCopy = (view, event) => {
const slice = view.state.selection.content();
const gfmContent = this.options.serializer.serialize({ doc: slice.content });
const documentFragment = DOMSerializer.fromSchema(view.state.schema).serializeFragment(
slice.content,
);
const div = document.createElement('div');
div.appendChild(documentFragment);
event.clipboardData.setData(TEXT_FORMAT, div.innerText);
event.clipboardData.setData(HTML_FORMAT, div.innerHTML);
event.clipboardData.setData(GFM_FORMAT, gfmContent);
event.preventDefault();
event.stopPropagation();
};
return [
new Plugin({
key: new PluginKey('pasteMarkdown'),
props: {
handleDOMEvents: {
copy: handleCutAndCopy,
cut: (view, event) => {
handleCutAndCopy(view, event);
this.editor.commands.deleteSelection();
},
},
handleKeyDown: (_, event) => {
pasteRaw = event.key === 'v' && (event.metaKey || event.ctrlKey) && event.shiftKey;
},
handlePaste: (view, event) => {
const { clipboardData } = event;
const content = clipboardData.getData(TEXT_FORMAT);
const { state } = view;
const { tr, selection } = state;
const { from, to } = selection;
const gfmContent = clipboardData.getData(GFM_FORMAT);
if (gfmContent) {
return this.editor.commands.pasteContent(gfmContent, true);
}
const textContent = clipboardData.getData(TEXT_FORMAT);
const htmlContent = clipboardData.getData(HTML_FORMAT);
const { from, to } = view.state.selection;
if (pasteRaw) {
tr.insertText(content.replace(/^\s+|\s+$/gm, ''), from, to);
view.dispatch(tr);
this.editor.commands.insertContentAt(
{ from, to },
textContent.replace(/^\s+|\s+$/gm, ''),
);
return true;
}
@ -91,18 +134,19 @@ export default Extension.create({
const vsCodeMeta = hasVsCode ? JSON.parse(clipboardData.getData(VS_CODE_FORMAT)) : {};
const language = vsCodeMeta.mode;
if (!content || (hasHTML && !hasVsCode) || (hasVsCode && language !== 'markdown')) {
return false;
}
// if a code block is active, paste as plain text
if (CODE_BLOCK_NODE_TYPES.some((type) => this.editor.isActive(type))) {
if (!textContent || CODE_BLOCK_NODE_TYPES.some((type) => this.editor.isActive(type))) {
return false;
}
this.editor.commands.pasteMarkdown(content);
if (hasVsCode) {
return this.editor.commands.pasteContent(
language === 'markdown' ? textContent : `\`\`\`${language}\n${textContent}\n\`\`\``,
true,
);
}
return true;
return this.editor.commands.pasteContent(hasHTML ? htmlContent : textContent, !hasHTML);
},
},
}),

View File

@ -20,7 +20,13 @@ export default Reference.extend({
},
color: {
default: null,
parseHTML: (element) => element.querySelector('.gl-label-text').style.backgroundColor,
parseHTML: (element) => {
let color = element.querySelector('.gl-label-text').style.backgroundColor;
if (!color || color.startsWith('var'))
color = element.style.getPropertyValue('--label-background-color');
return color;
},
},
};
},

View File

@ -64,7 +64,7 @@ import Text from '../extensions/text';
import Video from '../extensions/video';
import WordBreak from '../extensions/word_break';
import { ContentEditor } from './content_editor';
import createMarkdownSerializer from './markdown_serializer';
import MarkdownSerializer from './markdown_serializer';
import createGlApiMarkdownDeserializer from './gl_api_markdown_deserializer';
import createRemarkMarkdownDeserializer from './remark_markdown_deserializer';
import AssetResolver from './asset_resolver';
@ -97,6 +97,12 @@ export const createContentEditor = ({
const eventHub = eventHubFactory();
const assetResolver = new AssetResolver({ renderMarkdown });
const serializer = new MarkdownSerializer({ serializerConfig });
const deserializer = window.gon?.features?.preserveUnchangedMarkdown
? createRemarkMarkdownDeserializer()
: createGlApiMarkdownDeserializer({
render: renderMarkdown,
});
const builtInContentEditorExtensions = [
Attachment.configure({ uploadsPath, renderMarkdown, eventHub }),
@ -139,7 +145,7 @@ export const createContentEditor = ({
MathInline,
OrderedList,
Paragraph,
PasteMarkdown.configure({ eventHub, renderMarkdown }),
PasteMarkdown.configure({ eventHub, renderMarkdown, serializer }),
Reference.configure({ assetResolver }),
ReferenceLabel,
ReferenceDefinition,
@ -167,12 +173,6 @@ export const createContentEditor = ({
const trackedExtensions = allExtensions.map(trackInputRulesAndShortcuts);
const tiptapEditor = createTiptapEditor({ extensions: trackedExtensions, ...tiptapOptions });
const serializer = createMarkdownSerializer({ serializerConfig });
const deserializer = window.gon?.features?.preserveUnchangedMarkdown
? createRemarkMarkdownDeserializer()
: createGlApiMarkdownDeserializer({
render: renderMarkdown,
});
return new ContentEditor({
tiptapEditor,

View File

@ -67,6 +67,7 @@ import {
renderContent,
renderBulletList,
renderReference,
renderReferenceLabel,
preserveUnchanged,
bold,
italic,
@ -197,7 +198,7 @@ const defaultSerializerConfig = {
[OrderedList.name]: preserveUnchanged(renderOrderedList),
[Paragraph.name]: preserveUnchanged(defaultMarkdownSerializer.nodes.paragraph),
[Reference.name]: renderReference,
[ReferenceLabel.name]: renderReference,
[ReferenceLabel.name]: renderReferenceLabel,
[ReferenceDefinition.name]: preserveUnchanged({
render: (state, node, parent, index, same, sourceMarkdown) => {
const nextSibling = parent.maybeChild(index + 1);
@ -273,19 +274,22 @@ const createChangeTracker = (doc, pristineDoc) => {
return changeTracker;
};
/**
* Converts a ProseMirror document to Markdown. See the
* following documentation to learn how to implement
* custom node and mark serializer functions.
*
* https://github.com/prosemirror/prosemirror-markdown
*
* @param {Object} params.nodes ProseMirror node serializer functions
* @param {Object} params.marks ProseMirror marks serializer config
*
* @returns a markdown serializer
*/
export default ({ serializerConfig = {} } = {}) => ({
export default class MarkdownSerializer {
/**
* Converts a ProseMirror document to Markdown. See the
* following documentation to learn how to implement
* custom node and mark serializer functions.
*
* https://github.com/prosemirror/prosemirror-markdown
*
* @param {Object} params.nodes ProseMirror node serializer functions
* @param {Object} params.marks ProseMirror marks serializer config
*
* @returns a markdown serializer
*/
constructor({ serializerConfig = {} } = {}) {
this.serializerConfig = serializerConfig;
}
/**
* Serializes a ProseMirror document as Markdown. If a node contains
* sourcemap metadata, the serializer is capable of restoring the
@ -301,16 +305,16 @@ export default ({ serializerConfig = {} } = {}) => ({
* changed.
* @returns A String that represents the serialized document as Markdown
*/
serialize: ({ doc, pristineDoc }) => {
serialize({ doc, pristineDoc }) {
const changeTracker = createChangeTracker(doc, pristineDoc);
const serializer = new ProseMirrorMarkdownSerializer(
{
...defaultSerializerConfig.nodes,
...serializerConfig.nodes,
...this.serializerConfig.nodes,
},
{
...defaultSerializerConfig.marks,
...serializerConfig.marks,
...this.serializerConfig.marks,
},
);
@ -318,5 +322,5 @@ export default ({ serializerConfig = {} } = {}) => ({
tightLists: true,
changeTracker,
});
},
});
}
}

View File

@ -157,6 +157,10 @@ function setIsInBlockTable(table, value) {
});
}
function ensureSpace(state) {
if (!state.atBlank() && !state.out.endsWith(' ')) state.write(' ');
}
function unsetIsInBlockTable(table) {
tableMap.delete(table);
@ -457,9 +461,15 @@ export function renderOrderedList(state, node) {
}
export function renderReference(state, node) {
ensureSpace(state);
state.write(node.attrs.originalText || node.attrs.text);
}
export function renderReferenceLabel(state, node) {
ensureSpace(state);
state.write(node.attrs.originalText || `~${state.quote(node.attrs.text)}`);
}
const generateBoldTags = (wrapTagName = openTag) => {
return (_, mark) => {
const type = /^(\*\*|__|<strong|<b).*/.exec(mark.attrs.sourceMarkdown)?.[1];

View File

@ -199,6 +199,9 @@ export default {
(this.fileDiscussions.length || this.file.drafts.length || this.file.hasCommentForm)
);
},
diffFileHash() {
return this.file.file_hash;
},
},
watch: {
'file.id': {

View File

@ -40,6 +40,9 @@ export default {
iconName: 'lock',
visible: this.isLocked,
dataTestId: 'locked',
tooltip: sprintf(__('This %{issuable} is locked. Only project members can comment.'), {
issuable: noteableTypeText[this.getNoteableData.targetType],
}),
},
{
iconName: 'spam',
@ -67,7 +70,7 @@ export default {
<div
v-if="meta.visible"
:key="meta.iconName"
v-gl-tooltip
v-gl-tooltip.bottom
:data-testid="meta.dataTestId"
:title="meta.tooltip || null"
:class="{

View File

@ -531,7 +531,13 @@ export default {
statusText
}}</span></gl-badge
>
<span v-if="isLocked" data-testid="locked" class="issuable-warning-icon">
<span
v-if="isLocked"
v-gl-tooltip.bottom
data-testid="locked"
class="issuable-warning-icon"
:title="__('This issue is locked. Only project members can comment.')"
>
<gl-icon name="lock" :aria-label="__('Locked')" />
</span>
<confidentiality-badge
@ -542,7 +548,7 @@ export default {
/>
<span
v-if="isHidden"
v-gl-tooltip
v-gl-tooltip.bottom
:title="__('This issue is hidden because its author has been banned')"
data-testid="hidden"
class="issuable-warning-icon"

View File

@ -58,7 +58,7 @@ export default {
const position =
positionType ||
(this.diffFileCommentForm ? IMAGE_DIFF_POSITION_TYPE : TEXT_DIFF_POSITION_TYPE);
const selectedDiffFile = this.getDiffFileByHash(this.diffFileHash);
const diffFile = this.diffFile || this.file;
const postData = getDraftFormData({
note,
notesData: this.notesData,
@ -66,7 +66,7 @@ export default {
noteableType: this.noteableType,
noteTargetLine: this.noteTargetLine,
diffViewType: this.diffViewType,
diffFile: selectedDiffFile,
diffFile,
linePosition: this.position,
positionType: position,
...this.diffFileCommentForm,
@ -74,7 +74,7 @@ export default {
showWhitespace: this.showWhitespace,
});
const diffFileHeadSha = this.commit && this?.diffFile?.diff_refs?.head_sha;
const diffFileHeadSha = this.commit && diffFile?.diff_refs?.head_sha;
postData.data.note.commit_id = diffFileHeadSha || null;
@ -85,7 +85,7 @@ export default {
} else if (this.line?.line_code) {
this.handleClearForm(this.line.line_code);
} else if (position === FILE_DIFF_POSITION_TYPE) {
this.toggleFileCommentForm(this.diffFile.file_path);
this.toggleFileCommentForm(diffFile.file_path);
}
})
.catch(() => {

View File

@ -0,0 +1,37 @@
<script>
import { GlIcon } from '@gitlab/ui';
import SafeHtml from '~/vue_shared/directives/safe_html';
import highlight from '~/lib/utils/highlight';
export default {
name: 'CommandPaletteCommandAutocompleteItem',
components: {
GlIcon,
},
directives: {
SafeHtml,
},
props: {
command: {
type: Object,
required: true,
},
searchQuery: {
type: String,
required: true,
},
},
computed: {
highlightedName() {
return highlight(this.command.text, this.searchQuery);
},
},
};
</script>
<template>
<div class="gl-display-flex gl-align-items-center">
<gl-icon v-if="command.icon" class="gl-mr-3" :name="command.icon" />
<span v-safe-html="highlightedName" class="gl-text-gray-900"></span>
</div>
</template>

View File

@ -10,11 +10,13 @@ import {
COMMON_HANDLES,
COMMAND_HANDLE,
USER_HANDLE,
COMMANDS_GROUP_TITLE,
GLOBAL_COMMANDS_GROUP_TITLE,
USERS_GROUP_TITLE,
PAGES_GROUP_TITLE,
} from './constants';
import { userMapper, commandMapper } from './utils';
import { userMapper, commandMapper, linksReducer } from './utils';
import UserAutocompleteItem from './user_autocomplete_item.vue';
import CommandAutocompleteItem from './command_autocomplete_item.vue';
export default {
name: 'CommandPaletteItems',
@ -22,8 +24,9 @@ export default {
GlDisclosureDropdownGroup,
GlLoadingIcon,
UserAutocompleteItem,
CommandAutocompleteItem,
},
inject: ['commandPaletteData'],
inject: ['commandPaletteCommands', 'commandPaletteLinks'],
props: {
searchQuery: {
type: String,
@ -38,7 +41,7 @@ export default {
},
},
data: () => ({
group: null,
groups: [],
error: null,
loading: false,
}),
@ -50,21 +53,25 @@ export default {
return this.handle === USER_HANDLE;
},
commands() {
return this.commandPaletteData.map(commandMapper);
return this.commandPaletteCommands.map(commandMapper);
},
links() {
return this.commandPaletteLinks.reduce(linksReducer, []);
},
filteredCommands() {
return this.searchQuery
? fuzzaldrinPlus.filter(this.commands, this.searchQuery, { key: 'keywords' })
? this.commands
.map(({ name, items }) => {
return {
name: name || GLOBAL_COMMANDS_GROUP_TITLE,
items: this.filterBySearchQuery(items, 'text'),
};
})
.filter(({ items }) => items.length)
: this.commands;
},
commandsGroup() {
return {
name: COMMANDS_GROUP_TITLE,
items: this.filteredCommands,
};
},
hasResults() {
return this.group?.items?.length;
return this.groups?.length && this.groups.some((group) => group.items?.length);
},
hasSearchQuery() {
return this.searchQuery?.length;
@ -75,7 +82,7 @@ export default {
handler() {
switch (this.handle) {
case COMMAND_HANDLE:
this.group = this.commandsGroup;
this.getCommandsAndPages();
break;
case USER_HANDLE:
this.getUsers();
@ -88,6 +95,32 @@ export default {
},
},
methods: {
filterBySearchQuery(items, key = 'keywords') {
return fuzzaldrinPlus.filter(items, this.searchQuery, { key });
},
getCommandsAndPages() {
if (!this.searchQuery) {
this.groups = [...this.commands];
return;
}
const matchedLinks = this.filterBySearchQuery(this.links);
if (this.filteredCommands.length || matchedLinks.length) {
this.groups = [];
}
if (this.filteredCommands.length) {
this.groups = [...this.filteredCommands];
}
if (matchedLinks.length) {
this.groups.push({
name: PAGES_GROUP_TITLE,
items: matchedLinks,
});
}
},
getUsers: debounce(function debouncedUserSearch() {
if (this.searchQuery && this.searchQuery.length < 3) return null;
@ -100,10 +133,12 @@ export default {
},
})
.then(({ data }) => {
this.group = {
name: USERS_GROUP_TITLE,
items: data.map(userMapper),
};
this.groups = [
{
name: USERS_GROUP_TITLE,
items: data.map(userMapper),
},
];
})
.catch((error) => {
this.error = error;
@ -120,11 +155,25 @@ export default {
<ul class="gl-p-0 gl-m-0 gl-list-style-none">
<gl-loading-icon v-if="loading" size="lg" class="gl-my-5" />
<gl-disclosure-dropdown-group v-else-if="hasResults" :group="group" bordered class="gl-mt-0!">
<template v-if="isUserMode" #list-item="{ item }">
<user-autocomplete-item :user="item" :search-query="searchQuery" />
</template>
</gl-disclosure-dropdown-group>
<template v-else-if="hasResults">
<gl-disclosure-dropdown-group
v-for="(group, index) in groups"
:key="index"
:group="group"
bordered
class="{'gl-mt-0!': index===0}"
>
<template #list-item="{ item }">
<user-autocomplete-item v-if="isUserMode" :user="item" :search-query="searchQuery" />
<command-autocomplete-item
v-if="isCommandMode"
:command="item"
:search-query="searchQuery"
/>
</template>
</gl-disclosure-dropdown-group>
</template>
<div v-else-if="hasSearchQuery && !hasResults" class="gl-text-gray-700 gl-pl-5 gl-py-3">
{{ __('No results found') }}
</div>

View File

@ -21,5 +21,6 @@ export const SEARCH_SCOPE = {
[USER_HANDLE]: s__('CommandPalette|user (enter at least 3 chars)'),
};
export const COMMANDS_GROUP_TITLE = s__('CommandPalette|Commands');
export const GLOBAL_COMMANDS_GROUP_TITLE = s__('CommandPalette|Global Commands');
export const USERS_GROUP_TITLE = s__('CommandPalette|Users');
export const PAGES_GROUP_TITLE = s__('CommandPalette|Pages');

View File

@ -3,7 +3,6 @@ import { GlAvatar } from '@gitlab/ui';
import SafeHtml from '~/vue_shared/directives/safe_html';
import highlight from '~/lib/utils/highlight';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import { AUTOCOMPLETE_ERROR_MESSAGE } from '~/vue_shared/global_search/constants';
export default {
name: 'CommandPaletteUserAutocompleteItem',
@ -23,12 +22,9 @@ export default {
required: true,
},
},
i18n: {
AUTOCOMPLETE_ERROR_MESSAGE,
},
methods: {
highlightedName(val) {
return highlight(val, this.searchQuery);
computed: {
highlightedName() {
return highlight(this.user.text, this.searchQuery);
},
},
AVATAR_SHAPE_OPTION_RECT,
@ -48,7 +44,7 @@ export default {
aria-hidden="true"
/>
<span class="gl-display-flex gl-flex-direction-column">
<span v-safe-html="highlightedName(user.text)" class="gl-text-gray-900"></span>
<span v-safe-html="highlightedName" class="gl-text-gray-900"></span>
<span v-safe-html="user.username" class="gl-font-sm gl-text-gray-500"></span>
</span>
</div>

View File

@ -4,8 +4,32 @@ export const userMapper = ({ name: text, web_url: href, ...user } = {}) => ({
...user,
});
export const commandMapper = ({ text, href, keywords = [] } = {}) => ({
text,
href,
keywords: keywords.join(''),
});
export const commandMapper = ({ name, items }) => {
// TODO: we filter out invite_members for now, because it is complicated to add the invite members modal here
// and is out of scope for the basic command palette items. If it proves to be useful, we can add it later.
return {
name,
items: items.filter(({ component }) => component !== 'invite_members'),
};
};
export const linksReducer = (acc, menuItem) => {
acc.push({
text: menuItem.title,
keywords: menuItem.title,
icon: menuItem.icon,
href: menuItem.link,
});
if (menuItem.items?.length) {
const items = menuItem.items.map(({ title, link }) => ({
keywords: title,
text: [menuItem.title, title].join(' > '),
href: link,
icon: menuItem.icon,
}));
/* eslint-disable-next-line no-param-reassign */
acc = [...acc, ...items];
}
return acc;
};

View File

@ -72,7 +72,8 @@ export const initSuperSidebar = () => {
const sidebarData = JSON.parse(sidebar);
const searchData = convertObjectPropsToCamelCase(sidebarData.search);
const commandPaletteData = convertObjectPropsToCamelCase(sidebarData.command_palette_commands);
const commandPaletteCommands = sidebarData.create_new_menu_groups || [];
const commandPaletteLinks = convertObjectPropsToCamelCase(sidebarData.current_menu_items || []);
const { searchPath, issuesPath, mrPath, autocompletePath, searchContext } = searchData;
const isImpersonating = parseBoolean(sidebarData.is_impersonating);
@ -86,7 +87,8 @@ export const initSuperSidebar = () => {
toggleNewNavEndpoint,
isImpersonating,
...getTrialStatusWidgetData(sidebarData),
commandPaletteData,
commandPaletteCommands,
commandPaletteLinks,
},
store: createStore({
searchPath,

View File

@ -339,7 +339,7 @@ export default {
</script>
<template>
<div class="vue-filtered-search-bar-container gl-md-display-flex">
<div class="vue-filtered-search-bar-container gl-md-display-flex gl-min-w-0">
<gl-form-checkbox
v-if="showCheckbox"
class="gl-align-self-center"

View File

@ -4,6 +4,7 @@ import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__ } from '~/locale';
import { contentTop } from '~/lib/utils/common_utils';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import { DRAWER_Z_INDEX } from '~/lib/utils/constants';
import { getRenderedMarkdown } from './utils/fetch';
export const cache = {};
@ -95,10 +96,17 @@ export default {
safeHtmlConfig: {
ADD_TAGS: ['copy-code'],
},
DRAWER_Z_INDEX,
};
</script>
<template>
<gl-drawer :header-height="drawerTop" :open="open" header-sticky @close="closeDrawer">
<gl-drawer
:header-height="drawerTop"
:open="open"
header-sticky
:z-index="$options.DRAWER_Z_INDEX"
@close="closeDrawer"
>
<template #title>
<h4 data-testid="title-element" class="gl-m-0">{{ title }}</h4>
</template>

View File

@ -31,7 +31,7 @@ git push -uf origin <%= @project.default_branch_or_main %>
- [ ] [Create a new merge request](<%= redirect("https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html") %>)
- [ ] [Automatically close issues from merge requests](<%= redirect("https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically") %>)
- [ ] [Enable merge request approvals](<%= redirect("https://docs.gitlab.com/ee/user/project/merge_requests/approvals/") %>)
- [ ] [Automatically merge when pipeline succeeds](<%= redirect("https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html") %>)
- [ ] [Set auto-merge](<%= redirect("https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html") %>)
## Test and Deploy

View File

@ -89,9 +89,7 @@ module SidebarsHelper
update_pins_url: pins_url,
is_impersonating: impersonating?,
stop_impersonation_path: admin_impersonation_path,
shortcut_links: shortcut_links(user, project: project),
# command palette
command_palette_commands: create_command_palette_menu
shortcut_links: shortcut_links(user, project: project)
}
end
@ -173,34 +171,6 @@ module SidebarsHelper
end
end
def create_command_palette_menu
menu_items = []
if current_user.can_create_project?
menu_items.push({
text: _('New project/repository'),
href: new_project_path,
keywords: [_('Create a new project/repository')]
})
end
if current_user.can_create_group?
menu_items.push({
text: _('New group'),
href: new_group_path,
keywords: ['Create a new group']
})
end
return unless current_user.can?(:create_snippet)
menu_items.push({
text: _('New snippet'),
href: new_snippet_path,
keywords: ['Create a new snippet']
})
end
def create_merge_request_menu(user)
[
{

View File

@ -100,7 +100,10 @@ module Ci
scope :with_recent_runner_queue, -> { where('contacted_at > ?', recent_queue_deadline) }
scope :with_running_builds, -> do
where('EXISTS(?)', ::Ci::Build.running.select(1).where('ci_builds.runner_id = ci_runners.id'))
where('EXISTS(?)',
::Ci::Build.running.select(1)
.where("#{::Ci::Build.quoted_table_name}.runner_id = #{quoted_table_name}.id")
)
end
# BACKWARD COMPATIBILITY: There are needed to maintain compatibility with `AVAILABLE_SCOPES` used by `lib/api/runners.rb`

View File

@ -23,7 +23,7 @@ module Clusters
has_many :projects, through: :cluster_projects, class_name: '::Project'
has_one :cluster_project, -> { order(id: :desc) }, class_name: 'Clusters::Project'
has_many :deployment_clusters
has_many :deployments, inverse_of: :cluster
has_many :deployments, inverse_of: :cluster, through: :deployment_clusters
has_many :successful_deployments, -> { success }, class_name: 'Deployment'
has_many :environments, -> { distinct }, through: :deployments

View File

@ -16,7 +16,6 @@ class Deployment < ApplicationRecord
belongs_to :project, optional: false
belongs_to :environment, optional: false
belongs_to :cluster, class_name: 'Clusters::Cluster', optional: true
belongs_to :user
belongs_to :deployable, polymorphic: true, optional: true, inverse_of: :deployment # rubocop:disable Cop/PolymorphicAssociations
has_many :deployment_merge_requests
@ -35,6 +34,7 @@ class Deployment < ApplicationRecord
delegate :name, to: :environment, prefix: true
delegate :kubernetes_namespace, to: :deployment_cluster, allow_nil: true
delegate :cluster, to: :deployment_cluster, allow_nil: true
scope :for_iid, -> (project, iid) { where(project: project, iid: iid) }
scope :for_environment, -> (environment) { where(environment_id: environment) }

View File

@ -83,7 +83,7 @@ class EnvironmentSerializer < BaseSerializer
def deployment_associations
{
user: [],
cluster: [],
deployment_cluster: { cluster: [] },
project: {
route: [],
namespace: :route

View File

@ -78,7 +78,7 @@
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
%span= _('Merge request was scheduled to merge after pipeline succeeds')
%span= _('Merge request was set to auto-merge')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
@ -91,7 +91,7 @@
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:-4px;", alt: "Merge request icon" }
%span{ style: "font-weight: 600;color:#333333;" }= _('Merge request')
%a{ href: merge_request_url(@merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none" }= @merge_request.to_reference
%span= _('was scheduled to merge after pipeline succeeds by')
%span= _('was set to auto-merge by')
%img.avatar{ height: "24", src: avatar_icon_for_user(@mwps_set_by, 24, only_path: false), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar" }
%a.muted{ href: user_url(@mwps_set_by), style: "color:#333333;text-decoration:none;" }
= @mwps_set_by.name

View File

@ -1,4 +1,4 @@
Merge request #{@merge_request.to_reference} was scheduled to merge after pipeline succeeds by #{sanitize_name(@mwps_set_by.name)}
Merge request #{@merge_request.to_reference} was set to auto-merge by #{sanitize_name(@mwps_set_by.name)}
Merge request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}

View File

@ -31,7 +31,7 @@ git push -uf origin <%= params[:default_branch] %>
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/407456
milestone: '15.11'
type: development
group: group::pipeline execution
default_enabled: false
default_enabled: true

View File

@ -1,8 +0,0 @@
---
name: use_click_house_database_for_error_tracking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90675
rollout_issue_url: https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/1728
milestone: '15.2'
type: development
group: group::observability
default_enabled: false

View File

@ -8,4 +8,4 @@
stage: manage # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/388959 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
`omniauth-shibboleth` is an OmniAuth strategy gem that was part of GitLab. The gem has not received security updates and does not meet GitLab quality guidance criteria. This gem was originally scheduled for removal by 14.1, but it was not removed at that time. The gem is being removed now.
`omniauth-shibboleth` is an OmniAuth strategy gem that was part of GitLab. The gem has not received security updates and does not meet GitLab quality guidance criteria. This gem was originally scheduled for removal by 14.1, but it was not removed at that time. The gem is being removed now. In GitLab 16.1 or later, use the `omniauth-shibboleth-redux` gem instead.

View File

@ -934,7 +934,7 @@ The Live Preview feature of the Web IDE was intended to provide a client-side pr
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/388959).
</div>
`omniauth-shibboleth` is an OmniAuth strategy gem that was part of GitLab. The gem has not received security updates and does not meet GitLab quality guidance criteria. This gem was originally scheduled for removal by 14.1, but it was not removed at that time. The gem is being removed now.
`omniauth-shibboleth` is an OmniAuth strategy gem that was part of GitLab. The gem has not received security updates and does not meet GitLab quality guidance criteria. This gem was originally scheduled for removal by 14.1, but it was not removed at that time. The gem is being removed now. In GitLab 16.1 or later, use the `omniauth-shibboleth-redux` gem instead.
## Removed in 15.8

View File

@ -43,7 +43,7 @@ module API
project = Project.find(project_id)
enabled = error_tracking_enabled? &&
Feature.enabled?(:use_click_house_database_for_error_tracking, project) &&
Feature.enabled?(:gitlab_error_tracking, project) &&
::ErrorTracking::ClientKey.enabled_key_for(project_id, public_key).exists?
status 200

View File

@ -16,7 +16,7 @@ module Gitlab
# @return [self]
def self.build(project)
strategy =
if Feature.enabled?(:use_click_house_database_for_error_tracking, project)
if Feature.enabled?(:gitlab_error_tracking, project)
OpenApiStrategy.new(project)
else
ActiveRecordStrategy.new(project)

View File

@ -1,23 +0,0 @@
# frozen_string_literal: true
module Gitlab
module ImportExport
class LegacyRelationTreeSaver
include Gitlab::ImportExport::CommandLineUtil
def serialize(exportable, relations_tree)
Gitlab::ImportExport::FastHashSerializer
.new(exportable, relations_tree)
.execute
end
def save(tree, dir_path, filename)
mkdir_p(dir_path)
tree_json = ::JSON.generate(tree)
File.write(File.join(dir_path, filename), tree_json)
end
end
end
end

View File

@ -11202,7 +11202,10 @@ msgstr ""
msgid "Command line instructions"
msgstr ""
msgid "CommandPalette|Commands"
msgid "CommandPalette|Global Commands"
msgstr ""
msgid "CommandPalette|Pages"
msgstr ""
msgid "CommandPalette|Type %{commandHandle} for command, %{userHandle} for user or perform generic search..."
@ -12935,9 +12938,6 @@ msgstr ""
msgid "Create a new project"
msgstr ""
msgid "Create a new project/repository"
msgstr ""
msgid "Create a new repository"
msgstr ""
@ -28384,7 +28384,7 @@ msgstr ""
msgid "Merge request title"
msgstr ""
msgid "Merge request was scheduled to merge after pipeline succeeds"
msgid "Merge request was set to auto-merge"
msgstr ""
msgid "Merge requests"
@ -46391,6 +46391,9 @@ msgstr ""
msgid "This %{issuable} is locked. Only %{strong_open}project members%{strong_close} can comment."
msgstr ""
msgid "This %{issuable} is locked. Only project members can comment."
msgstr ""
msgid "This %{noteableTypeText} is %{confidentialLinkStart}confidential%{confidentialLinkEnd} and %{lockedLinkStart}locked%{lockedLinkEnd}."
msgstr ""
@ -46721,6 +46724,9 @@ msgstr ""
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
msgid "This issue is locked. Only project members can comment."
msgstr ""
msgid "This job could not start because it could not retrieve the needed artifacts%{punctuation}%{invalid_dependencies}"
msgstr ""
@ -55274,7 +55280,7 @@ msgstr ""
msgid "vulnerability|dismissed"
msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgid "was set to auto-merge by"
msgstr ""
msgid "weekly"

View File

@ -169,6 +169,7 @@
"monaco-editor-webpack-plugin": "^6.0.0",
"monaco-yaml": "4.0.0",
"mousetrap": "1.6.5",
"orderedmap": "^2.1.1",
"papaparse": "^5.3.1",
"patch-package": "^6.4.7",
"pdfjs-dist": "^2.16.105",

View File

@ -6,4 +6,12 @@ FactoryBot.define do
deployment
kubernetes_namespace { 'the-namespace' }
end
trait :provided_by_gcp do
cluster factory: %i[cluster provided_by_gcp]
end
trait :not_managed do
cluster factory: %i[cluster not_managed]
end
end

View File

@ -29,7 +29,11 @@ FactoryBot.define do
end
trait :on_cluster do
cluster factory: %i(cluster provided_by_gcp)
deployment_cluster factory: %i(deployment_cluster provided_by_gcp)
end
trait :on_cluster_not_managed do
deployment_cluster factory: %i(deployment_cluster not_managed)
end
trait :running do

View File

@ -548,24 +548,24 @@ RSpec.describe 'Jobs', :clean_gitlab_redis_shared_state, feature_category: :grou
end
context 'when there is a cluster used for the deployment' do
let(:cluster) { create(:cluster, name: 'the-cluster') }
let(:deployment) { create(:deployment, :success, cluster: cluster, environment: environment, project: environment.project) }
let(:deployment) { create(:deployment, :success, :on_cluster, environment: environment) }
let(:user_access_level) { :maintainer }
let(:cluster) { deployment.cluster }
it 'shows a link to the cluster' do
expect(page).to have_link 'the-cluster'
expect(page).to have_link cluster.name
end
it 'shows the name of the cluster' do
expect(page).to have_content 'using cluster the-cluster'
expect(page).to have_content "using cluster #{cluster.name}"
end
context 'when the user is not able to view the cluster' do
let(:user_access_level) { :reporter }
it 'includes only the name of the cluster without a link' do
expect(page).to have_content 'using cluster the-cluster'
expect(page).not_to have_link 'the-cluster'
expect(page).to have_content "using cluster #{cluster.name}"
expect(page).not_to have_link cluster.name
end
end
end

View File

@ -7,10 +7,10 @@ RSpec.describe Clusters::KnativeServicesFinder do
include ReactiveCachingHelpers
let(:project) { create(:project, :repository) }
let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) }
let(:cluster) { deployment.cluster }
let(:service) { environment.deployment_platform }
let(:environment) { create(:environment, project: project) }
let!(:deployment) { create(:deployment, :success, environment: environment, cluster: cluster) }
let!(:deployment) { create(:deployment, :success, :on_cluster, environment: environment) }
let(:namespace) do
create(:cluster_kubernetes_namespace,
cluster: cluster,

File diff suppressed because one or more lines are too long

View File

@ -1,507 +0,0 @@
{
"description":"",
"visibility_level":0,
"archived":false,
"merge_requests_template":null,
"merge_requests_rebase_enabled":false,
"approvals_before_merge":0,
"reset_approvals_on_push":true,
"merge_requests_ff_only_enabled":false,
"issues_template":null,
"shared_runners_enabled":true,
"build_allow_git_fetch":true,
"build_timeout":3600,
"pending_delete":false,
"public_builds":true,
"last_repository_check_failed":null,
"container_registry_enabled":true,
"only_allow_merge_if_pipeline_succeeds":false,
"has_external_issue_tracker":false,
"request_access_enabled":false,
"has_external_wiki":false,
"ci_config_path":null,
"only_allow_merge_if_all_discussions_are_resolved":false,
"repository_size_limit":null,
"printing_merge_request_link_enabled":true,
"auto_cancel_pending_pipelines":"enabled",
"service_desk_enabled":null,
"delete_error":null,
"disable_overriding_approvers_per_merge_request":null,
"resolve_outdated_diff_discussions":false,
"jobs_cache_index":null,
"external_authorization_classification_label":null,
"pages_https_only":false,
"external_webhook_token":null,
"merge_requests_author_approval":null,
"merge_requests_disable_committers_approval":null,
"require_password_to_approve":null,
"labels":[
],
"milestones":[
],
"issues":[
{
"id":469,
"title":"issue 1",
"author_id":1,
"project_id":30,
"created_at":"2019-08-07T03:57:55.007Z",
"updated_at":"2019-08-07T03:57:55.007Z",
"description":"",
"state":"opened",
"iid":1,
"updated_by_id":null,
"weight":null,
"confidential":false,
"due_date":null,
"moved_to_id":null,
"lock_version":0,
"time_estimate":0,
"relative_position":1073742323,
"external_author":null,
"last_edited_at":null,
"last_edited_by_id":null,
"discussion_locked":null,
"closed_at":null,
"closed_by_id":null,
"state_id":1,
"events":[
{
"id":1775,
"project_id":30,
"author_id":1,
"target_id":469,
"created_at":"2019-08-07T03:57:55.158Z",
"updated_at":"2019-08-07T03:57:55.158Z",
"target_type":"Issue",
"action":1
}
],
"timelogs":[
],
"notes":[
],
"label_links":[
],
"resource_label_events":[
],
"issue_assignees":[
],
"designs":[
{
"id":38,
"iid": 1,
"project_id":30,
"issue_id":469,
"filename":"chirrido3.jpg",
"notes":[
]
},
{
"id":39,
"iid": 2,
"project_id":30,
"issue_id":469,
"filename":"jonathan_richman.jpg",
"notes":[
]
},
{
"id":40,
"iid": 3,
"project_id":30,
"issue_id":469,
"filename":"mariavontrap.jpeg",
"notes":[
]
}
],
"design_versions":[
{
"id":24,
"sha":"9358d1bac8ff300d3d2597adaa2572a20f7f8703",
"issue_id":469,
"author_id":1,
"actions":[
{
"design_id":38,
"version_id":24,
"event":0,
"design":{
"id":38,
"iid": 1,
"project_id":30,
"issue_id":469,
"filename":"chirrido3.jpg"
}
}
]
},
{
"id":25,
"sha":"e1a4a501bcb42f291f84e5d04c8f927821542fb6",
"issue_id":469,
"author_id":2,
"actions":[
{
"design_id":38,
"version_id":25,
"event":1,
"design":{
"id":38,
"iid": 1,
"project_id":30,
"issue_id":469,
"filename":"chirrido3.jpg"
}
},
{
"design_id":39,
"version_id":25,
"event":0,
"design":{
"id":39,
"iid": 2,
"project_id":30,
"issue_id":469,
"filename":"jonathan_richman.jpg"
}
}
]
},
{
"id":26,
"sha":"27702d08f5ee021ae938737f84e8fe7c38599e85",
"issue_id":469,
"author_id":1,
"actions":[
{
"design_id":38,
"version_id":26,
"event":1,
"design":{
"id":38,
"iid": 1,
"project_id":30,
"issue_id":469,
"filename":"chirrido3.jpg"
}
},
{
"design_id":39,
"version_id":26,
"event":2,
"design":{
"id":39,
"iid": 2,
"project_id":30,
"issue_id":469,
"filename":"jonathan_richman.jpg"
}
},
{
"design_id":40,
"version_id":26,
"event":0,
"design":{
"id":40,
"iid": 3,
"project_id":30,
"issue_id":469,
"filename":"mariavontrap.jpeg"
}
}
]
}
]
},
{
"id":470,
"title":"issue 2",
"author_id":1,
"project_id":30,
"created_at":"2019-08-07T04:15:57.607Z",
"updated_at":"2019-08-07T04:15:57.607Z",
"description":"",
"state":"opened",
"iid":2,
"updated_by_id":null,
"weight":null,
"confidential":false,
"due_date":null,
"moved_to_id":null,
"lock_version":0,
"time_estimate":0,
"relative_position":1073742823,
"external_author":null,
"last_edited_at":null,
"last_edited_by_id":null,
"discussion_locked":null,
"closed_at":null,
"closed_by_id":null,
"state_id":1,
"events":[
{
"id":1776,
"project_id":30,
"author_id":1,
"target_id":470,
"created_at":"2019-08-07T04:15:57.789Z",
"updated_at":"2019-08-07T04:15:57.789Z",
"target_type":"Issue",
"action":1
}
],
"timelogs":[
],
"notes":[
],
"label_links":[
],
"resource_label_events":[
],
"issue_assignees":[
],
"designs":[
{
"id":42,
"project_id":30,
"issue_id":470,
"filename":"1 (1).jpeg",
"notes":[
]
},
{
"id":43,
"project_id":30,
"issue_id":470,
"filename":"2099743.jpg",
"notes":[
]
},
{
"id":44,
"project_id":30,
"issue_id":470,
"filename":"a screenshot (1).jpg",
"notes":[
]
},
{
"id":41,
"project_id":30,
"issue_id":470,
"filename":"chirrido3.jpg",
"notes":[
]
}
],
"design_versions":[
{
"id":27,
"sha":"8587e78ab6bda3bc820a9f014c3be4a21ad4fcc8",
"issue_id":470,
"author_id":1,
"actions":[
{
"design_id":41,
"version_id":27,
"event":0,
"design":{
"id":41,
"project_id":30,
"issue_id":470,
"filename":"chirrido3.jpg"
}
}
]
},
{
"id":28,
"sha":"73f871b4c8c1d65c62c460635e023179fb53abc4",
"issue_id":470,
"author_id":2,
"actions":[
{
"design_id":42,
"version_id":28,
"event":0,
"design":{
"id":42,
"project_id":30,
"issue_id":470,
"filename":"1 (1).jpeg"
}
},
{
"design_id":43,
"version_id":28,
"event":0,
"design":{
"id":43,
"project_id":30,
"issue_id":470,
"filename":"2099743.jpg"
}
}
]
},
{
"id":29,
"sha":"c9b5f067f3e892122a4b12b0a25a8089192f3ac8",
"issue_id":470,
"author_id":2,
"actions":[
{
"design_id":42,
"version_id":29,
"event":1,
"design":{
"id":42,
"project_id":30,
"issue_id":470,
"filename":"1 (1).jpeg"
}
},
{
"design_id":44,
"version_id":29,
"event":0,
"design":{
"id":44,
"project_id":30,
"issue_id":470,
"filename":"a screenshot (1).jpg"
}
}
]
}
]
}
],
"snippets":[
],
"releases":[
],
"project_members":[
{
"id":95,
"access_level":40,
"source_id":30,
"source_type":"Project",
"user_id":1,
"notification_level":3,
"created_at":"2019-08-07T03:57:32.825Z",
"updated_at":"2019-08-07T03:57:32.825Z",
"created_by_id":1,
"invite_email":null,
"invite_token":null,
"invite_accepted_at":null,
"requested_at":null,
"expires_at":null,
"ldap":false,
"override":false,
"user":{
"id":1,
"public_email":"admin@example.com",
"username":"root"
}
},
{
"id":96,
"access_level":40,
"source_id":30,
"source_type":"Project",
"user_id":2,
"notification_level":3,
"created_at":"2019-08-07T03:57:32.825Z",
"updated_at":"2019-08-07T03:57:32.825Z",
"created_by_id":null,
"invite_email":null,
"invite_token":null,
"invite_accepted_at":null,
"requested_at":null,
"expires_at":null,
"ldap":false,
"override":false,
"user":{
"id":2,
"public_email":"user_2@gitlabexample.com",
"username":"user_2"
}
}
],
"merge_requests":[
],
"ci_pipelines":[
],
"triggers":[
],
"pipeline_schedules":[
],
"protected_branches":[
],
"protected_environments": [
],
"protected_tags":[
],
"project_feature":{
"id":30,
"project_id":30,
"merge_requests_access_level":20,
"issues_access_level":20,
"wiki_access_level":20,
"snippets_access_level":20,
"builds_access_level":20,
"created_at":"2019-08-07T03:57:32.485Z",
"updated_at":"2019-08-07T03:57:32.485Z",
"repository_access_level":20,
"pages_access_level":10
},
"custom_attributes":[
],
"prometheus_metrics":[
],
"project_badges":[
],
"ci_cd_settings":{
"group_runners_enabled":true
},
"boards":[
],
"pipelines":[
]
}

View File

@ -1,282 +0,0 @@
{
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
"visibility_level": 10,
"archived": false,
"milestones": [
{
"id": 1,
"title": "Project milestone",
"project_id": 8,
"description": "Project-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": null
}
],
"labels": [
{
"id": 2,
"title": "A project label",
"color": "#428bca",
"project_id": 8,
"created_at": "2016-07-22T08:55:44.161Z",
"updated_at": "2016-07-22T08:55:44.161Z",
"template": false,
"description": "",
"type": "ProjectLabel",
"priorities": [
{
"id": 1,
"project_id": 5,
"label_id": 1,
"priority": 1,
"created_at": "2016-10-18T09:35:43.338Z",
"updated_at": "2016-10-18T09:35:43.338Z"
}
]
}
],
"issues": [
{
"id": 1,
"title": "Fugiat est minima quae maxime non similique.",
"assignee_id": null,
"project_id": 8,
"author_id": 1,
"created_at": "2017-07-07T18:13:01.138Z",
"updated_at": "2017-08-15T18:37:40.807Z",
"branch_name": null,
"description": "Quam totam fuga numquam in eveniet.",
"state": "opened",
"iid": 1,
"updated_by_id": 1,
"confidential": false,
"due_date": null,
"moved_to_id": null,
"lock_version": null,
"time_estimate": 0,
"closed_at": null,
"last_edited_at": null,
"last_edited_by_id": null,
"group_milestone_id": null,
"milestone": {
"id": 1,
"title": "Project milestone",
"project_id": 8,
"description": "Project-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": null
},
"label_links": [
{
"id": 11,
"label_id": 6,
"target_id": 1,
"target_type": "Issue",
"created_at": "2017-08-15T18:37:40.795Z",
"updated_at": "2017-08-15T18:37:40.795Z",
"label": {
"id": 6,
"title": "group label",
"color": "#A8D695",
"project_id": null,
"created_at": "2017-08-15T18:37:19.698Z",
"updated_at": "2017-08-15T18:37:19.698Z",
"template": false,
"description": "",
"group_id": 5,
"type": "GroupLabel",
"priorities": []
}
},
{
"id": 11,
"label_id": 2,
"target_id": 1,
"target_type": "Issue",
"created_at": "2017-08-15T18:37:40.795Z",
"updated_at": "2017-08-15T18:37:40.795Z",
"label": {
"id": 6,
"title": "A project label",
"color": "#A8D695",
"project_id": null,
"created_at": "2017-08-15T18:37:19.698Z",
"updated_at": "2017-08-15T18:37:19.698Z",
"template": false,
"description": "",
"group_id": 5,
"type": "ProjectLabel",
"priorities": []
}
}
]
},
{
"id": 2,
"title": "Fugiat est minima quae maxime non similique.",
"assignee_id": null,
"project_id": 8,
"author_id": 1,
"created_at": "2017-07-07T18:13:01.138Z",
"updated_at": "2017-08-15T18:37:40.807Z",
"branch_name": null,
"description": "Quam totam fuga numquam in eveniet.",
"state": "closed",
"iid": 2,
"updated_by_id": 1,
"confidential": false,
"due_date": null,
"moved_to_id": null,
"lock_version": null,
"time_estimate": 0,
"closed_at": null,
"last_edited_at": null,
"last_edited_by_id": null,
"group_milestone_id": null,
"milestone": {
"id": 2,
"title": "A group milestone",
"description": "Group-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": 100
},
"label_links": [
{
"id": 11,
"label_id": 2,
"target_id": 1,
"target_type": "Issue",
"created_at": "2017-08-15T18:37:40.795Z",
"updated_at": "2017-08-15T18:37:40.795Z",
"label": {
"id": 2,
"title": "A project label",
"color": "#A8D695",
"project_id": null,
"created_at": "2017-08-15T18:37:19.698Z",
"updated_at": "2017-08-15T18:37:19.698Z",
"template": false,
"description": "",
"group_id": 5,
"type": "ProjectLabel",
"priorities": []
}
}
]
},
{
"id": 3,
"title": "Issue with Epic",
"author_id": 1,
"project_id": 8,
"created_at": "2019-12-08T19:41:11.233Z",
"updated_at": "2019-12-08T19:41:53.194Z",
"position": 0,
"branch_name": null,
"description": "Donec at nulla vitae sem molestie rutrum ut at sem.",
"state": "opened",
"iid": 3,
"updated_by_id": null,
"confidential": false,
"due_date": null,
"moved_to_id": null,
"issue_assignees": [],
"notes": [],
"milestone": {
"id": 2,
"title": "A group milestone",
"description": "Group-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": 100
},
"iteration": {
"created_at": "2022-08-15T12:55:42.607Z",
"updated_at": "2022-08-15T12:56:19.269Z",
"start_date": "2022-08-15",
"due_date": "2022-08-21",
"group_id": 260,
"iid": 5,
"description": "iteration description",
"iterations_cadence": {
"title": "iterations cadence"
}
},
"epic_issue": {
"id": 78,
"relative_position": 1073740323,
"epic": {
"id": 1,
"group_id": 5,
"author_id": 1,
"assignee_id": null,
"iid": 1,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-12-08T19:37:07.098Z",
"updated_at": "2019-12-08T19:43:11.568Z",
"title": "An epic",
"description": null,
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": null,
"relative_position": null,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null,
"milestone_id": null
}
},
"resource_iteration_events": [
{
"user_id": 1,
"created_at": "2022-08-17T13:04:02.495Z",
"action": "add",
"iteration": {
"created_at": "2022-08-15T12:55:42.607Z",
"updated_at": "2022-08-15T12:56:19.269Z",
"start_date": "2022-08-15",
"due_date": "2022-08-21",
"group_id": 260,
"iid": 5,
"description": "iteration description",
"iterations_cadence": {
"title": "iterations cadence"
}
}
}
]
}
],
"snippets": [
],
"hooks": [
]
}

File diff suppressed because it is too large Load Diff

View File

@ -1,954 +0,0 @@
{
"name": "group",
"path": "group",
"owner_id": null,
"created_at": "2019-11-20 17:01:53 UTC",
"updated_at": "2019-11-20 17:05:44 UTC",
"description": "Group Description",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 0,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"saml_discovery_token": "rBKx3ioz",
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"milestones": [
{
"id": 7642,
"title": "v4.0",
"project_id": null,
"description": "Et laudantium enim omnis ea reprehenderit iure.",
"due_date": null,
"created_at": "2019-11-20T17:02:14.336Z",
"updated_at": "2019-11-20T17:02:14.336Z",
"state": "closed",
"iid": 5,
"start_date": null,
"group_id": 4351
},
{
"id": 7641,
"title": "v3.0",
"project_id": null,
"description": "Et repellat culpa nemo consequatur ut reprehenderit.",
"due_date": null,
"created_at": "2019-11-20T17:02:14.323Z",
"updated_at": "2019-11-20T17:02:14.323Z",
"state": "active",
"iid": 4,
"start_date": null,
"group_id": 4351
},
{
"id": 7640,
"title": "v2.0",
"project_id": null,
"description": "Velit cupiditate est neque voluptates iste rem sunt.",
"due_date": null,
"created_at": "2019-11-20T17:02:14.309Z",
"updated_at": "2019-11-20T17:02:14.309Z",
"state": "active",
"iid": 3,
"start_date": null,
"group_id": 4351
},
{
"id": 7639,
"title": "v1.0",
"project_id": null,
"description": "Amet velit repellat ut rerum aut cum.",
"due_date": null,
"created_at": "2019-11-20T17:02:14.296Z",
"updated_at": "2019-11-20T17:02:14.296Z",
"state": "active",
"iid": 2,
"start_date": null,
"group_id": 4351
},
{
"id": 7638,
"title": "v0.0",
"project_id": null,
"description": "Ea quia asperiores ut modi dolorem sunt non numquam.",
"due_date": null,
"created_at": "2019-11-20T17:02:14.282Z",
"updated_at": "2019-11-20T17:02:14.282Z",
"state": "active",
"iid": 1,
"start_date": null,
"group_id": 4351
}
],
"badges": [
{
"id": 10,
"link_url": "https://localhost:3443/%{default_branch}",
"image_url": "https://badge_image.png",
"project_id": null,
"group_id": 4351,
"created_at": "2019-11-20T17:27:02.047Z",
"updated_at": "2019-11-20T17:27:02.047Z",
"type": "GroupBadge"
}
],
"labels": [
{
"id": 23452,
"title": "Bruffefunc",
"color": "#1d2da4",
"project_id": null,
"created_at": "2019-11-20T17:02:20.546Z",
"updated_at": "2019-11-20T17:02:20.546Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23446,
"title": "Cafunc",
"color": "#73ed5b",
"project_id": null,
"created_at": "2019-11-20T17:02:20.526Z",
"updated_at": "2019-11-20T17:02:20.526Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23451,
"title": "Casche",
"color": "#649a75",
"project_id": null,
"created_at": "2019-11-20T17:02:20.544Z",
"updated_at": "2019-11-20T17:02:20.544Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23444,
"title": "Cocell",
"color": "#1b365c",
"project_id": null,
"created_at": "2019-11-20T17:02:20.521Z",
"updated_at": "2019-11-20T17:02:20.521Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23449,
"title": "Packfunc",
"color": "#e33bba",
"project_id": null,
"created_at": "2019-11-20T17:02:20.537Z",
"updated_at": "2019-11-20T17:02:20.537Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23443,
"title": "Panabalt",
"color": "#84f708",
"project_id": null,
"created_at": "2019-11-20T17:02:20.518Z",
"updated_at": "2019-11-20T17:02:20.518Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23447,
"title": "Phierefunc",
"color": "#4ab4a8",
"project_id": null,
"created_at": "2019-11-20T17:02:20.530Z",
"updated_at": "2019-11-20T17:02:20.530Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23445,
"title": "Pons",
"color": "#47f440",
"project_id": null,
"created_at": "2019-11-20T17:02:20.523Z",
"updated_at": "2019-11-20T17:02:20.523Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23448,
"title": "Sosync",
"color": "#110320",
"project_id": null,
"created_at": "2019-11-20T17:02:20.532Z",
"updated_at": "2019-11-20T17:02:20.532Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
},
{
"id": 23450,
"title": "TSL",
"color": "#58796f",
"project_id": null,
"created_at": "2019-11-20T17:02:20.541Z",
"updated_at": "2019-11-20T17:02:20.541Z",
"template": false,
"description": null,
"group_id": 4351,
"type": "GroupLabel",
"priorities": [],
"textColor": "#FFFFFF"
}
],
"boards": [
{
"id": 56,
"project_id": null,
"created_at": "2019-11-20T17:27:16.808Z",
"updated_at": "2019-11-20T17:27:16.808Z",
"name": "Development",
"milestone_id": null,
"group_id": 4351,
"weight": null,
"labels": []
},
{
"id": 57,
"project_id": null,
"created_at": "2019-11-20T17:27:41.118Z",
"updated_at": "2019-11-20T17:27:41.118Z",
"name": "Board!",
"milestone_id": 7638,
"group_id": 4351,
"weight": null,
"labels": []
}
],
"members": [
{
"id": 13766,
"access_level": 30,
"source_id": 4351,
"source_type": "Namespace",
"user_id": 42,
"notification_level": 3,
"created_at": "2019-11-20T17:04:36.184Z",
"updated_at": "2019-11-20T17:04:36.184Z",
"created_by_id": null,
"invite_email": null,
"invite_token": null,
"invite_accepted_at": null,
"requested_at": null,
"expires_at": null,
"ldap": false,
"override": false,
"user": {
"id": 42,
"public_email": "moriah@collinsmurphy.com",
"username": "reported_user_15"
}
},
{
"id": 13765,
"access_level": 40,
"source_id": 4351,
"source_type": "Namespace",
"user_id": 271,
"notification_level": 3,
"created_at": "2019-11-20T17:04:36.044Z",
"updated_at": "2019-11-20T17:04:36.044Z",
"created_by_id": null,
"invite_email": null,
"invite_token": null,
"invite_accepted_at": null,
"requested_at": null,
"expires_at": null,
"ldap": false,
"override": false,
"user": {
"id": 271,
"public_email": "garret@connellystark.ca",
"username": "charlesetta"
}
},
{
"id": 13764,
"access_level": 30,
"source_id": 4351,
"source_type": "Namespace",
"user_id": 206,
"notification_level": 3,
"created_at": "2019-11-20T17:04:35.840Z",
"updated_at": "2019-11-20T17:04:35.840Z",
"created_by_id": null,
"invite_email": null,
"invite_token": null,
"invite_accepted_at": null,
"requested_at": null,
"expires_at": null,
"ldap": false,
"override": false,
"user": {
"id": 206,
"public_email": "margaret.bergnaum@reynolds.us",
"username": "gwendolyn_robel"
}
},
{
"id": 13763,
"access_level": 10,
"source_id": 4351,
"source_type": "Namespace",
"user_id": 39,
"notification_level": 3,
"created_at": "2019-11-20T17:04:35.704Z",
"updated_at": "2019-11-20T17:04:35.704Z",
"created_by_id": null,
"invite_email": null,
"invite_token": null,
"invite_accepted_at": null,
"requested_at": null,
"expires_at": null,
"ldap": false,
"override": false,
"user": {
"id": 39,
"public_email": "alexis_berge@kerlukeklein.us",
"username": "reported_user_12"
}
},
{
"id": 13762,
"access_level": 20,
"source_id": 4351,
"source_type": "Namespace",
"user_id": 1624,
"notification_level": 3,
"created_at": "2019-11-20T17:04:35.566Z",
"updated_at": "2019-11-20T17:04:35.566Z",
"created_by_id": null,
"invite_email": null,
"invite_token": null,
"invite_accepted_at": null,
"requested_at": null,
"expires_at": null,
"ldap": false,
"override": false,
"user": {
"id": 1624,
"public_email": "nakesha.herzog@powlowski.com",
"username": "adriene.mcclure"
}
},
{
"id": 12920,
"access_level": 50,
"source_id": 4351,
"source_type": "Namespace",
"user_id": 1,
"notification_level": 3,
"created_at": "2019-11-20T17:01:53.505Z",
"updated_at": "2019-11-20T17:01:53.505Z",
"created_by_id": null,
"invite_email": null,
"invite_token": null,
"invite_accepted_at": null,
"requested_at": null,
"expires_at": null,
"ldap": false,
"override": false,
"user": {
"id": 1,
"public_email": "admin@example.com",
"username": "root"
}
}
],
"epics": [
{
"id": 13622,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 1,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.754Z",
"updated_at": "2019-11-20T18:38:40.054Z",
"title": "Provident neque consequatur numquam ad laboriosam voluptatem magnam.",
"description": "Fugit nisi est ut numquam quia rerum vitae qui. Et in est aliquid voluptas et ut vitae. In distinctio voluptates ut deleniti iste.\n\nReiciendis eum sunt vero blanditiis at quia. Voluptate eum facilis illum ea distinctio maiores. Doloribus aut nemo ea distinctio.\n\nNihil cum distinctio voluptates quam. Laboriosam distinctio ea accusantium soluta perspiciatis nesciunt impedit. Id qui natus quis minima voluptatum velit ut reprehenderit. Molestiae quia est harum sapiente rem error architecto id. Et minus ipsa et ut ut.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": null,
"relative_position": null,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null,
"notes": [
{
"id": 44170,
"note": "added epic \u00265 as child epic",
"noteable_type": "Epic",
"author_id": 1,
"created_at": "2019-11-20T18:38:40.031Z",
"updated_at": "2019-11-20T18:38:40.035Z",
"project_id": null,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"noteable_id": 13622,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": "ba005d8dd59cd37a4f32406d46e759b08fd15510",
"change_position": null,
"resolved_by_push": null,
"review_id": null,
"type": null,
"author": {
"name": "Administrator"
}
},
{
"id": 44168,
"note": "added epic \u00264 as child epic",
"noteable_type": "Epic",
"author_id": 1,
"created_at": "2019-11-20T18:38:35.669Z",
"updated_at": "2019-11-20T18:38:35.673Z",
"project_id": null,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"noteable_id": 13622,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": "9b49d3b017aadc1876d477b960e6f8efb99ce29f",
"change_position": null,
"resolved_by_push": null,
"review_id": null,
"type": null,
"author": {
"name": "Administrator"
}
},
{
"id": 44166,
"note": "added epic \u00263 as child epic",
"noteable_type": "Epic",
"author_id": 1,
"created_at": "2019-11-20T18:38:30.944Z",
"updated_at": "2019-11-20T18:38:30.948Z",
"project_id": null,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"noteable_id": 13622,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": "cccfe967f48e699a466c87a55a9f8acb00fec1a1",
"change_position": null,
"resolved_by_push": null,
"review_id": null,
"type": null,
"author": {
"name": "Administrator"
}
},
{
"id": 44164,
"note": "added epic \u00262 as child epic",
"noteable_type": "Epic",
"author_id": 1,
"created_at": "2019-11-20T18:38:26.689Z",
"updated_at": "2019-11-20T18:38:26.724Z",
"project_id": null,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"noteable_id": 13622,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": "133f0c3001860fa8d2031e398a65db74477378c4",
"change_position": null,
"resolved_by_push": null,
"review_id": null,
"type": null,
"author": {
"name": "Administrator"
}
}
]
},
{
"id": 13623,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 2,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.769Z",
"updated_at": "2019-11-20T18:38:26.851Z",
"title": "Omnis accusantium commodi voluptas odio illo eum ut.",
"description": "Eius vero et iste amet est voluptatem modi. Doloribus ipsam beatae et ut autem ut animi. Dolor culpa dolor omnis delectus est tempora inventore ab. Optio labore tenetur libero quia provident et quis. Blanditiis architecto sint possimus cum aut adipisci.\n\nDolores quisquam sunt cupiditate unde qui vitae nemo. Odio quas omnis ut nobis. Possimus fugit deserunt quia sed ab numquam veritatis nihil.\n\nQui nemo adipisci magnam perferendis voluptatem modi. Eius enim iure dolores consequuntur eum nobis adipisci. Consequatur architecto et quas deleniti hic id laborum officiis. Enim perferendis quis quasi totam delectus rerum deleniti.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": 13622,
"relative_position": 1073741323,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null,
"parent": {
"id": 13622,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 1,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.754Z",
"updated_at": "2019-11-20T18:38:40.054Z",
"title": "Provident neque consequatur numquam ad laboriosam voluptatem magnam.",
"description": "Fugit nisi est ut numquam quia rerum vitae qui. Et in est aliquid voluptas et ut vitae. In distinctio voluptates ut deleniti iste.\n\nReiciendis eum sunt vero blanditiis at quia. Voluptate eum facilis illum ea distinctio maiores. Doloribus aut nemo ea distinctio.\n\nNihil cum distinctio voluptates quam. Laboriosam distinctio ea accusantium soluta perspiciatis nesciunt impedit. Id qui natus quis minima voluptatum velit ut reprehenderit. Molestiae quia est harum sapiente rem error architecto id. Et minus ipsa et ut ut.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": null,
"relative_position": null,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null
},
"notes": [
{
"id": 44165,
"note": "added epic \u00261 as parent epic",
"noteable_type": "Epic",
"author_id": 1,
"created_at": "2019-11-20T18:38:26.822Z",
"updated_at": "2019-11-20T18:38:26.826Z",
"project_id": null,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"noteable_id": 13623,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": "15f0a7f4ed16a07bc78841e122524bb867edcf86",
"change_position": null,
"resolved_by_push": null,
"review_id": null,
"type": null,
"author": {
"name": "Administrator"
}
}
]
},
{
"id": 13624,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 3,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.783Z",
"updated_at": "2019-11-20T18:38:31.018Z",
"title": "Quis dolore velit possimus eaque aut amet esse voluptate aliquam.",
"description": "Ab veritatis reprehenderit nulla laboriosam et sed asperiores corporis. Est accusantium maxime perferendis et. Omnis a qui voluptates non excepturi.\n\nAdipisci labore maiores dicta sed magnam aut. Veritatis delectus dolorum qui id. Dolorum tenetur quo iure amet. Eveniet reprehenderit dolor ipsam quia ratione quo. Facilis voluptatem vel repellat id illum.\n\nAut et magnam aut minus aspernatur. Fuga quo necessitatibus mollitia maxime quasi. Qui aspernatur quia accusamus est quod. Qui assumenda veritatis dolor non eveniet quibusdam quos qui.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": 13622,
"relative_position": 1073740823,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null,
"parent": {
"id": 13622,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 1,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.754Z",
"updated_at": "2019-11-20T18:38:40.054Z",
"title": "Provident neque consequatur numquam ad laboriosam voluptatem magnam.",
"description": "Fugit nisi est ut numquam quia rerum vitae qui. Et in est aliquid voluptas et ut vitae. In distinctio voluptates ut deleniti iste.\n\nReiciendis eum sunt vero blanditiis at quia. Voluptate eum facilis illum ea distinctio maiores. Doloribus aut nemo ea distinctio.\n\nNihil cum distinctio voluptates quam. Laboriosam distinctio ea accusantium soluta perspiciatis nesciunt impedit. Id qui natus quis minima voluptatum velit ut reprehenderit. Molestiae quia est harum sapiente rem error architecto id. Et minus ipsa et ut ut.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": null,
"relative_position": null,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null
},
"notes": [
{
"id": 44167,
"note": "added epic \u00261 as parent epic",
"noteable_type": "Epic",
"author_id": 1,
"created_at": "2019-11-20T18:38:30.989Z",
"updated_at": "2019-11-20T18:38:30.993Z",
"project_id": null,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"noteable_id": 13624,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": "423ffec14a3ce148c11a802eb1f2613fa8ca9a94",
"change_position": null,
"resolved_by_push": null,
"review_id": null,
"type": null,
"author": {
"name": "Administrator"
}
}
]
},
{
"id": 13625,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 4,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.798Z",
"updated_at": "2019-11-20T18:38:35.765Z",
"title": "Possimus et ut iste temporibus earum cupiditate voluptatem esse assumenda amet.",
"description": "Et at corporis sed id rerum ullam dolore. Odio magnam corporis excepturi neque est. Est accusamus nostrum qui rerum.\n\nEt aut dolores eaque quibusdam aut quas explicabo id. Est necessitatibus praesentium omnis et vero laboriosam et. Sunt in saepe qui laudantium et voluptas.\n\nVelit sunt odit eum omnis beatae eius aut. Dolores commodi qui impedit deleniti et magnam pariatur. Aut odit amet ipsum ea atque. Itaque est ut sunt ullam eum nam.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": 13622,
"relative_position": 1073740323,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null,
"parent": {
"id": 13622,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 1,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.754Z",
"updated_at": "2019-11-20T18:38:40.054Z",
"title": "Provident neque consequatur numquam ad laboriosam voluptatem magnam.",
"description": "Fugit nisi est ut numquam quia rerum vitae qui. Et in est aliquid voluptas et ut vitae. In distinctio voluptates ut deleniti iste.\n\nReiciendis eum sunt vero blanditiis at quia. Voluptate eum facilis illum ea distinctio maiores. Doloribus aut nemo ea distinctio.\n\nNihil cum distinctio voluptates quam. Laboriosam distinctio ea accusantium soluta perspiciatis nesciunt impedit. Id qui natus quis minima voluptatum velit ut reprehenderit. Molestiae quia est harum sapiente rem error architecto id. Et minus ipsa et ut ut.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": null,
"relative_position": null,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null
},
"notes": [
{
"id": 44169,
"note": "added epic \u00261 as parent epic",
"noteable_type": "Epic",
"author_id": 1,
"created_at": "2019-11-20T18:38:35.737Z",
"updated_at": "2019-11-20T18:38:35.741Z",
"project_id": null,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"noteable_id": 13625,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": "5bc3e30d508affafc61de2b4e1d9f21039505cc3",
"change_position": null,
"resolved_by_push": null,
"review_id": null,
"type": null,
"author": {
"name": "Administrator"
}
}
]
},
{
"id": 13626,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 5,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.812Z",
"updated_at": "2019-11-20T18:38:40.101Z",
"title": "Ab deleniti ipsum voluptatem dolor qui quos saepe repellat quo.",
"description": "Sunt minus sunt reiciendis culpa sed excepturi. Aperiam sed quod nemo nesciunt et quia molestias incidunt. Ipsum nam magnam labore eos a molestiae rerum possimus. Sequi autem asperiores voluptas assumenda.\n\nRerum ipsa quia cum ab corrupti omnis. Velit libero et nihil ipsa aut quo rem ipsam. Architecto omnis distinctio sed doloribus perspiciatis consequatur aut et. Fugit consequuntur est minima reiciendis reprehenderit et.\n\nConsequatur distinctio et ut blanditiis perferendis officiis inventore. Alias aut voluptatem in facere. Ut perferendis dolorum hic dolores. Ipsa dolorem soluta at mollitia. Placeat et ea numquam dicta molestias.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": 13622,
"relative_position": 1073739823,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null,
"parent": {
"id": 13622,
"milestone_id": null,
"group_id": 4351,
"author_id": 1,
"assignee_id": null,
"iid": 1,
"updated_by_id": null,
"last_edited_by_id": null,
"lock_version": 0,
"start_date": null,
"end_date": null,
"last_edited_at": null,
"created_at": "2019-11-20T17:02:09.754Z",
"updated_at": "2019-11-20T18:38:40.054Z",
"title": "Provident neque consequatur numquam ad laboriosam voluptatem magnam.",
"description": "Fugit nisi est ut numquam quia rerum vitae qui. Et in est aliquid voluptas et ut vitae. In distinctio voluptates ut deleniti iste.\n\nReiciendis eum sunt vero blanditiis at quia. Voluptate eum facilis illum ea distinctio maiores. Doloribus aut nemo ea distinctio.\n\nNihil cum distinctio voluptates quam. Laboriosam distinctio ea accusantium soluta perspiciatis nesciunt impedit. Id qui natus quis minima voluptatum velit ut reprehenderit. Molestiae quia est harum sapiente rem error architecto id. Et minus ipsa et ut ut.",
"start_date_sourcing_milestone_id": null,
"due_date_sourcing_milestone_id": null,
"start_date_fixed": null,
"due_date_fixed": null,
"start_date_is_fixed": null,
"due_date_is_fixed": null,
"closed_by_id": null,
"closed_at": null,
"parent_id": null,
"relative_position": null,
"state_id": "opened",
"start_date_sourcing_epic_id": null,
"due_date_sourcing_epic_id": null
},
"notes": [
{
"id": 44171,
"note": "added epic \u00261 as parent epic",
"noteable_type": "Epic",
"author_id": 1,
"created_at": "2019-11-20T18:38:40.074Z",
"updated_at": "2019-11-20T18:38:40.077Z",
"project_id": null,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"noteable_id": 13626,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": "a6231acdaef5f4d2e569dfb604f1baf85c49e1a0",
"change_position": null,
"resolved_by_push": null,
"review_id": null,
"type": null,
"author": {
"name": "Administrator"
}
}
]
}
],
"children": []
}

View File

@ -1,154 +0,0 @@
{
"id": 283,
"name": "internal",
"path": "internal",
"owner_id": null,
"created_at": "2020-02-12T16:56:34.924Z",
"updated_at": "2020-02-12T16:56:38.710Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 10,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null,
"children": [
{
"id": 284,
"name": "public",
"path": "public",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 20,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
},
{
"id": 285,
"name": "internal",
"path": "internal",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 10,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
},
{
"id": 286,
"name": "private",
"path": "private",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 0,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
}
]
}

View File

@ -1,154 +0,0 @@
{
"id": 283,
"name": "private",
"path": "private",
"owner_id": null,
"created_at": "2020-02-12T16:56:34.924Z",
"updated_at": "2020-02-12T16:56:38.710Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 0,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null,
"children": [
{
"id": 284,
"name": "public",
"path": "public",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 20,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
},
{
"id": 285,
"name": "internal",
"path": "internal",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 10,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
},
{
"id": 286,
"name": "private",
"path": "private",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 0,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
}
]
}

View File

@ -1,154 +0,0 @@
{
"id": 283,
"name": "public",
"path": "public",
"owner_id": null,
"created_at": "2020-02-12T16:56:34.924Z",
"updated_at": "2020-02-12T16:56:38.710Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 20,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null,
"children": [
{
"id": 284,
"name": "public",
"path": "public",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 20,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
},
{
"id": 285,
"name": "internal",
"path": "internal",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 10,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
},
{
"id": 286,
"name": "private",
"path": "private",
"owner_id": null,
"created_at": "2020-02-12T17:33:00.575Z",
"updated_at": "2020-02-12T17:33:00.575Z",
"description": "",
"avatar": {
"url": null
},
"membership_lock": false,
"share_with_group_lock": false,
"visibility_level": 0,
"request_access_enabled": true,
"ldap_sync_status": "ready",
"ldap_sync_error": null,
"ldap_sync_last_update_at": null,
"ldap_sync_last_successful_update_at": null,
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
"emails_disabled": null,
"max_pages_size": null,
"max_artifacts_size": null,
"mentions_disabled": null
}
]
}

View File

@ -1,3 +0,0 @@
{
"invalid" json
}

View File

@ -1,164 +0,0 @@
{
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
"import_type": "gitlab_project",
"creator_id": 2147483547,
"visibility_level": 10,
"archived": false,
"milestones": [
{
"id": 1,
"title": "A milestone",
"project_id": 8,
"description": "Project-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": null
}
],
"labels": [
{
"id": 2,
"title": "A project label",
"color": "#428bca",
"project_id": 8,
"created_at": "2016-07-22T08:55:44.161Z",
"updated_at": "2016-07-22T08:55:44.161Z",
"template": false,
"description": "",
"type": "ProjectLabel",
"priorities": [
{
"id": 1,
"project_id": 5,
"label_id": 1,
"priority": 1,
"created_at": "2016-10-18T09:35:43.338Z",
"updated_at": "2016-10-18T09:35:43.338Z"
}
]
}
],
"issues": [
{
"id": 1,
"title": "Fugiat est minima quae maxime non similique.",
"assignee_id": null,
"project_id": 8,
"author_id": 1,
"created_at": "2017-07-07T18:13:01.138Z",
"updated_at": "2017-08-15T18:37:40.807Z",
"branch_name": null,
"description": "Quam totam fuga numquam in eveniet.",
"state": "opened",
"iid": 20,
"updated_by_id": 1,
"confidential": false,
"due_date": null,
"moved_to_id": null,
"lock_version": null,
"time_estimate": 0,
"closed_at": null,
"last_edited_at": null,
"last_edited_by_id": null,
"group_milestone_id": null,
"milestone": {
"id": 1,
"title": "A milestone",
"group_id": 8,
"description": "Project-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": null
},
"label_links": [
{
"id": 11,
"label_id": 2,
"target_id": 1,
"target_type": "Issue",
"created_at": "2017-08-15T18:37:40.795Z",
"updated_at": "2017-08-15T18:37:40.795Z",
"label": {
"id": 6,
"title": "Another label",
"color": "#A8D695",
"project_id": null,
"created_at": "2017-08-15T18:37:19.698Z",
"updated_at": "2017-08-15T18:37:19.698Z",
"template": false,
"description": "",
"group_id": null,
"type": "ProjectLabel",
"priorities": []
}
}
],
"notes": [
{
"id": 20,
"note": "created merge request !1 to address this issue",
"noteable_type": "Issue",
"author_id": 1,
"created_at": "2020-03-28T01:37:42.307Z",
"updated_at": "2020-03-28T01:37:42.307Z",
"project_id": 8,
"attachment": {
"url": null
},
"line_code": null,
"commit_id": null,
"system": true,
"st_diff": null,
"updated_by_id": null,
"position": null,
"original_position": null,
"resolved_at": null,
"resolved_by_id": null,
"discussion_id": null,
"change_position": null,
"resolved_by_push": null,
"confidential": null,
"type": null,
"author": {
"name": "Author"
},
"award_emoji": [],
"system_note_metadata": {
"id": 21,
"commit_count": null,
"action": "merge",
"created_at": "2020-03-28T01:37:42.307Z",
"updated_at": "2020-03-28T01:37:42.307Z"
},
"events": []
}
]
}
],
"snippets": [],
"hooks": [],
"custom_attributes": [
{
"id": 201,
"project_id": 5,
"created_at": "2016-06-14T15:01:51.315Z",
"updated_at": "2016-06-14T15:01:51.315Z",
"key": "color",
"value": "red"
},
{
"id": 202,
"project_id": 5,
"created_at": "2016-06-14T15:01:51.315Z",
"updated_at": "2016-06-14T15:01:51.315Z",
"key": "size",
"value": "small"
}
]
}

View File

@ -1,80 +0,0 @@
{
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
"import_type": "gitlab_project",
"creator_id": 2147483547,
"visibility_level": 10,
"archived": false,
"issues": [
{
"id": 1,
"title": "Fugiat est minima quae maxime non similique.",
"assignee_id": null,
"project_id": 8,
"author_id": 1,
"created_at": "2017-07-07T18:13:01.138Z",
"updated_at": "2017-08-15T18:37:40.807Z",
"branch_name": null,
"description": "Quam totam fuga numquam in eveniet.",
"state": "opened",
"iid": 20,
"updated_by_id": 1,
"confidential": false,
"due_date": null,
"moved_to_id": null,
"lock_version": null,
"time_estimate": 0,
"closed_at": null,
"last_edited_at": null,
"last_edited_by_id": null,
"group_milestone_id": null,
"milestone": {
"id": 1,
"title": "Group-level milestone",
"description": "Group-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": 8
}
},
{
"id": 2,
"title": "est minima quae maxime non similique.",
"assignee_id": null,
"project_id": 8,
"author_id": 1,
"created_at": "2017-07-07T18:13:01.138Z",
"updated_at": "2017-08-15T18:37:40.807Z",
"branch_name": null,
"description": "Quam totam fuga numquam in eveniet.",
"state": "opened",
"iid": 21,
"updated_by_id": 1,
"confidential": false,
"due_date": null,
"moved_to_id": null,
"lock_version": null,
"time_estimate": 0,
"closed_at": null,
"last_edited_at": null,
"last_edited_by_id": null,
"group_milestone_id": null,
"milestone": {
"id": 2,
"title": "Another milestone",
"project_id": 8,
"description": "milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": null
}
}
],
"snippets": [],
"hooks": []
}

View File

@ -1,143 +0,0 @@
{
"approvals_before_merge": 0,
"archived": false,
"auto_cancel_pending_pipelines": "enabled",
"autoclose_referenced_issues": true,
"boards": [],
"build_allow_git_fetch": true,
"build_timeout": 3600,
"ci_cd_settings": {
"group_runners_enabled": true
},
"ci_config_path": null,
"ci_pipelines": [
{
"before_sha": "0000000000000000000000000000000000000000",
"committed_at": null,
"config_source": "repository_source",
"created_at": "2020-02-25T12:08:40.615Z",
"duration": 61,
"external_pull_request": {
"created_at": "2020-02-25T12:08:40.478Z",
"id": 59023,
"project_id": 17121868,
"pull_request_iid": 4,
"source_branch": "new-branch",
"source_repository": "liptonshmidt/dotfiles",
"source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
"status": "open",
"target_branch": "master",
"target_repository": "liptonshmidt/dotfiles",
"target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
"updated_at": "2020-02-25T12:08:40.478Z"
},
"failure_reason": null,
"finished_at": "2020-02-25T12:09:44.464Z",
"id": 120842687,
"iid": 8,
"lock_version": 3,
"notes": [],
"project_id": 17121868,
"protected": false,
"ref": "new-branch",
"sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
"source": "external_pull_request_event",
"source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
"stages": [],
"started_at": "2020-02-25T12:08:42.511Z",
"status": "success",
"tag": false,
"target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
"updated_at": "2020-02-25T12:09:44.473Z",
"user_id": 4087087,
"yaml_errors": null
},
{
"before_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
"committed_at": null,
"config_source": "repository_source",
"created_at": "2020-02-25T12:08:37.434Z",
"duration": 57,
"external_pull_request": {
"created_at": "2020-02-25T12:08:40.478Z",
"id": 59023,
"project_id": 17121868,
"pull_request_iid": 4,
"source_branch": "new-branch",
"source_repository": "liptonshmidt/dotfiles",
"source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
"status": "open",
"target_branch": "master",
"target_repository": "liptonshmidt/dotfiles",
"target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
"updated_at": "2020-02-25T12:08:40.478Z"
},
"failure_reason": null,
"finished_at": "2020-02-25T12:09:36.557Z",
"id": 120842675,
"iid": 7,
"lock_version": 3,
"notes": [],
"project_id": 17121868,
"protected": false,
"ref": "new-branch",
"sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
"source": "external_pull_request_event",
"source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
"stages": [],
"started_at": "2020-02-25T12:08:38.682Z",
"status": "success",
"tag": false,
"target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
"updated_at": "2020-02-25T12:09:36.565Z",
"user_id": 4087087,
"yaml_errors": null
}
],
"custom_attributes": [],
"delete_error": null,
"description": "Vim, Tmux and others",
"disable_overriding_approvers_per_merge_request": null,
"external_authorization_classification_label": "",
"external_pull_requests": [
{
"created_at": "2020-02-25T12:08:40.478Z",
"id": 59023,
"project_id": 17121868,
"pull_request_iid": 4,
"source_branch": "new-branch",
"source_repository": "liptonshmidt/dotfiles",
"source_sha": "122bc4bbad5b6448089cacbe16d0bdc3534e7eda",
"status": "open",
"target_branch": "master",
"target_repository": "liptonshmidt/dotfiles",
"target_sha": "86ebe754fa12216e5c0d9d95890936e2fcc62392",
"updated_at": "2020-02-25T12:08:40.478Z"
}
],
"external_webhook_token": "D3mVYFzZkgZ5kMfcW_wx",
"issues": [],
"labels": [],
"milestones": [],
"pipeline_schedules": [],
"project_feature": {
"builds_access_level": 20,
"created_at": "2020-02-25T11:20:09.925Z",
"forking_access_level": 20,
"id": 17494715,
"issues_access_level": 0,
"merge_requests_access_level": 0,
"pages_access_level": 20,
"project_id": 17121868,
"repository_access_level": 20,
"snippets_access_level": 0,
"updated_at": "2020-02-25T11:20:10.376Z",
"wiki_access_level": 0
},
"public_builds": true,
"releases": [],
"shared_runners_enabled": true,
"snippets": [],
"triggers": [],
"visibility_level": 20
}

View File

@ -1,37 +0,0 @@
{
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
"import_type": "gitlab_project",
"creator_id": 2147483547,
"visibility_level": 10,
"archived": false,
"milestones": [
{
"id": 1,
"title": null,
"project_id": 8,
"description": 123,
"due_date": null,
"created_at": "NOT A DATE",
"updated_at": "NOT A DATE",
"state": "active",
"iid": 1,
"group_id": null
},
{
"id": 42,
"title": "A valid milestone",
"project_id": 8,
"description": "Project-level milestone",
"due_date": null,
"created_at": "2016-06-14T15:02:04.415Z",
"updated_at": "2016-06-14T15:02:04.415Z",
"state": "active",
"iid": 1,
"group_id": null
}
],
"labels": [],
"issues": [],
"snippets": [],
"hooks": []
}

View File

@ -4,24 +4,28 @@ import Diagram from '~/content_editor/extensions/diagram';
import Frontmatter from '~/content_editor/extensions/frontmatter';
import Heading from '~/content_editor/extensions/heading';
import Bold from '~/content_editor/extensions/bold';
import Italic from '~/content_editor/extensions/italic';
import { VARIANT_DANGER } from '~/alert';
import eventHubFactory from '~/helpers/event_hub_factory';
import { ALERT_EVENT } from '~/content_editor/constants';
import waitForPromises from 'helpers/wait_for_promises';
import MarkdownSerializer from '~/content_editor/services/markdown_serializer';
import { createTestEditor, createDocBuilder, waitUntilNextDocTransaction } from '../test_utils';
const CODE_BLOCK_HTML = '<pre class="js-syntax-highlight" lang="javascript">var a = 2;</pre>';
const DIAGRAM_HTML =
'<img data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,WzxmcmFtZT5EZWNvcmF0b3IgcGF0dGVybl0=">';
const FRONTMATTER_HTML = '<pre lang="yaml" data-lang-params="frontmatter">key: value</pre>';
const PARAGRAPH_HTML = '<p>Just a regular paragraph</p>';
const PARAGRAPH_HTML = '<p>Some text with <strong>bold</strong> and <em>italic</em> text.</p>';
describe('content_editor/extensions/paste_markdown', () => {
let tiptapEditor;
let doc;
let p;
let bold;
let italic;
let heading;
let codeBlock;
let renderMarkdown;
let eventHub;
const defaultData = { 'text/plain': '**bold text**' };
@ -35,28 +39,36 @@ describe('content_editor/extensions/paste_markdown', () => {
tiptapEditor = createTestEditor({
extensions: [
Bold,
Italic,
CodeBlockHighlight,
Diagram,
Frontmatter,
Heading,
PasteMarkdown.configure({ renderMarkdown, eventHub }),
PasteMarkdown.configure({ renderMarkdown, eventHub, serializer: new MarkdownSerializer() }),
],
});
({
builders: { doc, p, bold, heading },
builders: { doc, p, bold, italic, heading, codeBlock },
} = createDocBuilder({
tiptapEditor,
names: {
bold: { markType: Bold.name },
italic: { markType: Italic.name },
heading: { nodeType: Heading.name },
codeBlock: { nodeType: CodeBlockHighlight.name },
},
}));
});
const buildClipboardEvent = ({ data = {}, types = ['text/plain'] } = {}) => {
return Object.assign(new Event('paste'), {
clipboardData: { types, getData: jest.fn((type) => data[type] || defaultData[type]) },
const buildClipboardEvent = ({ eventName = 'paste', data = {}, types = ['text/plain'] } = {}) => {
return Object.assign(new Event(eventName), {
clipboardData: {
types,
getData: jest.fn((type) => data[type] || defaultData[type]),
setData: jest.fn(),
clearData: jest.fn(),
},
});
};
@ -80,13 +92,13 @@ describe('content_editor/extensions/paste_markdown', () => {
};
it.each`
types | data | handled | desc
${['text/plain']} | ${{}} | ${true} | ${'handles plain text'}
${['text/plain', 'text/html']} | ${{}} | ${false} | ${'doesnt handle html format'}
${['text/plain', 'text/html', 'vscode-editor-data']} | ${{ 'vscode-editor-data': '{ "mode": "markdown" }' }} | ${true} | ${'handles vscode markdown'}
${['text/plain', 'text/html', 'vscode-editor-data']} | ${{ 'vscode-editor-data': '{ "mode": "ruby" }' }} | ${false} | ${'doesnt vscode code snippet'}
`('$desc', async ({ types, handled, data }) => {
expect(await triggerPasteEventHandler(buildClipboardEvent({ types, data }))).toBe(handled);
types | data | formatDesc
${['text/plain']} | ${{}} | ${'plain text'}
${['text/plain', 'text/html']} | ${{}} | ${'html format'}
${['text/plain', 'text/html', 'vscode-editor-data']} | ${{ 'vscode-editor-data': '{ "mode": "markdown" }' }} | ${'vscode markdown'}
${['text/plain', 'text/html', 'vscode-editor-data']} | ${{ 'vscode-editor-data': '{ "mode": "ruby" }' }} | ${'vscode snippet'}
`('handles $formatDesc', async ({ types, data }) => {
expect(await triggerPasteEventHandler(buildClipboardEvent({ types, data }))).toBe(true);
});
it.each`
@ -101,6 +113,45 @@ describe('content_editor/extensions/paste_markdown', () => {
expect(await triggerPasteEventHandler(buildClipboardEvent())).toBe(handled);
});
describe.each`
eventName | expectedDoc
${'cut'} | ${() => doc(p())}
${'copy'} | ${() => doc(p('Some text with ', bold('bold'), ' and ', italic('italic'), ' text.'))}
`('when $eventName event is triggered', ({ eventName, expectedDoc }) => {
let event;
beforeEach(() => {
event = buildClipboardEvent({ eventName });
jest.spyOn(event, 'preventDefault');
jest.spyOn(event, 'stopPropagation');
tiptapEditor.commands.insertContent(PARAGRAPH_HTML);
tiptapEditor.commands.selectAll();
tiptapEditor.view.dispatchEvent(event);
});
it('prevents default', () => {
expect(event.preventDefault).toHaveBeenCalled();
expect(event.stopPropagation).toHaveBeenCalled();
});
it('sets the clipboard data', () => {
expect(event.clipboardData.setData).toHaveBeenCalledWith(
'text/plain',
'Some text with bold and italic text.',
);
expect(event.clipboardData.setData).toHaveBeenCalledWith('text/html', PARAGRAPH_HTML);
expect(event.clipboardData.setData).toHaveBeenCalledWith(
'text/x-gfm',
'Some text with **bold** and _italic_ text.',
);
});
it('modifies the document', () => {
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc().toJSON());
});
});
describe('when pasting raw markdown source', () => {
describe('when rendering markdown succeeds', () => {
beforeEach(() => {
@ -162,6 +213,97 @@ describe('content_editor/extensions/paste_markdown', () => {
});
});
describe('when pasting html content', () => {
it('strips out any stray div, pre, span tags', async () => {
renderMarkdown.mockResolvedValueOnce(
'<div><span dir="auto"><strong>bold text</strong></span></div><pre><code>some code</code></pre>',
);
const expectedDoc = doc(p(bold('bold text')), p('some code'));
await triggerPasteEventHandlerAndWaitForTransaction(
buildClipboardEvent({
types: ['text/html'],
data: {
'text/html':
'<div><span dir="auto"><strong>bold text</strong></span></div><pre><code>some code</code></pre>',
},
}),
);
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
});
});
describe('when pasting text/x-gfm', () => {
it('processes the content as markdown, even if html content exists', async () => {
renderMarkdown.mockResolvedValueOnce('<strong>bold text</strong>');
const expectedDoc = doc(p(bold('bold text')));
await triggerPasteEventHandlerAndWaitForTransaction(
buildClipboardEvent({
types: ['text/x-gfm'],
data: {
'text/x-gfm': '**bold text**',
'text/plain': 'irrelevant text',
'text/html': '<div>some random irrelevant html</div>',
},
}),
);
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
});
});
describe('when pasting vscode-editor-data', () => {
it('pastes the content as a code block', async () => {
renderMarkdown.mockResolvedValueOnce(
'<div class="gl-relative markdown-code-block js-markdown-code">&#x000A;<pre data-sourcepos="1:1-3:3" data-canonical-lang="ruby" class="code highlight js-syntax-highlight language-ruby" lang="ruby" v-pre="true"><code><span id="LC1" class="line" lang="ruby"><span class="nb">puts</span> <span class="s2">"Hello World"</span></span></code></pre>&#x000A;<copy-code></copy-code>&#x000A;</div>',
);
const expectedDoc = doc(
codeBlock(
{ language: 'ruby', class: 'code highlight js-syntax-highlight language-ruby' },
'puts "Hello World"',
),
);
await triggerPasteEventHandlerAndWaitForTransaction(
buildClipboardEvent({
types: ['vscode-editor-data', 'text/plain', 'text/html'],
data: {
'vscode-editor-data': '{ "version": 1, "mode": "ruby" }',
'text/plain': 'puts "Hello World"',
'text/html':
'<meta charset=\'utf-8\'><div style="color: #d4d4d4;background-color: #1e1e1e;font-family: \'Fira Code\', Menlo, Monaco, \'Courier New\', monospace, Menlo, Monaco, \'Courier New\', monospace;font-weight: normal;font-size: 14px;line-height: 21px;white-space: pre;"><div><span style="color: #dcdcaa;">puts</span><span style="color: #d4d4d4;"> </span><span style="color: #ce9178;">"Hello world"</span></div></div>',
},
}),
);
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
});
it('pastes as regular markdown if language is markdown', async () => {
renderMarkdown.mockResolvedValueOnce('<p><strong>bold text</strong></p>');
const expectedDoc = doc(p(bold('bold text')));
await triggerPasteEventHandlerAndWaitForTransaction(
buildClipboardEvent({
types: ['vscode-editor-data', 'text/plain', 'text/html'],
data: {
'vscode-editor-data': '{ "version": 1, "mode": "markdown" }',
'text/plain': '**bold text**',
'text/html': '<p><strong>bold text</strong></p>',
},
}),
);
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
});
});
describe('when rendering markdown fails', () => {
beforeEach(() => {
renderMarkdown.mockRejectedValueOnce();

View File

@ -30,7 +30,7 @@ import TaskList from '~/content_editor/extensions/task_list';
import TaskItem from '~/content_editor/extensions/task_item';
import Video from '~/content_editor/extensions/video';
import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
import markdownSerializer from '~/content_editor/services/markdown_serializer';
import MarkdownSerializer from '~/content_editor/services/markdown_serializer';
import { SAFE_VIDEO_EXT, SAFE_AUDIO_EXT, DIAGRAM_LANGUAGES } from '~/content_editor/constants';
import { createTestEditor, createDocBuilder } from './test_utils';
@ -158,7 +158,7 @@ describe('Client side Markdown processing', () => {
};
const serialize = (document) =>
markdownSerializer({}).serialize({
new MarkdownSerializer().serialize({
doc: document,
pristineDoc: document,
});

View File

@ -26,6 +26,8 @@ import Link from '~/content_editor/extensions/link';
import ListItem from '~/content_editor/extensions/list_item';
import OrderedList from '~/content_editor/extensions/ordered_list';
import Paragraph from '~/content_editor/extensions/paragraph';
import Reference from '~/content_editor/extensions/reference';
import ReferenceLabel from '~/content_editor/extensions/reference_label';
import ReferenceDefinition from '~/content_editor/extensions/reference_definition';
import Sourcemap from '~/content_editor/extensions/sourcemap';
import Strike from '~/content_editor/extensions/strike';
@ -35,7 +37,7 @@ import TableHeader from '~/content_editor/extensions/table_header';
import TableRow from '~/content_editor/extensions/table_row';
import TaskItem from '~/content_editor/extensions/task_item';
import TaskList from '~/content_editor/extensions/task_list';
import markdownSerializer from '~/content_editor/services/markdown_serializer';
import MarkdownSerializer from '~/content_editor/services/markdown_serializer';
import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
import { createTiptapEditor, createDocBuilder } from '../test_utils';
@ -76,6 +78,8 @@ const {
orderedList,
paragraph,
referenceDefinition,
reference,
referenceLabel,
strike,
table,
tableCell,
@ -116,6 +120,8 @@ const {
orderedList: { nodeType: OrderedList.name },
paragraph: { nodeType: Paragraph.name },
referenceDefinition: { nodeType: ReferenceDefinition.name },
reference: { nodeType: Reference.name },
referenceLabel: { nodeType: ReferenceLabel.name },
strike: { markType: Strike.name },
table: { nodeType: Table.name },
tableCell: { nodeType: TableCell.name },
@ -134,7 +140,7 @@ const {
});
const serialize = (...content) =>
markdownSerializer({}).serialize({
new MarkdownSerializer().serialize({
doc: doc(...content),
});
@ -281,6 +287,77 @@ hi
).toBe('![GitLab][gitlab-url]');
});
it('correctly serializes references', () => {
expect(
serialize(
paragraph(
reference({
referenceType: 'issue',
originalText: '#123',
href: '/gitlab-org/gitlab-test/-/issues/123',
text: '#123',
}),
),
),
).toBe('#123');
});
it('correctly renders a reference label', () => {
expect(
serialize(
paragraph(
referenceLabel({
referenceType: 'label',
originalText: '~foo',
href: '/gitlab-org/gitlab-test/-/labels/foo',
text: '~foo',
}),
),
),
).toBe('~foo');
});
it('correctly renders a reference label without originalText', () => {
expect(
serialize(
paragraph(
referenceLabel({
referenceType: 'label',
href: '/gitlab-org/gitlab-test/-/labels/foo',
text: 'Foo Bar',
}),
),
),
).toBe('~"Foo Bar"');
});
it('ensures spaces between multiple references', () => {
expect(
serialize(
paragraph(
reference({
referenceType: 'issue',
originalText: '#123',
href: '/gitlab-org/gitlab-test/-/issues/123',
text: '#123',
}),
referenceLabel({
referenceType: 'label',
originalText: '~foo',
href: '/gitlab-org/gitlab-test/-/labels/foo',
text: '~foo',
}),
reference({
referenceType: 'issue',
originalText: '#456',
href: '/gitlab-org/gitlab-test/-/issues/456',
text: '#456',
}),
),
),
).toBe('#123 ~foo #456');
});
it.each`
src
${'data:image/png;base64,iVBORw0KGgoAAAAN'}
@ -1485,7 +1562,7 @@ paragraph
editAction(document);
const serialized = markdownSerializer({}).serialize({
const serialized = new MarkdownSerializer().serialize({
pristineDoc: document,
doc: tiptapEditor.state.doc,
});

View File

@ -37,6 +37,8 @@ import Link from '~/content_editor/extensions/link';
import ListItem from '~/content_editor/extensions/list_item';
import OrderedList from '~/content_editor/extensions/ordered_list';
import ReferenceDefinition from '~/content_editor/extensions/reference_definition';
import Reference from '~/content_editor/extensions/reference';
import ReferenceLabel from '~/content_editor/extensions/reference_label';
import Strike from '~/content_editor/extensions/strike';
import Table from '~/content_editor/extensions/table';
import TableCell from '~/content_editor/extensions/table_cell';
@ -291,6 +293,8 @@ export const createTiptapEditor = (extensions = []) =>
ListItem,
OrderedList,
ReferenceDefinition,
Reference,
ReferenceLabel,
Strike,
Table,
TableCell,

View File

@ -63,7 +63,16 @@ describe('IssuableHeaderWarnings', () => {
});
it(`${renderTestMessage(lockStatus)} the locked icon`, () => {
expect(findLockedIcon().exists()).toBe(lockStatus);
const lockedIcon = findLockedIcon();
expect(lockedIcon.exists()).toBe(lockStatus);
if (lockStatus) {
expect(lockedIcon.attributes('title')).toBe(
`This ${issuableType.replace('_', ' ')} is locked. Only project members can comment.`,
);
expect(getBinding(lockedIcon.element, 'gl-tooltip')).not.toBeUndefined();
}
});
it(`${renderTestMessage(confidentialStatus)} the confidential icon`, () => {

View File

@ -397,7 +397,16 @@ describe('Issuable output', () => {
`('$title', async ({ isLocked }) => {
await wrapper.setProps({ isLocked });
expect(findLockedBadge().exists()).toBe(isLocked);
const lockedBadge = findLockedBadge();
expect(lockedBadge.exists()).toBe(isLocked);
if (isLocked) {
expect(lockedBadge.attributes('title')).toBe(
'This issue is locked. Only project members can comment.',
);
expect(getBinding(lockedBadge.element, 'gl-tooltip')).not.toBeUndefined();
}
});
it.each`

View File

@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CommandAutocompleteItem should render user item 1`] = `
<div
class="gl-display-flex gl-align-items-center"
>
<gl-icon-stub
class="gl-mr-3"
name="users"
size="16"
/>
<span
class="gl-text-gray-900"
>
Manage &gt; Activity
</span>
</div>
`;

View File

@ -0,0 +1,25 @@
import { shallowMount } from '@vue/test-utils';
import CommandAutocompleteItem from '~/super_sidebar/components/global_search/command_palette/command_autocomplete_item.vue';
import { linksReducer } from '~/super_sidebar/components/global_search/command_palette/utils';
import { LINKS } from './mock_data';
describe('CommandAutocompleteItem', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(CommandAutocompleteItem, {
propsData: {
command: LINKS.reduce(linksReducer, [])[1],
searchQuery: 'root',
},
});
};
beforeEach(() => {
createComponent();
});
it('should render user item', () => {
expect(wrapper.element).toMatchSnapshot();
});
});

View File

@ -5,21 +5,19 @@ import MockAdapter from 'axios-mock-adapter';
import CommandPaletteItems from '~/super_sidebar/components/global_search/command_palette/command_palette_items.vue';
import {
COMMAND_HANDLE,
COMMANDS_GROUP_TITLE,
USERS_GROUP_TITLE,
USER_HANDLE,
} from '~/super_sidebar/components/global_search/command_palette/constants';
import { userMapper } from '~/super_sidebar/components/global_search/command_palette/utils';
import {
userMapper,
linksReducer,
} from '~/super_sidebar/components/global_search/command_palette/utils';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import waitForPromises from 'helpers/wait_for_promises';
import { COMMANDS, USERS } from './mock_data';
import { COMMANDS, LINKS, USERS } from './mock_data';
const commands = COMMANDS.map(({ text, href, keywords }) => ({
text,
href,
keywords: keywords.join(''),
}));
const links = LINKS.reduce(linksReducer, []);
describe('CommandPaletteItems', () => {
let wrapper;
@ -36,7 +34,8 @@ describe('CommandPaletteItems', () => {
GlDisclosureDropdownItem,
},
provide: {
commandPaletteData: COMMANDS,
commandPaletteCommands: COMMANDS,
commandPaletteLinks: LINKS,
},
});
};
@ -45,24 +44,31 @@ describe('CommandPaletteItems', () => {
const findGroup = () => wrapper.findComponent(GlDisclosureDropdownGroup);
const findLoader = () => wrapper.findComponent(GlLoadingIcon);
describe('COMMANDS', () => {
describe('COMMANDS & LINKS', () => {
it('renders all commands initially', () => {
createComponent();
expect(findItems()).toHaveLength(COMMANDS.length);
const commandGroup = COMMANDS[0];
expect(findItems()).toHaveLength(commandGroup.items.length);
expect(findGroup().props('group')).toEqual({
name: COMMANDS_GROUP_TITLE,
items: commands,
name: commandGroup.name,
items: commandGroup.items,
});
});
describe('with search query', () => {
it('should filter by the search query', async () => {
it('should filter comamnds and links by the search query', async () => {
jest.spyOn(fuzzaldrinPlus, 'filter');
createComponent({ searchQuery: 'mr' });
const searchQuery = 'todo';
await wrapper.setProps({ searchQuery });
const commandGroup = COMMANDS[0];
expect(fuzzaldrinPlus.filter).toHaveBeenCalledWith(
commands,
commandGroup.items,
searchQuery,
expect.objectContaining({ key: 'text' }),
);
expect(fuzzaldrinPlus.filter).toHaveBeenCalledWith(
links,
searchQuery,
expect.objectContaining({ key: 'keywords' }),
);

View File

@ -1,18 +1,63 @@
export const COMMANDS = [
{
text: 'New project/repository',
href: '/projects/new',
keywords: ['new', 'project', 'repository'],
name: 'Global',
items: [
{
text: 'New project/repository',
href: '/projects/new',
keywords: ['new', 'project', 'repository'],
},
{
text: 'New group',
href: '/groups/new',
keywords: ['new', 'group'],
},
{
text: 'New snippet',
href: '/-/snippets/new',
keywords: ['new', 'snippet'],
},
],
},
];
export const LINKS = [
{
text: 'New group',
href: '/groups/new',
keywords: ['new', 'group'],
},
{
text: 'New snippet',
href: '/-/snippets/new',
keywords: ['new', 'snippet'],
title: 'Manage',
icon: 'users',
link: '/flightjs/Flight/activity',
is_active: false,
pill_count: null,
items: [
{
id: 'activity',
title: 'Activity',
icon: null,
link: '/flightjs/Flight/activity',
pill_count: null,
link_classes: 'shortcuts-project-activity',
is_active: false,
},
{
id: 'members',
title: 'Members',
icon: null,
link: '/flightjs/Flight/-/project_members',
pill_count: null,
link_classes: null,
is_active: false,
},
{
id: 'labels',
title: 'Labels',
icon: null,
link: '/flightjs/Flight/-/labels',
pill_count: null,
link_classes: null,
is_active: false,
},
],
separated: false,
},
];

View File

@ -1,8 +1,5 @@
import {
userMapper,
commandMapper,
} from '~/super_sidebar/components/global_search/command_palette/utils';
import { COMMANDS, USERS } from './mock_data';
import { userMapper } from '~/super_sidebar/components/global_search/command_palette/utils';
import { USERS } from './mock_data';
describe('userMapper', () => {
it('should transform users response', () => {
@ -16,14 +13,3 @@ describe('userMapper', () => {
});
});
});
describe('commandMapper', () => {
it('should transform commands response', () => {
const command = COMMANDS[0];
expect(commandMapper(command)).toEqual({
href: command.href,
text: command.text,
keywords: command.keywords.join(''),
});
});
});

View File

@ -4,6 +4,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import MarkdownDrawer, { cache } from '~/vue_shared/components/markdown_drawer/markdown_drawer.vue';
import { getRenderedMarkdown } from '~/vue_shared/components/markdown_drawer/utils/fetch';
import { contentTop } from '~/lib/utils/common_utils';
import { DRAWER_Z_INDEX } from '~/lib/utils/constants';
jest.mock('~/vue_shared/components/markdown_drawer/utils/fetch', () => ({
getRenderedMarkdown: jest.fn().mockReturnValue({
@ -55,6 +56,10 @@ describe('MarkdownDrawer', () => {
expect(findDrawerTitle().text()).toBe('test title test');
expect(findDrawerBody().text()).toBe('test body');
});
it(`has proper z-index set for the drawer component`, () => {
expect(findDrawer().attributes('zindex')).toBe(DRAWER_Z_INDEX.toString());
});
});
describe.each`

View File

@ -319,17 +319,6 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do
)
end
it 'returns command palette items', :use_clean_rails_memory_store_caching do
expect(subject[:command_palette_commands]).to match_array([
{ href: "/projects/new",
text: "New project/repository", keywords: [_('Create a new project/repository')] },
{ href: "/groups/new", text: "New group",
keywords: ['Create a new group'] },
{ href: "/-/snippets/new", text: "New snippet",
keywords: ['Create a new snippet'] }
])
end
describe 'current context' do
context 'when current context is a project' do
let_it_be(:project) { build(:project) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
RSpec.describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace, feature_category: :continuous_delivery do
describe '#unmet?' do
let(:build) { create(:ci_build) }
@ -17,15 +17,13 @@ RSpec.describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
end
context 'build has a deployment' do
let!(:deployment) { create(:deployment, deployable: build, cluster: cluster) }
context 'and a cluster to deploy to' do
let(:cluster) { create(:cluster, :group) }
let!(:deployment) { create(:deployment, :on_cluster, deployable: build) }
it { is_expected.to be_truthy }
context 'and the cluster is not managed' do
let(:cluster) { create(:cluster, :not_managed, projects: [build.project]) }
let!(:deployment) { create(:deployment, :on_cluster_not_managed, deployable: build) }
it { is_expected.to be_falsey }
end
@ -63,8 +61,8 @@ RSpec.describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
subject { prerequisite.complete! }
context 'completion is required' do
let(:cluster) { create(:cluster, :group) }
let(:deployment) { create(:deployment, cluster: cluster) }
let(:cluster) { deployment.cluster }
let(:deployment) { create(:deployment, :on_cluster) }
let(:service) { double(execute: true) }
let(:kubernetes_namespace) { double }
@ -84,12 +82,12 @@ RSpec.describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
it 'creates a namespace using a new record' do
expect(Clusters::BuildKubernetesNamespaceService)
.to receive(:new)
.with(cluster, environment: deployment.environment)
.with(deployment.cluster, environment: deployment.environment)
.and_return(namespace_builder)
expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService)
.to receive(:new)
.with(cluster: cluster, kubernetes_namespace: kubernetes_namespace)
.with(cluster: deployment.cluster, kubernetes_namespace: kubernetes_namespace)
.and_return(service)
expect(service).to receive(:execute).once
@ -112,12 +110,12 @@ RSpec.describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
it 'creates a namespace' do
expect(Clusters::BuildKubernetesNamespaceService)
.to receive(:new)
.with(cluster, environment: deployment.environment)
.with(deployment.cluster, environment: deployment.environment)
.and_return(namespace_builder)
expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService)
.to receive(:new)
.with(cluster: cluster, kubernetes_namespace: kubernetes_namespace)
.with(cluster: deployment.cluster, kubernetes_namespace: kubernetes_namespace)
.and_return(service)
expect(service).to receive(:execute).once
@ -150,7 +148,7 @@ RSpec.describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
expect(Clusters::Kubernetes::CreateOrUpdateNamespaceService)
.to receive(:new)
.with(cluster: cluster, kubernetes_namespace: kubernetes_namespace)
.with(cluster: deployment.cluster, kubernetes_namespace: kubernetes_namespace)
.and_return(service)
subject

View File

@ -12,7 +12,7 @@ RSpec.describe Gitlab::ErrorTracking::ErrorRepository::OpenApiStrategy do
before do
# Disabled in spec_helper by default thus we need to enable it here.
stub_feature_flags(use_click_house_database_for_error_tracking: true)
stub_feature_flags(gitlab_error_tracking: true)
end
shared_examples 'exception logging' do

View File

@ -7,7 +7,7 @@ require 'spec_helper'
# Fixture JSONs we use for testing Import such as
# `spec/fixtures/lib/gitlab/import_export/complex/project.json`
# should include these relations being non-empty.
RSpec.describe 'Test coverage of the Project Import' do
RSpec.describe 'Test coverage of the Project Import', feature_category: :importers do
include ConfigurationHelper
# `muted_relations` is a technical debt.
@ -18,7 +18,6 @@ RSpec.describe 'Test coverage of the Project Import' do
let(:muted_relations) do
%w[
project.milestones.events.push_event_payload
project.issues.events
project.issues.events.push_event_payload
project.issues.notes.events
project.issues.notes.events.push_event_payload
@ -53,19 +52,23 @@ RSpec.describe 'Test coverage of the Project Import' do
project.boards.lists.label.priorities
project.service_desk_setting
project.security_setting
project.push_rule
project.approval_rules
project.approval_rules.approval_project_rules_protected_branches
project.approval_rules.approval_project_rules_users
].freeze
end
# A list of JSON fixture files we use to test Import.
# Most of the relations are present in `complex/project.json`
# A list of project tree fixture files we use to test Import.
# Most of the relations are present in `complex/tree`
# which is our main fixture.
let(:project_json_fixtures) do
let(:project_tree_fixtures) do
[
'spec/fixtures/lib/gitlab/import_export/complex/project.json',
'spec/fixtures/lib/gitlab/import_export/group/project.json',
'spec/fixtures/lib/gitlab/import_export/light/project.json',
'spec/fixtures/lib/gitlab/import_export/milestone-iid/project.json',
'spec/fixtures/lib/gitlab/import_export/designs/project.json'
'spec/fixtures/lib/gitlab/import_export/complex/tree',
'spec/fixtures/lib/gitlab/import_export/group/tree',
'spec/fixtures/lib/gitlab/import_export/light/tree',
'spec/fixtures/lib/gitlab/import_export/milestone-iid/tree',
'spec/fixtures/lib/gitlab/import_export/designs/tree'
].freeze
end
@ -82,16 +85,30 @@ RSpec.describe 'Test coverage of the Project Import' do
end
def tested_relations
project_json_fixtures.flat_map(&method(:relations_from_json)).to_set
project_tree_fixtures.flat_map(&method(:relations_from_tree)).to_set
end
def relations_from_json(json_file)
json = Gitlab::Json.parse(File.read(json_file))
def relations_from_tree(json_tree_path)
json = convert_tree_to_json(json_tree_path)
[].tap { |res| gather_relations({ project: json }, res, []) }
.map { |relation_names| relation_names.join('.') }
end
def convert_tree_to_json(json_tree_path)
json = Gitlab::Json.parse(File.read(File.join(json_tree_path, 'project.json')))
Dir["#{json_tree_path}/project/*.ndjson"].each do |ndjson|
relation_name = File.basename(ndjson, '.ndjson')
json[relation_name] = []
File.foreach(ndjson) do |line|
json[relation_name] << Gitlab::Json.parse(line)
end
end
json
end
def gather_relations(item, res, path)
case item
when Hash
@ -112,7 +129,7 @@ RSpec.describe 'Test coverage of the Project Import' do
These relations seem to be added recently and
they expected to be covered in our Import specs: #{not_tested_relations}.
To do that, expand one of the files listed in `project_json_fixtures`
To do that, expand one of the files listed in `project_tree_fixtures`
(or expand the list if you consider adding a new fixture file).
After that, add a new spec into

View File

@ -1,24 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::ImportExport::LegacyRelationTreeSaver do
let(:exportable) { create(:group) }
let(:relation_tree_saver) { described_class.new }
let(:tree) { {} }
describe '#serialize' do
let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) }
it 'uses FastHashSerializer' do
expect(Gitlab::ImportExport::FastHashSerializer)
.to receive(:new)
.with(exportable, tree)
.and_return(serializer)
expect(serializer).to receive(:execute)
relation_tree_saver.serialize(exportable, tree)
end
end
end

View File

@ -182,7 +182,7 @@ RSpec.describe Gitlab::Utils::UsageData do
end
it 'counts over joined relations' do
expect(described_class.estimate_batch_distinct_count(model.joins(:build), "ci_builds.name")).to eq(ci_builds_estimated_cardinality)
expect(described_class.estimate_batch_distinct_count(model.joins(:build), "#{Ci::Build.table_name}.name")).to eq(ci_builds_estimated_cardinality)
end
it 'counts with :column field with batch_size of 50K' do

View File

@ -170,7 +170,7 @@ RSpec.describe Emails::MergeRequests do
end
describe "#merge_when_pipeline_succeeds_email" do
let(:title) { "Merge request #{merge_request.to_reference} was scheduled to merge after pipeline succeeds by #{current_user.name}" }
let(:title) { "Merge request #{merge_request.to_reference} was set to auto-merge by #{current_user.name}" }
subject { Notify.merge_when_pipeline_succeeds_email(recipient.id, merge_request.id, current_user.id) }

View File

@ -188,8 +188,15 @@ RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching,
context 'cluster has multiple successful deployment with environment' do
let!(:environment) { create(:environment) }
let!(:deployment) { create(:deployment, :success, cluster: cluster, environment: environment) }
let!(:deployment_2) { create(:deployment, :success, cluster: cluster, environment: environment) }
let!(:deployment) { create(:deployment, :on_cluster, :success, environment: environment) }
let!(:deployment_2) { create(:deployment, :on_cluster, :success, environment: environment) }
before do
deployment.deployment_cluster.update!(cluster: cluster)
deployment_2.deployment_cluster.update!(cluster: cluster)
deployment.reload
deployment_2.reload
end
it { is_expected.to include(cluster) }
@ -200,9 +207,9 @@ RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching,
context 'cluster has only failed deployment with environment' do
let!(:environment) { create(:environment) }
let!(:deployment) { create(:deployment, :failed, cluster: cluster, environment: environment) }
let!(:deployment) { create(:deployment, :failed, :on_cluster, environment: environment) }
it { is_expected.not_to include(cluster) }
it { is_expected.not_to include(deployment.cluster) }
end
context 'cluster does not have any deployment' do

View File

@ -7,7 +7,6 @@ RSpec.describe Deployment, feature_category: :continuous_delivery do
it { is_expected.to belong_to(:project).required }
it { is_expected.to belong_to(:environment).required }
it { is_expected.to belong_to(:cluster).class_name('Clusters::Cluster') }
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:deployable) }
it { is_expected.to have_one(:deployment_cluster) }
@ -18,6 +17,7 @@ RSpec.describe Deployment, feature_category: :continuous_delivery do
it { is_expected.to delegate_method(:commit).to(:project) }
it { is_expected.to delegate_method(:commit_title).to(:commit).as(:try) }
it { is_expected.to delegate_method(:kubernetes_namespace).to(:deployment_cluster).as(:kubernetes_namespace) }
it { is_expected.to delegate_method(:cluster).to(:deployment_cluster) }
it { is_expected.to validate_presence_of(:ref) }
it { is_expected.to validate_presence_of(:sha) }
@ -1476,11 +1476,4 @@ RSpec.describe Deployment, feature_category: :continuous_delivery do
end
end
end
context 'loose foreign key on deployments.cluster_id' do
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:cluster) }
let!(:model) { create(:deployment, cluster: parent) }
end
end
end

View File

@ -1660,20 +1660,18 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ
end
context 'environment has a deployment' do
let!(:deployment) { create(:deployment, :success, environment: environment, cluster: cluster) }
context 'with no cluster associated' do
let(:cluster) { nil }
let!(:deployment) { create(:deployment, :success, environment: environment) }
it { is_expected.to be_nil }
end
context 'with a cluster associated' do
let(:cluster) { create(:cluster) }
let!(:deployment) { create(:deployment, :success, :on_cluster, environment: environment) }
it 'calls the service finder' do
expect(Clusters::KnativeServicesFinder).to receive(:new)
.with(cluster, environment).and_return(:finder)
.with(deployment.cluster, environment).and_return(:finder)
is_expected.to eq :finder
end

View File

@ -19,7 +19,6 @@ RSpec.describe API::Internal::ErrorTracking, feature_category: :error_tracking d
before do
# Because the feature flag is disabled in specs we have to enable it explicitly.
stub_feature_flags(use_click_house_database_for_error_tracking: true)
stub_feature_flags(gitlab_error_tracking: true)
end
@ -90,9 +89,8 @@ RSpec.describe API::Internal::ErrorTracking, feature_category: :error_tracking d
expect(json_response).to eq('enabled' => true)
end
context 'when feature flags use_click_house_database_for_error_tracking or gitlab_error_tracking are disabled' do
context 'when feature flags gitlab_error_tracking are disabled' do
before do
stub_feature_flags(use_click_house_database_for_error_tracking: false)
stub_feature_flags(gitlab_error_tracking: false)
end

View File

@ -815,10 +815,8 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process', feature_category: :team
{
"type": "link",
"attrs": {
"href": "/uploads/groups-test-file",
"target": "_blank",
"class": null,
"uploading": false,
"href": "/uploads/groups-test-file",
"title": null,
"canonicalSrc": "/uploads/groups-test-file",
"isReference": false
@ -844,10 +842,8 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process', feature_category: :team
{
"type": "link",
"attrs": {
"href": "projects-test-file",
"target": "_blank",
"class": null,
"uploading": false,
"href": "projects-test-file",
"title": null,
"canonicalSrc": "projects-test-file",
"isReference": false
@ -903,10 +899,8 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process', feature_category: :team
{
"type": "link",
"attrs": {
"href": "project-wikis-test-file",
"target": "_blank",
"class": null,
"uploading": false,
"href": "project-wikis-test-file",
"title": null,
"canonicalSrc": "project-wikis-test-file",
"isReference": false

View File

@ -12,7 +12,7 @@ RSpec.describe DeploymentClusterEntity do
let(:request) { double(:request, current_user: current_user) }
let(:project) { create(:project) }
let(:cluster) { create(:cluster, name: 'the-cluster', projects: [project]) }
let(:deployment) { create(:deployment, cluster: cluster) }
let(:deployment) { create(:deployment) }
let!(:deployment_cluster) { create(:deployment_cluster, cluster: cluster, deployment: deployment) }
before do

View File

@ -207,6 +207,8 @@ RSpec.describe Ci::UnlockArtifactsService, feature_category: :continuous_integra
describe '#unlock_job_artifacts_query' do
subject { described_class.new(pipeline.project, pipeline.user).unlock_job_artifacts_query(pipeline_ids) }
let(:builds_table) { Ci::Build.quoted_table_name }
context 'when given a single pipeline ID' do
let(:pipeline_ids) { [older_pipeline.id] }
@ -219,12 +221,12 @@ RSpec.describe Ci::UnlockArtifactsService, feature_category: :continuous_integra
WHERE
"ci_job_artifacts"."job_id" IN
(SELECT
"ci_builds"."id"
#{builds_table}."id"
FROM
"ci_builds"
#{builds_table}
WHERE
"ci_builds"."type" = 'Ci::Build'
AND "ci_builds"."commit_id" = #{older_pipeline.id})
#{builds_table}."type" = 'Ci::Build'
AND #{builds_table}."commit_id" = #{older_pipeline.id})
RETURNING
("ci_job_artifacts"."id")
SQL
@ -243,12 +245,12 @@ RSpec.describe Ci::UnlockArtifactsService, feature_category: :continuous_integra
WHERE
"ci_job_artifacts"."job_id" IN
(SELECT
"ci_builds"."id"
#{builds_table}."id"
FROM
"ci_builds"
#{builds_table}
WHERE
"ci_builds"."type" = 'Ci::Build'
AND "ci_builds"."commit_id" IN (#{pipeline_ids.join(', ')}))
#{builds_table}."type" = 'Ci::Build'
AND #{builds_table}."commit_id" IN (#{pipeline_ids.join(', ')}))
RETURNING
("ci_job_artifacts"."id")
SQL

View File

@ -275,7 +275,7 @@ RSpec.configure do |config|
# It's disabled in specs because we don't support certain features which
# cause spec failures.
stub_feature_flags(use_click_house_database_for_error_tracking: false)
stub_feature_flags(gitlab_error_tracking: false)
# Disable this to avoid the Web IDE modals popping up in tests:
# https://gitlab.com/gitlab-org/gitlab/-/issues/385453

View File

@ -9872,10 +9872,10 @@ optionator@^0.9.1:
type-check "^0.4.0"
word-wrap "^1.2.3"
orderedmap@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.0.0.tgz#12ff5ef6ea9d12d6430b80c701b35475e1c9ff34"
integrity sha512-buf4PoAMlh45b8a8gsGy/X6w279TSqkyAS0C0wdTSJwFSU+ljQFJON5I8NfjLHoCXwpSROIo2wr0g33T+kQshQ==
orderedmap@^2.0.0, orderedmap@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.1.tgz#61481269c44031c449915497bf5a4ad273c512d2"
integrity sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==
os-browserify@^0.3.0:
version "0.3.0"