diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-15 00:12:54 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-15 00:12:54 +0300 |
commit | 3e9e2cd52e37434eb0b3931dc532ef27e5d3feab (patch) | |
tree | 1efc6d3ea6712162172ee80d8686c739bad5384b | |
parent | 5ca32d2dacee40663b0ce51969c944e220927896 (diff) |
Add latest changes from gitlab-org/gitlab@master
24 files changed, 237 insertions, 70 deletions
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 50aceaa7b71..0a3db35b241 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.45.0 +1.46.0 diff --git a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue index 2aee84b805f..14c84d3c4e5 100644 --- a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue +++ b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue @@ -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> diff --git a/config/feature_flags/development/linear_ee_group_ancestor_scopes.yml b/config/feature_flags/development/linear_ee_group_ancestor_scopes.yml new file mode 100644 index 00000000000..46294b0aef0 --- /dev/null +++ b/config/feature_flags/development/linear_ee_group_ancestor_scopes.yml @@ -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 diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index 2da8396cfd7..31dd503a0cf 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -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 diff --git a/lib/bulk_imports/groups/pipelines/boards_pipeline.rb b/lib/bulk_imports/common/pipelines/boards_pipeline.rb index 08a0a4abc9f..8329c217d54 100644 --- a/lib/bulk_imports/groups/pipelines/boards_pipeline.rb +++ b/lib/bulk_imports/common/pipelines/boards_pipeline.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module BulkImports - module Groups + module Common module Pipelines class BoardsPipeline include NdjsonPipeline diff --git a/lib/bulk_imports/groups/stage.rb b/lib/bulk_imports/groups/stage.rb index 398b954dc6f..a1869b4cb0e 100644 --- a/lib/bulk_imports/groups/stage.rb +++ b/lib/bulk_imports/groups/stage.rb @@ -36,7 +36,7 @@ module BulkImports stage: 1 }, boards: { - pipeline: BulkImports::Groups::Pipelines::BoardsPipeline, + pipeline: BulkImports::Common::Pipelines::BoardsPipeline, stage: 2 }, finisher: { diff --git a/lib/bulk_imports/projects/stage.rb b/lib/bulk_imports/projects/stage.rb index b10ac8dfb42..3ada0f406ca 100644 --- a/lib/bulk_imports/projects/stage.rb +++ b/lib/bulk_imports/projects/stage.rb @@ -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 diff --git a/lib/gitlab/sidekiq_enq.rb b/lib/gitlab/sidekiq_enq.rb index 71aa12dadb5..d8a01ac8ef4 100644 --- a/lib/gitlab/sidekiq_enq.rb +++ b/lib/gitlab/sidekiq_enq.rb @@ -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 diff --git a/locale/gitlab.pot b/locale/gitlab.pot index bb6c31656ef..15fea0288b9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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 "" diff --git a/spec/frontend/.eslintrc.yml b/spec/frontend/.eslintrc.yml index 2686a14f9eb..e12c4e5e820 100644 --- a/spec/frontend/.eslintrc.yml +++ b/spec/frontend/.eslintrc.yml @@ -12,7 +12,6 @@ settings: jest: jestConfigFile: 'jest.config.js' globals: - getJSONFixture: false loadFixtures: false setFixtures: false rules: diff --git a/spec/frontend/__helpers__/fixtures.js b/spec/frontend/__helpers__/fixtures.js index 4b86724df93..d8054d32fae 100644 --- a/spec/frontend/__helpers__/fixtures.js +++ b/spec/frontend/__helpers__/fixtures.js @@ -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 = () => { diff --git a/spec/frontend/boards/components/board_add_new_column_trigger_spec.js b/spec/frontend/boards/components/board_add_new_column_trigger_spec.js new file mode 100644 index 00000000000..c35f2463f69 --- /dev/null +++ b/spec/frontend/boards/components/board_add_new_column_trigger_spec.js @@ -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'); + }); + }); +}); diff --git a/spec/frontend/content_editor/markdown_processing_examples.js b/spec/frontend/content_editor/markdown_processing_examples.js index b3aabfeb145..da895970289 100644 --- a/spec/frontend/content_editor/markdown_processing_examples.js +++ b/spec/frontend/content_editor/markdown_processing_examples.js @@ -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; }; diff --git a/spec/frontend/cycle_analytics/mock_data.js b/spec/frontend/cycle_analytics/mock_data.js index 9a89ac23b21..1fbeafd5204 100644 --- a/spec/frontend/cycle_analytics/mock_data.js +++ b/spec/frontend/cycle_analytics/mock_data.js @@ -1,3 +1,5 @@ +/* eslint-disable import/no-deprecated */ + import { getJSONFixture } from 'helpers/fixtures'; import { TEST_HOST } from 'helpers/test_constants'; import { diff --git a/spec/frontend/deprecated_jquery_dropdown_spec.js b/spec/frontend/deprecated_jquery_dropdown_spec.js index 4a6dee31cd5..7e4c6e131b4 100644 --- a/spec/frontend/deprecated_jquery_dropdown_spec.js +++ b/spec/frontend/deprecated_jquery_dropdown_spec.js @@ -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'); }); diff --git a/spec/frontend/filterable_list_spec.js b/spec/frontend/filterable_list_spec.js index 8c6a71abad7..556cf6f8137 100644 --- a/spec/frontend/filterable_list_spec.js +++ b/spec/frontend/filterable_list_spec.js @@ -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'); diff --git a/spec/frontend/projects/projects_filterable_list_spec.js b/spec/frontend/projects/projects_filterable_list_spec.js index 377d347623a..d4dbf85b5ca 100644 --- a/spec/frontend/projects/projects_filterable_list_spec.js +++ b/spec/frontend/projects/projects_filterable_list_spec.js @@ -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'); diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js index 4d1b0f54e42..2c8e0fff848 100644 --- a/spec/frontend/test_setup.js +++ b/spec/frontend/test_setup.js @@ -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, }); diff --git a/spec/frontend/vue_shared/components/project_selector/project_list_item_spec.js b/spec/frontend/vue_shared/components/project_selector/project_list_item_spec.js index ab028ea52b7..1ed7844b395 100644 --- a/spec/frontend/vue_shared/components/project_selector/project_list_item_spec.js +++ b/spec/frontend/vue_shared/components/project_selector/project_list_item_spec.js @@ -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(() => { diff --git a/spec/frontend/vue_shared/components/project_selector/project_selector_spec.js b/spec/frontend/vue_shared/components/project_selector/project_selector_spec.js index 06b00a8e196..1f97d3ff3fa 100644 --- a/spec/frontend/vue_shared/components/project_selector/project_selector_spec.js +++ b/spec/frontend/vue_shared/components/project_selector/project_selector_spec.js @@ -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 = []; diff --git a/spec/lib/bulk_imports/common/pipelines/boards_pipeline_spec.rb b/spec/lib/bulk_imports/common/pipelines/boards_pipeline_spec.rb new file mode 100644 index 00000000000..241bd694a2c --- /dev/null +++ b/spec/lib/bulk_imports/common/pipelines/boards_pipeline_spec.rb @@ -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 diff --git a/spec/lib/bulk_imports/groups/pipelines/boards_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/boards_pipeline_spec.rb deleted file mode 100644 index 8b2f03ca15f..00000000000 --- a/spec/lib/bulk_imports/groups/pipelines/boards_pipeline_spec.rb +++ /dev/null @@ -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 diff --git a/spec/lib/bulk_imports/groups/stage_spec.rb b/spec/lib/bulk_imports/groups/stage_spec.rb index 696e68c2f65..b322b7b0edf 100644 --- a/spec/lib/bulk_imports/groups/stage_spec.rb +++ b/spec/lib/bulk_imports/groups/stage_spec.rb @@ -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 diff --git a/spec/lib/bulk_imports/projects/stage_spec.rb b/spec/lib/bulk_imports/projects/stage_spec.rb index 57f63e8f9fb..c606cf7c556 100644 --- a/spec/lib/bulk_imports/projects/stage_spec.rb +++ b/spec/lib/bulk_imports/projects/stage_spec.rb @@ -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 |