Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-10-14 21:12:54 +00:00
parent 5ca32d2dac
commit 3e9e2cd52e
24 changed files with 237 additions and 70 deletions

View file

@ -1 +1 @@
1.45.0
1.46.0

View file

@ -1,13 +1,23 @@
<script>
import { GlButton } from '@gitlab/ui';
import { mapActions } from 'vuex';
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { __ } from '~/locale';
import Tracking from '~/tracking';
export default {
components: {
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [Tracking.mixin()],
computed: {
...mapState({ isNewListShowing: ({ addColumnForm }) => addColumnForm.visible }),
tooltip() {
return this.isNewListShowing ? __('The list creation wizard is already open') : '';
},
},
methods: {
...mapActions(['setAddColumnFormVisibility']),
handleClick() {
@ -19,7 +29,14 @@ export default {
</script>
<template>
<div class="gl-ml-3 gl-display-flex gl-align-items-center" data-testid="boards-create-list">
<gl-button variant="confirm" @click="handleClick">{{ __('Create list') }} </gl-button>
<div
v-gl-tooltip="tooltip"
:tabindex="isNewListShowing ? '0' : undefined"
class="gl-ml-3 gl-display-flex gl-align-items-center"
data-testid="boards-create-list"
>
<gl-button :disabled="isNewListShowing" variant="confirm" @click="handleClick"
>{{ __('Create list') }}
</gl-button>
</div>
</template>

View file

@ -0,0 +1,8 @@
---
name: linear_ee_group_ancestor_scopes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70708
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341350
milestone: '14.4'
type: development
group: group::access
default_enabled: false

View file

@ -485,7 +485,7 @@ config:
agent:
monitor:
eventTypes: ["drop"]
eventTypes: ["drop"] # Note: possible values are documented at https://docs.cilium.io/en/stable/cmdref/cilium_monitor/
```
The Cilium monitor log for traffic is logged out by the

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
module BulkImports
module Groups
module Common
module Pipelines
class BoardsPipeline
include NdjsonPipeline

View file

@ -36,7 +36,7 @@ module BulkImports
stage: 1
},
boards: {
pipeline: BulkImports::Groups::Pipelines::BoardsPipeline,
pipeline: BulkImports::Common::Pipelines::BoardsPipeline,
stage: 2
},
finisher: {

View file

@ -23,9 +23,13 @@ module BulkImports
pipeline: BulkImports::Projects::Pipelines::IssuesPipeline,
stage: 3
},
boards: {
pipeline: BulkImports::Common::Pipelines::BoardsPipeline,
stage: 4
},
finisher: {
pipeline: BulkImports::Common::Pipelines::EntityFinisher,
stage: 4
stage: 5
}
}
end

View file

@ -11,14 +11,15 @@
module Gitlab
class SidekiqEnq
def enqueue_jobs(now = Time.now.to_f.to_s, sorted_sets = Sidekiq::Scheduled::SETS)
start_time = ::Gitlab::Metrics::System.monotonic_time
jobs = redundant_jobs = 0
Sidekiq.logger.info(message: 'Enqueuing scheduled jobs', status: 'start')
# A job's "score" in Redis is the time at which it should be processed.
# Just check Redis for the set of jobs with a timestamp before now.
Sidekiq.redis do |conn|
sorted_sets.each do |sorted_set|
start_time = ::Gitlab::Metrics::System.monotonic_time
jobs = redundant_jobs = 0
Sidekiq.logger.info(message: 'Enqueuing scheduled jobs', status: 'start', sorted_set: sorted_set)
# Get the next item in the queue if it's score (time to execute) is <= now.
# We need to go through the list one at a time to reduce the risk of something
# going wrong between the time jobs are popped from the scheduled queue and when
@ -35,11 +36,16 @@ module Gitlab
redundant_jobs += 1
end
end
end_time = ::Gitlab::Metrics::System.monotonic_time
Sidekiq.logger.info(message: 'Enqueuing scheduled jobs',
status: 'done',
sorted_set: sorted_set,
jobs_count: jobs,
redundant_jobs_count: redundant_jobs,
duration_s: end_time - start_time)
end
end
end_time = ::Gitlab::Metrics::System.monotonic_time
Sidekiq.logger.info(message: 'Enqueuing scheduled jobs', status: 'done', jobs_count: jobs, redundant_jobs_count: redundant_jobs, duration_s: end_time - start_time)
end
end
end

View file

@ -34065,6 +34065,9 @@ msgstr ""
msgid "The license was successfully uploaded and will be active from %{starts_at}. You can see the details below."
msgstr ""
msgid "The list creation wizard is already open"
msgstr ""
msgid "The maximum file size allowed is %{size}."
msgstr ""

View file

@ -12,7 +12,6 @@ settings:
jest:
jestConfigFile: 'jest.config.js'
globals:
getJSONFixture: false
loadFixtures: false
setFixtures: false
rules:

View file

@ -20,6 +20,11 @@ Did you run bin/rake frontend:fixtures?`,
return fs.readFileSync(absolutePath, 'utf8');
}
/**
* @deprecated Use `import` to load a JSON fixture instead.
* See https://docs.gitlab.com/ee/development/testing_guide/frontend_testing.html#use-fixtures,
* https://gitlab.com/gitlab-org/gitlab/-/issues/339346.
*/
export const getJSONFixture = (relativePath) => JSON.parse(getFixture(relativePath));
export const resetHTMLFixture = () => {

View file

@ -0,0 +1,59 @@
import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
import Vuex from 'vuex';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
import { createStore } from '~/boards/stores';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
Vue.use(Vuex);
describe('BoardAddNewColumnTrigger', () => {
let wrapper;
const findBoardsCreateList = () => wrapper.findByTestId('boards-create-list');
const findTooltipText = () => getBinding(findBoardsCreateList().element, 'gl-tooltip');
const mountComponent = () => {
wrapper = mountExtended(BoardAddNewColumnTrigger, {
directives: {
GlTooltip: createMockDirective(),
},
store: createStore(),
});
};
beforeEach(() => {
mountComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('when button is active', () => {
it('does not show the tooltip', () => {
const tooltip = findTooltipText();
expect(tooltip.value).toBe('');
});
it('renders an enabled button', () => {
const button = wrapper.find(GlButton);
expect(button.props('disabled')).toBe(false);
});
});
describe('when button is disabled', () => {
it('shows the tooltip', async () => {
wrapper.find(GlButton).vm.$emit('click');
await wrapper.vm.$nextTick();
const tooltip = findTooltipText();
expect(tooltip.value).toBe('The list creation wizard is already open');
});
});
});

View file

@ -1,11 +1,13 @@
import fs from 'fs';
import path from 'path';
import jsYaml from 'js-yaml';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures';
export const loadMarkdownApiResult = (testName) => {
const fixturePathPrefix = `api/markdown/${testName}.json`;
// eslint-disable-next-line import/no-deprecated
const fixture = getJSONFixture(fixturePathPrefix);
return fixture.body || fixture.html;
};

View file

@ -1,3 +1,5 @@
/* eslint-disable import/no-deprecated */
import { getJSONFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import {

View file

@ -3,6 +3,8 @@
import $ from 'jquery';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import '~/lib/utils/common_utils';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures';
import { visitUrl } from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility', () => ({
@ -66,6 +68,7 @@ describe('deprecatedJQueryDropdown', () => {
loadFixtures('static/deprecated_jquery_dropdown.html');
test.dropdownContainerElement = $('.dropdown.inline');
test.$dropdownMenuElement = $('.dropdown-menu', test.dropdownContainerElement);
// eslint-disable-next-line import/no-deprecated
test.projectsData = getJSONFixture('static/projects.json');
});

View file

@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture, setHTMLFixture } from 'helpers/fixtures';
import FilterableList from '~/filterable_list';
@ -14,6 +15,7 @@ describe('FilterableList', () => {
</div>
<div class="js-projects-list-holder"></div>
`);
// eslint-disable-next-line import/no-deprecated
getJSONFixture('static/projects.json');
form = document.querySelector('form#project-filter-form');
filter = document.querySelector('.js-projects-list-filter');

View file

@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture, setHTMLFixture } from 'helpers/fixtures';
import ProjectsFilterableList from '~/projects/projects_filterable_list';
@ -14,6 +15,7 @@ describe('ProjectsFilterableList', () => {
</div>
<div class="js-projects-list-holder"></div>
`);
// eslint-disable-next-line import/no-deprecated
getJSONFixture('static/projects.json');
form = document.querySelector('form#project-filter-form');
filter = document.querySelector('.js-projects-list-filter');

View file

@ -6,7 +6,7 @@ import { setGlobalDateToFakeDate } from 'helpers/fake_date';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
import Translate from '~/vue_shared/translate';
import { getJSONFixture, loadHTMLFixture, setHTMLFixture } from './__helpers__/fixtures';
import { loadHTMLFixture, setHTMLFixture } from './__helpers__/fixtures';
import { initializeTestTimeout } from './__helpers__/timeout';
import customMatchers from './matchers';
import { setupManualMocks } from './mocks/mocks_helper';
@ -43,7 +43,6 @@ Vue.use(Translate);
// convenience wrapper for migration from Karma
Object.assign(global, {
getJSONFixture,
loadFixtures: loadHTMLFixture,
setFixtures: setHTMLFixture,
});

View file

@ -1,4 +1,6 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures';
import { trimText } from 'helpers/text_helper';
import ProjectAvatar from '~/vue_shared/components/deprecated_project_avatar/default.vue';
import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue';
@ -11,6 +13,7 @@ describe('ProjectListItem component', () => {
let vm;
let options;
// eslint-disable-next-line import/no-deprecated
const project = getJSONFixture('static/projects.json')[0];
beforeEach(() => {

View file

@ -2,6 +2,8 @@ import { GlSearchBoxByType, GlInfiniteScroll } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import { head } from 'lodash';
import Vue from 'vue';
// eslint-disable-next-line import/no-deprecated
import { getJSONFixture } from 'helpers/fixtures';
import { trimText } from 'helpers/text_helper';
import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue';
import ProjectSelector from '~/vue_shared/components/project_selector/project_selector.vue';
@ -11,6 +13,7 @@ const localVue = createLocalVue();
describe('ProjectSelector component', () => {
let wrapper;
let vm;
// eslint-disable-next-line import/no-deprecated
const allProjects = getJSONFixture('static/projects.json');
const searchResults = allProjects.slice(0, 5);
let selected = [];

View file

@ -0,0 +1,98 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Common::Pipelines::BoardsPipeline do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
let(:board_data) do
{
"name" => "Test Board",
"lists" => [
{
"list_type" => "backlog",
"position" => 0
},
{
"list_type" => "closed",
"position" => 1
},
{
"list_type" => "label",
"position" => 2,
"label" => {
"title" => "test",
"type" => "GroupLabel",
"group_id" => group.id
}
}
]
}
end
let(:tracker) { create(:bulk_import_tracker, entity: entity) }
let(:context) { BulkImports::Pipeline::Context.new(tracker) }
subject { described_class.new(context) }
before do
allow_next_instance_of(BulkImports::Common::Extractors::NdjsonExtractor) do |extractor|
allow(extractor).to receive(:extract).and_return(BulkImports::Pipeline::ExtractedData.new(data: board_data))
end
group.add_owner(user)
end
context 'when issue board belongs to a project' do
let_it_be(:entity) do
create(
:bulk_import_entity,
source_type: :project_entity,
project: project,
bulk_import: bulk_import,
source_full_path: 'source/full/path',
destination_name: 'My Destination Group',
destination_namespace: group.full_path
)
end
describe '#run' do
it 'imports issue boards into destination project' do
expect { subject.run }.to change(::Board, :count).by(1)
board = project.boards.find_by(name: board_data["name"])
expect(board).to be
expect(board.project.id).to eq(project.id)
expect(board.lists.count).to eq(3)
expect(board.lists.map(&:list_type).sort).to match_array(%w(backlog closed label))
expect(board.lists.find_by(list_type: "label").label.title).to eq("test")
end
end
end
context 'when issue board belongs to a group' do
let_it_be(:entity) do
create(
:bulk_import_entity,
group: group,
bulk_import: bulk_import,
source_full_path: 'source/full/path',
destination_name: 'My Destination Group',
destination_namespace: group.full_path
)
end
describe '#run' do
it 'imports issue boards into destination group' do
expect { subject.run }.to change(::Board, :count).by(1)
board = group.boards.find_by(name: board_data["name"])
expect(board).to be
expect(board.group.id).to eq(group.id)
expect(board.lists.count).to eq(3)
expect(board.lists.map(&:list_type).sort).to match_array(%w(backlog closed label))
expect(board.lists.find_by(list_type: "label").label.title).to eq("test")
end
end
end
end

View file

@ -1,49 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Groups::Pipelines::BoardsPipeline do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
let_it_be(:filepath) { 'spec/fixtures/bulk_imports/gz/boards.ndjson.gz' }
let_it_be(:entity) do
create(
:bulk_import_entity,
group: group,
bulk_import: bulk_import,
source_full_path: 'source/full/path',
destination_name: 'My Destination Group',
destination_namespace: group.full_path
)
end
let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
let(:tmpdir) { Dir.mktmpdir }
before do
FileUtils.copy_file(filepath, File.join(tmpdir, 'boards.ndjson.gz'))
group.add_owner(user)
end
subject { described_class.new(context) }
describe '#run' do
it 'imports group boards into destination group and removes tmpdir' do
allow(Dir).to receive(:mktmpdir).and_return(tmpdir)
allow_next_instance_of(BulkImports::FileDownloadService) do |service|
allow(service).to receive(:execute)
end
expect { subject.run }.to change(Board, :count).by(1)
lists = group.boards.find_by(name: 'first board').lists
expect(lists.count).to eq(3)
expect(lists.first.label.title).to eq('TSL')
expect(lists.second.label.title).to eq('Sosync')
end
end
end

View file

@ -14,7 +14,7 @@ RSpec.describe BulkImports::Groups::Stage do
[1, BulkImports::Common::Pipelines::LabelsPipeline],
[1, BulkImports::Groups::Pipelines::MilestonesPipeline],
[1, BulkImports::Groups::Pipelines::BadgesPipeline],
[2, BulkImports::Groups::Pipelines::BoardsPipeline]
[2, BulkImports::Common::Pipelines::BoardsPipeline]
]
end

View file

@ -9,7 +9,8 @@ RSpec.describe BulkImports::Projects::Stage do
[1, BulkImports::Projects::Pipelines::RepositoryPipeline],
[2, BulkImports::Common::Pipelines::LabelsPipeline],
[3, BulkImports::Projects::Pipelines::IssuesPipeline],
[4, BulkImports::Common::Pipelines::EntityFinisher]
[4, BulkImports::Common::Pipelines::BoardsPipeline],
[5, BulkImports::Common::Pipelines::EntityFinisher]
]
end