Commit ae832eef authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 91f8b22c 4ca0e8e7
......@@ -36,6 +36,9 @@ export default {
computed: {
...mapGetters(['isSidebarOpen', 'shouldUseGraphQL']),
...mapState(['activeId', 'sidebarType', 'boardLists']),
isWipLimitsOn() {
return this.glFeatures.wipLimits;
},
activeList() {
/*
Warning: Though a computed property it is not reactive because we are
......@@ -105,7 +108,10 @@ export default {
:active-list="activeList"
:board-list-type="boardListType"
/>
<board-settings-sidebar-wip-limit :max-issue-count="activeList.maxIssueCount" />
<board-settings-sidebar-wip-limit
v-if="isWipLimitsOn"
:max-issue-count="activeList.maxIssueCount"
/>
<div v-if="canAdminList && !activeList.preset && activeList.id" class="gl-m-4">
<gl-button
variant="danger"
......
<script>
import { mapActions } from 'vuex';
import { GlAlert, GlButton } from '@gitlab/ui';
import { CENTERED_LIMITED_CONTAINER_CLASSES } from '../constants';
import eventHub from '../event_hub';
export default {
components: {
......@@ -36,13 +35,12 @@ export default {
},
methods: {
...mapActions('diffs', ['expandAllFiles']),
dismiss() {
this.isDismissed = true;
this.$emit('dismiss');
},
expand() {
this.expandAllFiles();
eventHub.$emit('mr:diffs:expandAllFiles');
this.dismiss();
},
},
......
......@@ -7,6 +7,7 @@ import CompareDropdownLayout from './compare_dropdown_layout.vue';
import SettingsDropdown from './settings_dropdown.vue';
import DiffStats from './diff_stats.vue';
import { CENTERED_LIMITED_CONTAINER_CLASSES } from '../constants';
import eventHub from '../event_hub';
export default {
components: {
......@@ -67,9 +68,11 @@ export default {
...mapActions('diffs', [
'setInlineDiffViewType',
'setParallelDiffViewType',
'expandAllFiles',
'toggleShowTreeList',
]),
expandAllFiles() {
eventHub.$emit('mr:diffs:expandAllFiles');
},
},
};
</script>
......
......@@ -6,13 +6,14 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { sprintf } from '~/locale';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { hasDiff } from '~/helpers/diffs_helper';
import eventHub from '../../notes/event_hub';
import notesEventHub from '../../notes/event_hub';
import DiffFileHeader from './diff_file_header.vue';
import DiffContent from './diff_content.vue';
import { diffViewerErrors } from '~/ide/constants';
import { collapsedType, isCollapsed } from '../diff_file';
import { DIFF_FILE_AUTOMATIC_COLLAPSE, DIFF_FILE_MANUAL_COLLAPSE } from '../constants';
import { DIFF_FILE, GENERIC_ERROR } from '../i18n';
import eventHub from '../event_hub';
export default {
components: {
......@@ -151,7 +152,11 @@ export default {
},
},
created() {
eventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.requestDiff);
notesEventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.requestDiff);
eventHub.$on('mr:diffs:expandAllFiles', this.expandAllListener);
},
beforeDestroy() {
eventHub.$off('mr:diffs:expandAllFiles', this.expandAllListener);
},
methods: {
...mapActions('diffs', [
......@@ -160,6 +165,11 @@ export default {
'setRenderIt',
'setFileCollapsedByUser',
]),
expandAllListener() {
if (this.isCollapsed) {
this.handleToggle();
}
},
handleToggle() {
const currentCollapsedFlag = this.isCollapsed;
......
import eventHubFactory from '~/helpers/event_hub_factory';
export default eventHubFactory();
......@@ -364,10 +364,6 @@ export const loadCollapsedDiff = ({ commit, getters, state }, file) =>
});
});
export const expandAllFiles = ({ commit }) => {
commit(types.EXPAND_ALL_FILES);
};
/**
* Toggles the file discussions after user clicked on the toggle discussions button.
*
......
......@@ -13,7 +13,6 @@ export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS';
export const TOGGLE_LINE_HAS_FORM = 'TOGGLE_LINE_HAS_FORM';
export const ADD_CONTEXT_LINES = 'ADD_CONTEXT_LINES';
export const ADD_COLLAPSED_DIFFS = 'ADD_COLLAPSED_DIFFS';
export const EXPAND_ALL_FILES = 'EXPAND_ALL_FILES';
export const RENDER_FILE = 'RENDER_FILE';
export const SET_LINE_DISCUSSIONS_FOR_FILE = 'SET_LINE_DISCUSSIONS_FOR_FILE';
export const REMOVE_LINE_DISCUSSIONS_FOR_FILE = 'REMOVE_LINE_DISCUSSIONS_FOR_FILE';
......
......@@ -176,17 +176,6 @@ export default {
Object.assign(selectedFile, { ...newFileData });
},
[types.EXPAND_ALL_FILES](state) {
state.diffFiles.forEach(file => {
Object.assign(file, {
viewer: Object.assign(file.viewer, {
automaticallyCollapsed: false,
manuallyCollapsed: false,
}),
});
});
},
[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode, hash }) {
const { latestDiff } = state;
......
......@@ -16,63 +16,53 @@ const frequentItemDropdowns = [
},
];
const initFrequentItemList = (namespace, key) => {
const el = document.getElementById(`js-${namespace}-dropdown`);
// Don't do anything if element doesn't exist (No groups dropdown)
// This is for when the user accesses GitLab without logging in
if (!el) {
return;
}
import('./components/app.vue')
.then(({ default: FrequentItems }) => {
// eslint-disable-next-line no-new
new Vue({
el,
data() {
const { dataset } = this.$options.el;
const item = {
id: Number(dataset[`${key}Id`]),
name: dataset[`${key}Name`],
namespace: dataset[`${key}Namespace`],
webUrl: dataset[`${key}WebUrl`],
avatarUrl: dataset[`${key}AvatarUrl`] || null,
lastAccessedOn: Date.now(),
};
return {
currentUserName: dataset.userName,
currentItem: item,
};
},
render(createElement) {
return createElement(FrequentItems, {
props: {
namespace,
currentUserName: this.currentUserName,
currentItem: this.currentItem,
},
});
},
});
})
.catch(() => {});
};
export default function initFrequentItemDropdowns() {
frequentItemDropdowns.forEach(dropdown => {
const { namespace, key } = dropdown;
const el = document.getElementById(`js-${namespace}-dropdown`);
const navEl = document.getElementById(`nav-${namespace}-dropdown`);
// Don't do anything if element doesn't exist (No groups dropdown)
// This is for when the user accesses GitLab without logging in
if (!navEl) {
if (!el || !navEl) {
return;
}
import('./components/app.vue')
.then(({ default: FrequentItems }) => {
// eslint-disable-next-line no-new
new Vue({
el,
data() {
const { dataset } = this.$options.el;
const item = {
id: Number(dataset[`${key}Id`]),
name: dataset[`${key}Name`],
namespace: dataset[`${key}Namespace`],
webUrl: dataset[`${key}WebUrl`],
avatarUrl: dataset[`${key}AvatarUrl`] || null,
lastAccessedOn: Date.now(),
};
return {
currentUserName: dataset.userName,
currentItem: item,
};
},
render(createElement) {
return createElement(FrequentItems, {
props: {
namespace,
currentUserName: this.currentUserName,
currentItem: this.currentItem,
},
});
},
});
})
.catch(() => {});
$(navEl).on('shown.bs.dropdown', () => {
initFrequentItemList(namespace, key);
eventHub.$emit(`${namespace}-dropdownOpen`);
});
});
......
---
title: Add Caching to BitBucket Server Import for pull requests
merge_request: 45790
author: Simon Schrottner
type: performance
---
title: Fix tracking of frequently visited projects / groups
merge_request: 46348
author:
type: fixed
---
title: Add licensed check for wip limits
merge_request: 46387
author:
type: fixed
......@@ -22,7 +22,7 @@ describe('ee/BoardSettingsSidebar', () => {
const listId = 1;
let mock;
const createComponent = (actions = {}) => {
const createComponent = (actions = {}, isWipLimitsOn = false) => {
storeActions = actions;
const store = new Vuex.Store({
......@@ -34,6 +34,11 @@ describe('ee/BoardSettingsSidebar', () => {
wrapper = shallowMount(BoardSettingsSidebar, {
store,
localVue,
provide: {
glFeatures: {
wipLimits: isWipLimitsOn,
},
},
stubs: {
'board-settings-sidebar-wip-limit': BoardSettingsWipLimit,
'board-settings-list-types': BoardSettingsListTypes,
......@@ -59,7 +64,7 @@ describe('ee/BoardSettingsSidebar', () => {
list_type: 'label',
});
createComponent();
createComponent({}, true);
expect(wrapper.find(BoardSettingsWipLimit).exists()).toBe(true);
});
......
......@@ -8,6 +8,8 @@ module API
helpers ::API::Helpers::BadgesHelpers
feature_category :continuous_integration
helpers do
def find_source_if_admin(source_type)
source = find_source(source_type, params[:id])
......
......@@ -9,6 +9,8 @@ module API
before { authenticate! }
feature_category :boards
helpers do
def board_parent
user_project
......
......@@ -10,6 +10,8 @@ module API
after_validation { content_type "application/json" }
feature_category :source_code_management
before do
require_repository_enabled!
authorize! :download_code, user_project
......
......@@ -4,6 +4,8 @@ module API
class BroadcastMessages < ::API::Base
include PaginationParams
feature_category :navigation
resource :broadcast_messages do
helpers do
def find_message
......
......@@ -7,6 +7,8 @@ module API
before { authenticate! }
feature_category :continuous_integration
params do
requires :id, type: String, desc: 'The ID of a project'
end
......
......@@ -7,6 +7,8 @@ module API
before { authenticate_non_get! }
feature_category :continuous_integration
params do
requires :id, type: String, desc: 'The project ID'
end
......
......@@ -7,6 +7,8 @@ module API
content_type :txt, 'text/plain'
feature_category :continuous_integration
resource :runners do
desc 'Registers a new Runner' do
success Entities::RunnerRegistrationDetails
......
......@@ -7,6 +7,8 @@ module API
before { authenticate! }
feature_category :continuous_integration
resource :runners do
desc 'Get runners available for user' do
success Entities::Runner
......
......@@ -4,6 +4,8 @@ require 'mime/types'
module API
class CommitStatuses < ::API::Base
feature_category :continuous_integration
params do
requires :id, type: String, desc: 'The ID of a project'
end
......
......@@ -6,6 +6,8 @@ module API
class Commits < ::API::Base
include PaginationParams
feature_category :source_code_management
before do
require_repository_enabled!
authorize! :download_code, user_project
......
......@@ -4,6 +4,8 @@ module API
class ContainerRegistryEvent < ::API::Base
DOCKER_DISTRIBUTION_EVENTS_V1_JSON = 'application/vnd.docker.distribution.events.v1+json'
feature_category :package_registry
before { authenticate_registry_notification! }
resource :container_registry_event do
......
......@@ -6,6 +6,8 @@ module API
before { authenticate! }
feature_category :continuous_delivery
helpers do
def add_deploy_keys_project(project, attrs = {})
project.deploy_keys_projects.create(attrs)
......
......@@ -4,6 +4,8 @@ module API
class DeployTokens < ::API::Base
include PaginationParams
feature_category :continuous_delivery
helpers do
def scope_params
scopes = params.delete(:scopes)
......
......@@ -7,6 +7,8 @@ module API
before { authenticate! }
feature_category :continuous_delivery
params do
requires :id, type: String, desc: 'The project ID'
end
......
......@@ -7,6 +7,8 @@ module API
before { authenticate! }
feature_category :continuous_delivery
params do
requires :id, type: String, desc: 'The project ID'
end
......
......@@ -4,6 +4,8 @@ module API
class ErrorTracking < ::API::Base
before { authenticate! }
feature_category :error_tracking
params do
requires :id, type: String, desc: 'The ID of a project'
end
......
......@@ -8,6 +8,8 @@ module API
allow_access_with_scope :read_user, if: -> (request) { request.get? }
feature_category :users
resource :events do
desc "List currently authenticated user's events" do
detail 'This feature was introduced in GitLab 9.3.'
......
......@@ -7,6 +7,8 @@ module API
ENVIRONMENT_SCOPE_ENDPOINT_REQUIREMENTS = FeatureFlags::FEATURE_FLAG_ENDPOINT_REQUIREMENTS
.merge(environment_scope: API::NO_SLASH_URL_PART_REGEX)
feature_category :feature_flags
before do
authorize_read_feature_flags!
end
......
......@@ -7,6 +7,8 @@ module API
FEATURE_FLAG_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
.merge(name: API::NO_SLASH_URL_PART_REGEX)
feature_category :feature_flags
before do
authorize_read_feature_flags!
end
......
......@@ -8,6 +8,8 @@ module API
message.is_a?(String) ? { message: message }.to_json : message.to_json
}
feature_category :feature_flags
before do
authorize_admin_feature_flags_user_lists!
end
......
......@@ -4,6 +4,8 @@ module API
class Features < ::API::Base
before { authenticated_as_admin! }
feature_category :feature_flags
helpers do
def gate_value(params)
case params[:value]
......
......@@ -9,6 +9,8 @@ module API
# Prevents returning plain/text responses for files with .txt extension
after_validation { content_type "application/json" }
feature_category :source_code_management
helpers ::API::Helpers::HeadersHelpers
helpers do
......
......@@ -6,6 +6,8 @@ module API
before { authenticate! }
feature_category :continuous_delivery
params do
requires :id, type: String, desc: 'The ID of a project'
end
......
......@@ -7,6 +7,8 @@ module API
prepend_if_ee('EE::API::BoardsResponses') # rubocop: disable Cop/InjectEnterpriseEditionModule
feature_category :boards
before do
authenticate!
end
......
......@@ -6,6 +6,8 @@ module API
before { authenticate! }
feature_category :kubernetes_management
params do
requires :id, type: String, desc: 'The ID of the group'
end
......
......@@ -8,6 +8,8 @@ module API
before { authorize_read_group_container_images! }
feature_category :package_registry
REPOSITORY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
tag_name: API::NO_SLASH_URL_PART_REGEX)
......
......@@ -10,6 +10,8 @@ module API
authorize! :admin_group, user_group
end
feature_category :importers
params do
requires :id, type: String, desc: 'The ID of a group'
end
......
......@@ -2,6 +2,8 @@
module API
class GroupImport < ::API::Base
feature_category :importers
helpers Helpers::FileUploadHelpers
helpers do
......
......@@ -7,6 +7,8 @@ module API
before { authenticate! }
feature_category :issue_tracking
params do
requires :id, type: String, desc: 'The ID of a group'
end
......
......@@ -7,6 +7,8 @@ module API
before { authenticate! }
feature_category :issue_tracking
params do
requires :id, type: String, desc: 'The ID of a group'
end
......
......@@ -7,6 +7,8 @@ module API
before { authenticate! }
before { authorize! :admin_build, user_group }
feature_category :continuous_integration
params do
requires :id, type: String, desc: 'The ID of a group'
end
......
......@@ -7,6 +7,8 @@ module API
before { authenticate_non_get! }
feature_category :subgroups
helpers Helpers::GroupsHelpers
helpers do
......
......@@ -4,11 +4,14 @@ module Gitlab
module BitbucketServerImport
class Importer
attr_reader :recover_missing_commits
attr_reader :project, :project_key, :repository_slug, :client, :errors, :users
attr_reader :project, :project_key, :repository_slug, :client, :errors, :users, :already_imported_cache_key
attr_accessor :logger
REMOTE_NAME = 'bitbucket_server'
BATCH_SIZE = 100
# The base cache key to use for tracking already imported objects.
ALREADY_IMPORTED_CACHE_KEY =
'bitbucket_server-importer/already-imported/%{project}/%{collection}'
TempBranch = Struct.new(:name, :sha)
......@@ -36,6 +39,12 @@ module Gitlab
@users = {}
@temp_branches = []
@logger = Gitlab::Import::Logger.build
@already_imported_cache_key = ALREADY_IMPORTED_CACHE_KEY %
{ project: project.id, collection: collection_method }
end
def collection_method
:pull_requests
end
def execute
......@@ -48,6 +57,7 @@ module Gitlab
log_info(stage: "complete")
Gitlab::Cache::Import::Caching.expire(already_imported_cache_key, 15.minutes.to_i)
true
end
......@@ -167,6 +177,7 @@ module Gitlab
# on the remote server. Then we have to issue a `git fetch` to download these
# branches.
def import_pull_requests
log_info(stage: 'import_pull_requests', message: 'starting')
pull_requests = client.pull_requests(project_key, repository_slug).to_a
# Creating branches on the server and fetching the newly-created branches
......@@ -176,7 +187,11 @@ module Gitlab
restore_branches(batch) if recover_missing_commits
batch.each do |pull_request|
import_bitbucket_pull_request(pull_request)
if already_imported?(pull_request)
log_info(stage: 'import_pull_requests', message: 'already imported', iid: pull_request.iid)
else
import_bitbucket_pull_request(pull_request)
end
rescue StandardError => e
Gitlab::ErrorTracking.log_exception(
e,
......@@ -189,6 +204,19 @@ module Gitlab
end
end
# Returns true if the given object has already been imported, false
# otherwise.
#
# object - The object to check.
def already_imported?(pull_request)
Gitlab::Cache::Import::Caching.set_includes?(already_imported_cache_key, pull_request.iid)
end
# Marks the given object as "already imported".
def mark_as_imported(pull_request)
Gitlab::Cache::Import::Caching.set_add(already_imported_cache_key, pull_request.iid)
end
def delete_temp_branches
@temp_branches.each do |branch|
client.delete_branch(project_key, repository_slug, branch.name, branch.sha)
......@@ -236,6 +264,7 @@ module Gitlab
end
log_info(stage: 'import_bitbucket_pull_requests', message: 'finished', iid: pull_request.iid)
mark_as_imported(pull_request)
end
def import_pull_request_comments(pull_request, merge_request)
......
......@@ -6,7 +6,7 @@ module CrystalballEnv
extend self
def start!
return unless ENV['CRYSTALBALL'] && ENV['CI_PIPELINE_SOURCE'] == 'schedule'
return unless ENV['CRYSTALBALL'] && ENV['CI_PIPELINE_SOURCE'] == 'schedule' && ENV['FREQUENCY'] == '2-hourly'
require 'crystalball'
require_relative '../tooling/lib/tooling/crystalball/coverage_lines_execution_detector'
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Frequently visited items', :js do
let_it_be(:user) { create(:user) }
before do
sign_in(user)
end
context 'for projects' do
let_it_be(:project) { create(:project, :public) }
it 'increments localStorage counter when visiting the project' do
visit project_path(project)
frequent_projects = nil
wait_for('localStorage frequent-projects') do
frequent_projects = page.evaluate_script("localStorage['#{user.username}/frequent-projects']")
frequent_projects.present?
end
expect(Gitlab::Json.parse(frequent_projects)).to contain_exactly(a_hash_including('id' => project.id, 'frequency' => 1))
end
end
context 'for groups' do
let_it_be(:group) { create(:group, :public) }
it 'increments localStorage counter when visiting the group' do
visit group_path(group)
frequent_groups = nil
wait_for('localStorage frequent-groups') do
frequent_groups = page.evaluate_script("localStorage['#{user.username}/frequent-groups']")
frequent_groups.present?
end
expect(Gitlab::Json.parse(frequent_groups)).to contain_exactly(a_hash_including('id' => group.id, 'frequency' => 1))
end
end
end
......@@ -294,7 +294,7 @@ RSpec.describe 'User interacts with awards' do
end
end
it 'toggles the smiley emoji on a note', :js do
it 'toggles the smiley emoji on a note', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/267525' do
toggle_smiley_emoji(true)
within('.note-body') do
......
......@@ -3,6 +3,7 @@ import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
import createStore from '~/diffs/store/modules';
import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vue';
import { CENTERED_LIMITED_CONTAINER_CLASSES } from '~/diffs/constants';
import eventHub from '~/diffs/event_hub';
const propsData = {
limited: true,
......@@ -76,13 +77,13 @@ describe('CollapsedFilesWarning', () => {
expect(wrapper.find('[data-testid="root"]').exists()).toBe(false);
});
it('triggers the expandAllFiles action when the alert action button is clicked', () => {
it('emits the `mr:diffs:expandAllFiles` event when the alert action button is clicked', () => {
createComponent({}, { full: true });
jest.spyOn(wrapper.vm.$store, 'dispatch').mockReturnValue(undefined);
jest.spyOn(eventHub, '$emit');
getAlertActionButton().vm.$emit('click');
expect(wrapper.vm.$store.dispatch).toHaveBeenCalledWith('diffs/expandAllFiles', undefined);
expect(eventHub.$emit).toHaveBeenCalledWith('mr:diffs:expandAllFiles');
});
});
......@@ -9,6 +9,8 @@ import DiffFileComponent from '~/diffs/components/diff_file.vue';
import DiffFileHeaderComponent from '~/diffs/components/diff_file_header.vue';
import DiffContentComponent from '~/diffs/components/diff_content.vue';
import eventHub from '~/diffs/event_hub';
import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
function changeViewer(store, index, { automaticallyCollapsed, manuallyCollapsed, name }) {
......@@ -138,6 +140,30 @@ describe('DiffFile', () => {
});
describe('collapsing', () => {
describe('`mr:diffs:expandAllFiles` event', () => {
beforeEach(() => {
jest.spyOn(wrapper.vm, 'handleToggle').mockImplementation(() => {});
});
it('performs the normal file toggle when the file is collapsed', async () => {
makeFileAutomaticallyCollapsed(store);
await wrapper.vm.$nextTick();
eventHub.$emit('mr:diffs:expandAllFiles');
expect(wrapper.vm.handleToggle).toHaveBeenCalledTimes(1);
});
it('does nothing when the file is not collapsed', async () => {
eventHub.$emit('mr:diffs:expandAllFiles');
await wrapper.vm.$nextTick();
expect(wrapper.vm.handleToggle).not.toHaveBeenCalled();
});
});
describe('user collapsed', () => {
beforeEach(() => {
makeFileManuallyCollapsed(store);
......
......@@ -27,7 +27,6 @@ import {
scrollToLineIfNeededInline,
scrollToLineIfNeededParallel,
loadCollapsedDiff,
expandAllFiles,
toggleFileDiscussions,
saveDiffDiscussion,
setHighlightedRow,
......@@ -658,23 +657,6 @@ describe('DiffsStoreActions', () => {
});
});
describe('expandAllFiles', () => {
it('should change the collapsed prop from the diffFiles', done => {
testAction(
expandAllFiles,
null,
{},
[
{
type: types.EXPAND_ALL_FILES,
},
],
[],
done,
);
});
});
describe('toggleFileDiscussions', () => {
it('should dispatch collapseDiscussion when all discussions are expanded', () => {
const getters = {
......
......@@ -126,21 +126,6 @@ describe('DiffsStoreMutations', () => {
});
});
describe('EXPAND_ALL_FILES', () => {
it('should change the collapsed prop from diffFiles', () => {
const diffFile = {
viewer: {
automaticallyCollapsed: true,
},
};
const state = { expandAllFiles: true, diffFiles: [diffFile] };
mutations[types.EXPAND_ALL_FILES](state);
expect(state.diffFiles[0].viewer.automaticallyCollapsed).toEqual(false);
});
});
describe('ADD_CONTEXT_LINES', () => {
it('should call utils.addContextLines with proper params', () => {
const options = {
......
......@@ -22,7 +22,17 @@ RSpec.describe 'Every API endpoint' do
completed_classes = [
::API::Users, ::API::Issues, ::API::AccessRequests, ::API::Admin::Ci::Variables,
::API::Admin::InstanceClusters, ::API::Admin::Sidekiq, ::API::Appearance,
::API::Applications, ::API::Avatar, ::API::AwardEmoji
::API::Applications, ::API::Avatar, ::API::AwardEmoji, API::Badges,
::API::Boards, ::API::Branches, ::API::BroadcastMessages, ::API::Ci::Pipelines,
::API::Ci::PipelineSchedules, ::API::Ci::Runners, ::API::Ci::Runner,
::API::Commits, ::API::CommitStatuses, ::API::ContainerRegistryEvent,
::API::DeployKeys, ::API::DeployTokens, ::API::Deployments, ::API::Environments,
::API::ErrorTracking, ::API::Events, ::API::FeatureFlags, ::API::FeatureFlagScopes,
::API::FeatureFlagsUserLists, ::API::Features, ::API::Files, ::API::FreezePeriods,
::API::GroupBoards, ::API::GroupClusters, ::API::GroupExport, ::API::GroupImport,
::API::GroupLabels, ::API::GroupMilestones, ::API::Groups,
::API::GroupContainerRepositories, ::API::GroupVariables
]
next unless completed_classes.include?(klass)
......
......@@ -115,6 +115,12 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
allow(subject.client).to receive(:pull_requests).and_return([pull_request])
end
# As we are using Caching with redis, it is best to clean the cache after each test run, else we need to wait for
# the expiration by the importer
after do
Gitlab::Cache::Import::Caching.expire(subject.already_imported_cache_key, 0)
end
it 'imports merge event' do
expect(subject.client).to receive(:activities).and_return([merge_event])
......@@ -463,6 +469,47 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
subject.execute
end
describe 'import pull requests with caching' do
let(:pull_request_already_imported) do
instance_double(
BitbucketServer::Representation::PullRequest,
iid: 11)
end
let(:pull_request_to_be_imported) do
instance_double(
BitbucketServer::Representation::PullRequest,
iid: 12,
source_branch_sha: sample.commits.last,
source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
target_branch_sha: sample.commits.first,
target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
title: 'This is a title',
description: 'This is a test pull request',
state: 'merged',
author: 'Test Author',
author_email: pull_request_author.email,
author_username: pull_request_author.username,
created_at: Time.now,
updated_at: Time.now,
raw: {},
merged?: true)
end
before do
Gitlab::Cache::Import::Caching.set_add(subject.already_imported_cache_key, pull_request_already_imported.iid)
allow(subject.client).to receive(:pull_requests).and_return([pull_request_to_be_imported, pull_request_already_imported])
end
it 'only imports one Merge Request, as the other on is in the cache' do
expect(subject.client).to receive(:activities).and_return([merge_event])
expect { subject.execute }.to change { MergeRequest.count }.by(1)
expect(Gitlab::Cache::Import::Caching.set_includes?(subject.already_imported_cache_key, pull_request_already_imported.iid)).to eq(true)
expect(Gitlab::Cache::Import::Caching.set_includes?(subject.already_imported_cache_key, pull_request_to_be_imported.iid)).to eq(true)
end
end
end
describe 'inaccessible branches' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment