Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
02246c40b8
commit
8f5ebbe2c7
|
|
@ -27,7 +27,13 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
urlParams() {
|
||||
const { authorUsername, labelName, assigneeUsername, search } = this.filterParams;
|
||||
const {
|
||||
authorUsername,
|
||||
labelName,
|
||||
assigneeUsername,
|
||||
search,
|
||||
milestoneTitle,
|
||||
} = this.filterParams;
|
||||
let notParams = {};
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
|
||||
|
|
@ -36,6 +42,7 @@ export default {
|
|||
'not[label_name][]': this.filterParams.not.labelName,
|
||||
'not[author_username]': this.filterParams.not.authorUsername,
|
||||
'not[assignee_username]': this.filterParams.not.assigneeUsername,
|
||||
'not[milestone_title]': this.filterParams.not.milestoneTitle,
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
|
|
@ -46,6 +53,7 @@ export default {
|
|||
author_username: authorUsername,
|
||||
'label_name[]': labelName,
|
||||
assignee_username: assigneeUsername,
|
||||
milestone_title: milestoneTitle,
|
||||
search,
|
||||
};
|
||||
},
|
||||
|
|
@ -64,7 +72,13 @@ export default {
|
|||
this.performSearch();
|
||||
},
|
||||
getFilteredSearchValue() {
|
||||
const { authorUsername, labelName, assigneeUsername, search } = this.filterParams;
|
||||
const {
|
||||
authorUsername,
|
||||
labelName,
|
||||
assigneeUsername,
|
||||
search,
|
||||
milestoneTitle,
|
||||
} = this.filterParams;
|
||||
const filteredSearchValue = [];
|
||||
|
||||
if (authorUsername) {
|
||||
|
|
@ -90,6 +104,13 @@ export default {
|
|||
);
|
||||
}
|
||||
|
||||
if (milestoneTitle) {
|
||||
filteredSearchValue.push({
|
||||
type: 'milestone_title',
|
||||
value: { data: milestoneTitle, operator: '=' },
|
||||
});
|
||||
}
|
||||
|
||||
if (this.filterParams['not[authorUsername]']) {
|
||||
filteredSearchValue.push({
|
||||
type: 'author_username',
|
||||
|
|
@ -97,6 +118,13 @@ export default {
|
|||
});
|
||||
}
|
||||
|
||||
if (this.filterParams['not[milestoneTitle]']) {
|
||||
filteredSearchValue.push({
|
||||
type: 'milestone_title',
|
||||
value: { data: this.filterParams['not[milestoneTitle]'], operator: '!=' },
|
||||
});
|
||||
}
|
||||
|
||||
if (this.filterParams['not[assigneeUsername]']) {
|
||||
filteredSearchValue.push({
|
||||
type: 'assignee_username',
|
||||
|
|
@ -143,6 +171,9 @@ export default {
|
|||
case 'label_name':
|
||||
labels.push(filter.value.data);
|
||||
break;
|
||||
case 'milestone_title':
|
||||
filterParams.milestoneTitle = filter.value.data;
|
||||
break;
|
||||
case 'filtered-search-term':
|
||||
if (filter.value.data) plainText.push(filter.value.data);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue';
|
||||
import issueBoardFilters from '~/boards/issue_board_filters';
|
||||
import { TYPE_USER } from '~/graphql_shared/constants';
|
||||
|
|
@ -6,6 +7,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
|
|||
import { __ } from '~/locale';
|
||||
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
|
||||
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
|
||||
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
|
|
@ -13,6 +15,7 @@ export default {
|
|||
label: __('Label'),
|
||||
author: __('Author'),
|
||||
assignee: __('Assignee'),
|
||||
milestone: __('Milestone'),
|
||||
is: __('is'),
|
||||
isNot: __('is not'),
|
||||
},
|
||||
|
|
@ -29,7 +32,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
tokens() {
|
||||
const { label, is, isNot, author, assignee } = this.$options.i18n;
|
||||
const { label, is, isNot, author, assignee, milestone } = this.$options.i18n;
|
||||
const { fetchAuthors, fetchLabels } = issueBoardFilters(
|
||||
this.$apollo,
|
||||
this.fullPath,
|
||||
|
|
@ -77,10 +80,21 @@ export default {
|
|||
fetchAuthors,
|
||||
preloadedAuthors: this.preloadedAuthors(),
|
||||
},
|
||||
{
|
||||
type: 'milestone_title',
|
||||
title: milestone,
|
||||
icon: 'clock',
|
||||
symbol: '%',
|
||||
token: MilestoneToken,
|
||||
unique: true,
|
||||
defaultMilestones: [], // todo: https://gitlab.com/gitlab-org/gitlab/-/issues/337044#note_640010094
|
||||
fetchMilestones: this.fetchMilestones,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['fetchMilestones']),
|
||||
preloadedAuthors() {
|
||||
return gon?.current_user_id
|
||||
? [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<script>
|
||||
import { GlAlert } from '@gitlab/ui';
|
||||
import EditorStateObserver from './editor_state_observer.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlAlert,
|
||||
EditorStateObserver,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
displayError({ error }) {
|
||||
this.error = error;
|
||||
},
|
||||
dismissError() {
|
||||
this.error = null;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<editor-state-observer @error="displayError">
|
||||
<gl-alert v-if="error" class="gl-mb-6" variant="danger" @dismiss="dismissError">
|
||||
{{ error }}
|
||||
</gl-alert>
|
||||
</editor-state-observer>
|
||||
</template>
|
||||
|
|
@ -5,6 +5,9 @@ export const tiptapToComponentMap = {
|
|||
update: 'docUpdate',
|
||||
selectionUpdate: 'selectionUpdate',
|
||||
transaction: 'transaction',
|
||||
focus: 'focus',
|
||||
blur: 'blur',
|
||||
error: 'error',
|
||||
};
|
||||
|
||||
const getComponentEventName = (tiptapEventName) => tiptapToComponentMap[tiptapEventName];
|
||||
|
|
|
|||
|
|
@ -275,7 +275,6 @@ export default {
|
|||
avatar_url: gon.current_user_avatar_url,
|
||||
});
|
||||
}
|
||||
|
||||
const tokens = [
|
||||
{
|
||||
type: TOKEN_TYPE_AUTHOR,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUniqueIndexToVulnerabilityFlagsTable < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
INDEX_NAME = 'index_vulnerability_flags_on_unique_columns'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :vulnerability_flags, [:vulnerability_occurrence_id, :flag_type, :origin], name: INDEX_NAME, unique: true
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :vulnerability_flags, INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
529cf86e09b5aa9015b604e73827cb21e92ced401f30dfb281115a506596bd4e
|
||||
|
|
@ -25450,6 +25450,8 @@ CREATE INDEX index_vulnerability_findings_remediations_on_remediation_id ON vuln
|
|||
|
||||
CREATE UNIQUE INDEX index_vulnerability_findings_remediations_on_unique_keys ON vulnerability_findings_remediations USING btree (vulnerability_occurrence_id, vulnerability_remediation_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_vulnerability_flags_on_unique_columns ON vulnerability_flags USING btree (vulnerability_occurrence_id, flag_type, origin);
|
||||
|
||||
CREATE INDEX index_vulnerability_flags_on_vulnerability_occurrence_id ON vulnerability_flags USING btree (vulnerability_occurrence_id);
|
||||
|
||||
CREATE INDEX index_vulnerability_historical_statistics_on_date_and_id ON vulnerability_historical_statistics USING btree (date, id);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,42 @@
|
|||
|
||||
namespace :gitlab do
|
||||
namespace :gitaly do
|
||||
desc 'Installs gitaly for running tests within gitlab-development-kit'
|
||||
task :test_install, [:dir, :storage_path, :repo] => :gitlab_environment do |t, args|
|
||||
inside_gdk = Rails.env.test? && File.exist?(Rails.root.join('../GDK_ROOT'))
|
||||
|
||||
if ENV['FORCE_GITALY_INSTALL'] || !inside_gdk
|
||||
Rake::Task["gitlab:gitaly:install"].invoke(*args)
|
||||
|
||||
next
|
||||
end
|
||||
|
||||
gdk_gitaly_dir = ENV.fetch('GDK_GITALY', Rails.root.join('../gitaly'))
|
||||
|
||||
# Our test setup expects a git repo, so clone rather than copy
|
||||
version = Gitlab::GitalyClient.expected_server_version
|
||||
checkout_or_clone_version(version: version, repo: gdk_gitaly_dir, target_dir: args.dir, clone_opts: %w[--depth 1])
|
||||
|
||||
# We assume the GDK gitaly already compiled binaries
|
||||
build_dir = File.join(gdk_gitaly_dir, '_build')
|
||||
FileUtils.cp_r(build_dir, args.dir)
|
||||
|
||||
# We assume the GDK gitaly already ran bundle install
|
||||
bundle_dir = File.join(gdk_gitaly_dir, 'ruby', '.bundle')
|
||||
FileUtils.cp_r(bundle_dir, File.join(args.dir, 'ruby'))
|
||||
|
||||
# For completeness we copy this for gitaly's make target
|
||||
ruby_bundle_file = File.join(gdk_gitaly_dir, '.ruby-bundle')
|
||||
FileUtils.cp_r(ruby_bundle_file, args.dir)
|
||||
|
||||
gitaly_binary = File.join(build_dir, 'bin', 'gitaly')
|
||||
warn_gitaly_out_of_date!(gitaly_binary, version)
|
||||
rescue Errno::ENOENT => e
|
||||
puts "Could not copy files, did you run `gdk update`? Error: #{e.message}"
|
||||
|
||||
raise
|
||||
end
|
||||
|
||||
desc 'GitLab | Gitaly | Install or upgrade gitaly'
|
||||
task :install, [:dir, :storage_path, :repo] => :gitlab_environment do |t, args|
|
||||
warn_user_is_not_gitlab
|
||||
|
|
@ -41,5 +77,24 @@ Usage: rake "gitlab:gitaly:install[/installation/dir,/storage/path]")
|
|||
_, status = Gitlab::Popen.popen(%w[which gmake])
|
||||
status == 0 ? 'gmake' : 'make'
|
||||
end
|
||||
|
||||
def warn_gitaly_out_of_date!(gitaly_binary, expected_version)
|
||||
binary_version, exit_status = Gitlab::Popen.popen(%W[#{gitaly_binary} -version])
|
||||
|
||||
raise "Failed to run `#{gitaly_binary} -version`" unless exit_status == 0
|
||||
|
||||
binary_version = binary_version.strip
|
||||
|
||||
# See help for `git describe` for format
|
||||
git_describe_sha = /g([a-f0-9]{5,40})\z/
|
||||
match = binary_version.match(git_describe_sha)
|
||||
|
||||
# Just skip if the version does not have a sha component
|
||||
return unless match
|
||||
|
||||
return if expected_version.start_with?(match[1])
|
||||
|
||||
puts "WARNING: #{binary_version.strip} does not exactly match repository version #{expected_version}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ describe('BoardFilteredSearch', () => {
|
|||
{ type: 'author_username', value: { data: 'root', operator: '=' } },
|
||||
{ type: 'label_name', value: { data: 'label', operator: '=' } },
|
||||
{ type: 'label_name', value: { data: 'label2', operator: '=' } },
|
||||
{ type: 'milestone_title', value: { data: 'New Milestone', operator: '=' } },
|
||||
];
|
||||
jest.spyOn(urlUtility, 'updateHistory');
|
||||
findFilteredSearch().vm.$emit('onFilter', mockFilters);
|
||||
|
|
@ -122,7 +123,8 @@ describe('BoardFilteredSearch', () => {
|
|||
expect(urlUtility.updateHistory).toHaveBeenCalledWith({
|
||||
title: '',
|
||||
replace: true,
|
||||
url: 'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2',
|
||||
url:
|
||||
'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&milestone_title=New+Milestone',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue';
|
||||
import IssueBoardFilteredSpec from '~/boards/components/issue_board_filtered_search.vue';
|
||||
import { BoardType } from '~/boards/constants';
|
||||
import issueBoardFilters from '~/boards/issue_board_filters';
|
||||
import { mockTokens } from '../mock_data';
|
||||
|
||||
jest.mock('~/boards/issue_board_filters');
|
||||
|
||||
describe('IssueBoardFilter', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = ({ initialFilterParams = {} } = {}) => {
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(IssueBoardFilteredSpec, {
|
||||
provide: { initialFilterParams },
|
||||
props: { fullPath: '', boardType: '' },
|
||||
});
|
||||
};
|
||||
|
|
@ -20,7 +20,17 @@ describe('IssueBoardFilter', () => {
|
|||
});
|
||||
|
||||
describe('default', () => {
|
||||
let fetchAuthorsSpy;
|
||||
let fetchLabelsSpy;
|
||||
beforeEach(() => {
|
||||
fetchAuthorsSpy = jest.fn();
|
||||
fetchLabelsSpy = jest.fn();
|
||||
|
||||
issueBoardFilters.mockReturnValue({
|
||||
fetchAuthors: fetchAuthorsSpy,
|
||||
fetchLabels: fetchLabelsSpy,
|
||||
});
|
||||
|
||||
createComponent();
|
||||
});
|
||||
|
||||
|
|
@ -28,17 +38,10 @@ describe('IssueBoardFilter', () => {
|
|||
expect(wrapper.find(BoardFilteredSearch).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it.each([[BoardType.group], [BoardType.project]])(
|
||||
'when boardType is %s we pass the correct tokens to BoardFilteredSearch',
|
||||
(boardType) => {
|
||||
const { fetchAuthors, fetchLabels } = issueBoardFilters({}, '', boardType);
|
||||
it('passes the correct tokens to BoardFilteredSearch', () => {
|
||||
const tokens = mockTokens(fetchLabelsSpy, fetchAuthorsSpy, wrapper.vm.fetchMilestones);
|
||||
|
||||
const tokens = mockTokens(fetchLabels, fetchAuthors);
|
||||
|
||||
expect(wrapper.find(BoardFilteredSearch).props('tokens').toString()).toBe(
|
||||
tokens.toString(),
|
||||
);
|
||||
},
|
||||
);
|
||||
expect(wrapper.find(BoardFilteredSearch).props('tokens')).toEqual(tokens);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import boardsStore from '~/boards/stores/boards_store';
|
|||
import { __ } from '~/locale';
|
||||
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
|
||||
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
|
||||
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
|
||||
|
||||
export const boardObj = {
|
||||
id: 1,
|
||||
|
|
@ -542,7 +543,7 @@ export const mockMoveData = {
|
|||
...mockMoveIssueParams,
|
||||
};
|
||||
|
||||
export const mockTokens = (fetchLabels, fetchAuthors) => [
|
||||
export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones) => [
|
||||
{
|
||||
icon: 'labels',
|
||||
title: __('Label'),
|
||||
|
|
@ -568,6 +569,7 @@ export const mockTokens = (fetchLabels, fetchAuthors) => [
|
|||
token: AuthorToken,
|
||||
unique: true,
|
||||
fetchAuthors,
|
||||
preloadedAuthors: [],
|
||||
},
|
||||
{
|
||||
icon: 'user',
|
||||
|
|
@ -580,5 +582,16 @@ export const mockTokens = (fetchLabels, fetchAuthors) => [
|
|||
token: AuthorToken,
|
||||
unique: true,
|
||||
fetchAuthors,
|
||||
preloadedAuthors: [],
|
||||
},
|
||||
{
|
||||
icon: 'clock',
|
||||
title: __('Milestone'),
|
||||
symbol: '%',
|
||||
type: 'milestone_title',
|
||||
token: MilestoneToken,
|
||||
unique: true,
|
||||
defaultMilestones: [],
|
||||
fetchMilestones,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
import { GlAlert } from '@gitlab/ui';
|
||||
import { nextTick } from 'vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import ContentEditorError from '~/content_editor/components/content_editor_error.vue';
|
||||
import EditorStateObserver from '~/content_editor/components/editor_state_observer.vue';
|
||||
import { createTestEditor, emitEditorEvent } from '../test_utils';
|
||||
|
||||
describe('content_editor/components/content_editor_error', () => {
|
||||
let wrapper;
|
||||
let tiptapEditor;
|
||||
|
||||
const findErrorAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
const createWrapper = async () => {
|
||||
tiptapEditor = createTestEditor();
|
||||
|
||||
wrapper = shallowMountExtended(ContentEditorError, {
|
||||
provide: {
|
||||
tiptapEditor,
|
||||
},
|
||||
stubs: {
|
||||
EditorStateObserver,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders error when content editor emits an error event', async () => {
|
||||
const error = 'error message';
|
||||
|
||||
createWrapper();
|
||||
|
||||
await emitEditorEvent({ tiptapEditor, event: 'error', params: { error } });
|
||||
|
||||
expect(findErrorAlert().text()).toBe(error);
|
||||
});
|
||||
|
||||
it('allows dismissing the error', async () => {
|
||||
const error = 'error message';
|
||||
|
||||
createWrapper();
|
||||
|
||||
await emitEditorEvent({ tiptapEditor, event: 'error', params: { error } });
|
||||
|
||||
findErrorAlert().vm.$emit('dismiss');
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(findErrorAlert().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -158,7 +158,7 @@ module TestEnv
|
|||
component_timed_setup('Gitaly',
|
||||
install_dir: gitaly_dir,
|
||||
version: Gitlab::GitalyClient.expected_server_version,
|
||||
task: "gitlab:gitaly:install",
|
||||
task: "gitlab:gitaly:test_install",
|
||||
task_args: [gitaly_dir, repos_path, gitaly_url].compact) do
|
||||
Gitlab::SetupHelper::Gitaly.create_configuration(
|
||||
gitaly_dir,
|
||||
|
|
|
|||
Loading…
Reference in New Issue