Commit 242358bb authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@13-3-stable-ee

parent 517f2549
...@@ -23,6 +23,7 @@ import { addInProgressImportToStore } from '../utils/cache_update'; ...@@ -23,6 +23,7 @@ import { addInProgressImportToStore } from '../utils/cache_update';
import { import {
debounceWait, debounceWait,
dropdownLabel, dropdownLabel,
userMappingsPageSize,
previousImportsMessage, previousImportsMessage,
tableConfig, tableConfig,
userMappingMessage, userMappingMessage,
...@@ -74,12 +75,15 @@ export default { ...@@ -74,12 +75,15 @@ export default {
}, },
data() { data() {
return { return {
hasMoreUsers: false,
isFetching: false, isFetching: false,
isLoadingMoreUsers: false,
isSubmitting: false, isSubmitting: false,
searchTerm: '', searchTerm: '',
selectedProject: undefined, selectedProject: undefined,
selectState: null, selectState: null,
userMappings: [], userMappings: [],
userMappingsStartAt: 0,
users: [], users: [],
}; };
}, },
...@@ -101,6 +105,9 @@ export default { ...@@ -101,6 +105,9 @@ export default {
? `jira-import::${this.selectedProject}-${this.numberOfPreviousImports + 1}` ? `jira-import::${this.selectedProject}-${this.numberOfPreviousImports + 1}`
: 'jira-import::KEY-1'; : 'jira-import::KEY-1';
}, },
isInitialLoadingState() {
return this.isLoadingMoreUsers && !this.hasMoreUsers;
},
}, },
watch: { watch: {
searchTerm: debounce(function debouncedUserSearch() { searchTerm: debounce(function debouncedUserSearch() {
...@@ -108,31 +115,45 @@ export default { ...@@ -108,31 +115,45 @@ export default {
}, debounceWait), }, debounceWait),
}, },
mounted() { mounted() {
this.getJiraUserMapping();
this.searchUsers()
.then(data => {
this.initialUsers = data;
})
.catch(() => {});
},
methods: {
getJiraUserMapping() {
this.isLoadingMoreUsers = true;
this.$apollo this.$apollo
.mutate({ .mutate({
mutation: getJiraUserMappingMutation, mutation: getJiraUserMappingMutation,
variables: { variables: {
input: { input: {
projectPath: this.projectPath, projectPath: this.projectPath,
startAt: this.userMappingsStartAt,
}, },
}, },
}) })
.then(({ data }) => { .then(({ data }) => {
if (data.jiraImportUsers.errors.length) { if (data.jiraImportUsers.errors.length) {
this.$emit('error', data.jiraImportUsers.errors.join('. ')); this.$emit('error', data.jiraImportUsers.errors.join('. '));
} else { return;
this.userMappings = data.jiraImportUsers.jiraUsers;
} }
})
.catch(() => this.$emit('error', __('There was an error retrieving the Jira users.')));
this.searchUsers() this.userMappings = this.userMappings.concat(data.jiraImportUsers.jiraUsers);
.then(data => { this.hasMoreUsers = data.jiraImportUsers.jiraUsers.length === userMappingsPageSize;
this.initialUsers = data; this.userMappingsStartAt += userMappingsPageSize;
}) })
.catch(() => {}); .catch(() => {
this.$emit('error', __('There was an error retrieving the Jira users.'));
})
.finally(() => {
this.isLoadingMoreUsers = false;
});
}, },
methods: {
searchUsers() { searchUsers() {
const params = { const params = {
active: true, active: true,
...@@ -187,7 +208,9 @@ export default { ...@@ -187,7 +208,9 @@ export default {
this.selectedProject = undefined; this.selectedProject = undefined;
} }
}) })
.catch(() => this.$emit('error', __('There was an error importing the Jira project.'))) .catch(() => {
this.$emit('error', __('There was an error importing the Jira project.'));
})
.finally(() => { .finally(() => {
this.isSubmitting = false; this.isSubmitting = false;
}); });
...@@ -280,9 +303,7 @@ export default { ...@@ -280,9 +303,7 @@ export default {
> >
<gl-search-box-by-type v-model.trim="searchTerm" class="m-2" /> <gl-search-box-by-type v-model.trim="searchTerm" class="m-2" />
<div v-if="isFetching" class="gl-text-center"> <gl-loading-icon v-if="isFetching" />
<gl-loading-icon />
</div>
<gl-new-dropdown-item <gl-new-dropdown-item
v-for="user in users" v-for="user in users"
...@@ -300,6 +321,17 @@ export default { ...@@ -300,6 +321,17 @@ export default {
</template> </template>
</gl-table> </gl-table>
<gl-loading-icon v-if="isInitialLoadingState" />
<gl-button
v-if="hasMoreUsers"
:loading="isLoadingMoreUsers"
data-testid="load-more-users-button"
@click="getJiraUserMapping"
>
{{ __('Load more users') }}
</gl-button>
<div class="footer-block row-content-block d-flex justify-content-between"> <div class="footer-block row-content-block d-flex justify-content-between">
<gl-button <gl-button
type="submit" type="submit"
......
...@@ -27,3 +27,6 @@ export const tableConfig = [ ...@@ -27,3 +27,6 @@ export const tableConfig = [
export const userMappingMessage = __(`Jira users have been imported from the configured Jira export const userMappingMessage = __(`Jira users have been imported from the configured Jira
instance. They can be mapped by selecting a GitLab user from the dropdown in the "GitLab username" instance. They can be mapped by selecting a GitLab user from the dropdown in the "GitLab username"
column. When the form appears, the dropdown defaults to the user conducting the import.`); column. When the form appears, the dropdown defaults to the user conducting the import.`);
// pageSize must match the MAX_USERS value in app/services/jira_import/users_mapper_service.rb
export const userMappingsPageSize = 50;
...@@ -129,6 +129,10 @@ module AuthenticatesWithTwoFactor ...@@ -129,6 +129,10 @@ module AuthenticatesWithTwoFactor
def user_changed?(user) def user_changed?(user)
return false unless session[:user_updated_at] return false unless session[:user_updated_at]
user.updated_at != session[:user_updated_at] # See: https://gitlab.com/gitlab-org/gitlab/-/issues/244638
# Rounding errors happen when the user is updated, as the Rails ActiveRecord
# object has higher precision than what is stored in the database, therefore
# using .to_i to force truncation to the timestamp
user.updated_at.to_i != session[:user_updated_at].to_i
end end
end end
...@@ -426,6 +426,8 @@ class ApplicationSetting < ApplicationRecord ...@@ -426,6 +426,8 @@ class ApplicationSetting < ApplicationRecord
end end
def self.create_from_defaults def self.create_from_defaults
check_schema!
transaction(requires_new: true) do transaction(requires_new: true) do
super super
end end
...@@ -434,6 +436,22 @@ class ApplicationSetting < ApplicationRecord ...@@ -434,6 +436,22 @@ class ApplicationSetting < ApplicationRecord
current_without_cache current_without_cache
end end
# Due to the frequency with which settings are accessed, it is
# likely that during a backup restore a running GitLab process
# will insert a new `application_settings` row before the
# constraints have been added to the table. This would add an
# extra row with ID 1 and prevent the primary key constraint from
# being added, which made ActiveRecord throw a
# IrreversibleOrderError anytime the settings were accessed
# (https://gitlab.com/gitlab-org/gitlab/-/issues/36405). To
# prevent this from happening, we do a sanity check that the
# primary key constraint is present before inserting a new entry.
def self.check_schema!
return if ActiveRecord::Base.connection.primary_key(self.table_name).present?
raise "The `#{self.table_name}` table is missing a primary key constraint in the database schema"
end
# By default, the backend is Rails.cache, which uses # By default, the backend is Rails.cache, which uses
# ActiveSupport::Cache::RedisStore. Since loading ApplicationSetting # ActiveSupport::Cache::RedisStore. Since loading ApplicationSetting
# can cause a significant amount of load on Redis, let's cache it in # can cause a significant amount of load on Redis, let's cache it in
......
...@@ -42,7 +42,7 @@ module Git ...@@ -42,7 +42,7 @@ module Git
push_service_class = push_service_class_for(ref_type) push_service_class = push_service_class_for(ref_type)
create_bulk_push_event = changes.size > Gitlab::CurrentSettings.push_event_activities_limit create_bulk_push_event = changes.size > Gitlab::CurrentSettings.push_event_activities_limit
merge_request_branches = merge_request_branches_for(changes) merge_request_branches = merge_request_branches_for(ref_type, changes)
changes.each do |change| changes.each do |change|
push_service_class.new( push_service_class.new(
...@@ -74,8 +74,10 @@ module Git ...@@ -74,8 +74,10 @@ module Git
Git::BranchPushService Git::BranchPushService
end end
def merge_request_branches_for(changes) def merge_request_branches_for(ref_type, changes)
@merge_requests_branches ||= MergeRequests::PushedBranchesService.new(project, current_user, changes: changes).execute return [] if ref_type == :tag
MergeRequests::PushedBranchesService.new(project, current_user, changes: changes).execute
end end
end end
end end
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
module JiraImport module JiraImport
class UsersMapperService class UsersMapperService
# MAX_USERS must match the pageSize value in app/assets/javascripts/jira_import/utils/constants.js
MAX_USERS = 50 MAX_USERS = 50
attr_reader :jira_service, :start_at attr_reader :jira_service, :start_at
......
---
title: Fix Jira importer user mapping limit
merge_request: 40310
author:
type: fixed
---
title: Fix auto-deploy-image external chart dependencies
merge_request: 40730
author:
type: fixed
---
title: Update the 2FA user update check to account for rounding errors
merge_request: 41327
author:
type: fixed
---
title: Fix wrong caching logic in ProcessRefChangesService
merge_request: 40821
author:
type: fixed
---
title: Coerce string object storage options to booleans
merge_request: 39901
author:
type: fixed
---
title: Fix ActiveRecord::IrreversibleOrderError during restore from backup
merge_request: 40789
author:
type: fixed
.dast-auto-deploy: .dast-auto-deploy:
image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v1.0.0" image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v1.0.2"
dast_environment_deploy: dast_environment_deploy:
extends: .dast-auto-deploy extends: .dast-auto-deploy
......
.auto-deploy: .auto-deploy:
image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v1.0.0" image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v1.0.2"
dependencies: [] dependencies: []
include: include:
......
...@@ -14501,6 +14501,9 @@ msgstr "" ...@@ -14501,6 +14501,9 @@ msgstr ""
msgid "Load more" msgid "Load more"
msgstr "" msgstr ""
msgid "Load more users"
msgstr ""
msgid "Loading" msgid "Loading"
msgstr "" msgstr ""
......
...@@ -10,6 +10,7 @@ import { ...@@ -10,6 +10,7 @@ import {
imports, imports,
issuesPath, issuesPath,
jiraProjects, jiraProjects,
jiraUsersResponse,
projectId, projectId,
projectPath, projectPath,
userMappings as defaultUserMappings, userMappings as defaultUserMappings,
...@@ -38,7 +39,10 @@ describe('JiraImportForm', () => { ...@@ -38,7 +39,10 @@ describe('JiraImportForm', () => {
const getHeader = name => getByRole(wrapper.element, 'columnheader', { name }); const getHeader = name => getByRole(wrapper.element, 'columnheader', { name });
const findLoadMoreUsersButton = () => wrapper.find('[data-testid="load-more-users-button"]');
const mountComponent = ({ const mountComponent = ({
hasMoreUsers = false,
isSubmitting = false, isSubmitting = false,
loading = false, loading = false,
mutate = mutateSpy, mutate = mutateSpy,
...@@ -55,6 +59,7 @@ describe('JiraImportForm', () => { ...@@ -55,6 +59,7 @@ describe('JiraImportForm', () => {
projectPath, projectPath,
}, },
data: () => ({ data: () => ({
hasMoreUsers,
isFetching: false, isFetching: false,
isSubmitting, isSubmitting,
searchTerm: '', searchTerm: '',
...@@ -300,6 +305,7 @@ describe('JiraImportForm', () => { ...@@ -300,6 +305,7 @@ describe('JiraImportForm', () => {
variables: { variables: {
input: { input: {
projectPath, projectPath,
startAt: 0,
}, },
}, },
}; };
...@@ -318,4 +324,53 @@ describe('JiraImportForm', () => { ...@@ -318,4 +324,53 @@ describe('JiraImportForm', () => {
}); });
}); });
}); });
describe('load more users button', () => {
describe('when all users have been loaded', () => {
it('is not shown', () => {
wrapper = mountComponent();
expect(findLoadMoreUsersButton().exists()).toBe(false);
});
});
describe('when all users have not been loaded', () => {
it('is shown', () => {
wrapper = mountComponent({ hasMoreUsers: true });
expect(findLoadMoreUsersButton().exists()).toBe(true);
});
});
describe('when clicked', () => {
beforeEach(() => {
mutateSpy = jest.fn(() =>
Promise.resolve({
data: {
jiraImportStart: { errors: [] },
jiraImportUsers: { jiraUsers: jiraUsersResponse, errors: [] },
},
}),
);
wrapper = mountComponent({ hasMoreUsers: true });
});
it('calls the GraphQL user mapping mutation', async () => {
const mutationArguments = {
mutation: getJiraUserMappingMutation,
variables: {
input: {
projectPath,
startAt: 0,
},
},
};
findLoadMoreUsersButton().vm.$emit('click');
expect(mutateSpy).toHaveBeenCalledWith(expect.objectContaining(mutationArguments));
});
});
});
}); });
import getJiraImportDetailsQuery from '~/jira_import/queries/get_jira_import_details.query.graphql'; import getJiraImportDetailsQuery from '~/jira_import/queries/get_jira_import_details.query.graphql';
import { IMPORT_STATE } from '~/jira_import/utils/jira_import_utils'; import { IMPORT_STATE } from '~/jira_import/utils/jira_import_utils';
import { userMappingsPageSize } from '~/jira_import/utils/constants';
export const fullPath = 'gitlab-org/gitlab-test'; export const fullPath = 'gitlab-org/gitlab-test';
...@@ -87,6 +88,8 @@ export const jiraProjects = [ ...@@ -87,6 +88,8 @@ export const jiraProjects = [
{ text: 'Migrate to GitLab (MTG)', value: 'MTG' }, { text: 'Migrate to GitLab (MTG)', value: 'MTG' },
]; ];
export const jiraUsersResponse = new Array(userMappingsPageSize);
export const imports = [ export const imports = [
{ {
jiraProjectKey: 'MTG', jiraProjectKey: 'MTG',
......
...@@ -115,6 +115,16 @@ RSpec.describe Gitlab::CurrentSettings do ...@@ -115,6 +115,16 @@ RSpec.describe Gitlab::CurrentSettings do
expect(settings).to have_attributes(settings_from_defaults) expect(settings).to have_attributes(settings_from_defaults)
end end
context 'when ApplicationSettings does not have a primary key' do
before do
allow(ActiveRecord::Base.connection).to receive(:primary_key).with('application_settings').and_return(nil)
end
it 'raises an exception if ApplicationSettings does not have a primary key' do
expect { described_class.current_application_settings }.to raise_error(/table is missing a primary key constraint/)
end
end
context 'with pending migrations' do context 'with pending migrations' do
let(:current_settings) { described_class.current_application_settings } let(:current_settings) { described_class.current_application_settings }
......
...@@ -650,6 +650,16 @@ RSpec.describe ApplicationSetting do ...@@ -650,6 +650,16 @@ RSpec.describe ApplicationSetting do
end end
end end
context 'when ApplicationSettings does not have a primary key' do
before do
allow(ActiveRecord::Base.connection).to receive(:primary_key).with(described_class.table_name).and_return(nil)
end
it 'raises an exception' do
expect { described_class.create_from_defaults }.to raise_error(/table is missing a primary key constraint/)
end
end
describe '#disabled_oauth_sign_in_sources=' do describe '#disabled_oauth_sign_in_sources=' do
before do before do
allow(Devise).to receive(:omniauth_providers).and_return([:github]) allow(Devise).to receive(:omniauth_providers).and_return([:github])
......
...@@ -172,23 +172,31 @@ RSpec.describe Git::ProcessRefChangesService do ...@@ -172,23 +172,31 @@ RSpec.describe Git::ProcessRefChangesService do
[ [
{ index: 0, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789012', ref: "#{ref_prefix}/create1" }, { index: 0, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789012', ref: "#{ref_prefix}/create1" },
{ index: 1, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789013', ref: "#{ref_prefix}/create2" }, { index: 1, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789013', ref: "#{ref_prefix}/create2" },
{ index: 2, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789014', ref: "#{ref_prefix}/create3" } { index: 2, oldrev: Gitlab::Git::BLANK_SHA, newrev: '789014', ref: "#{ref_prefix}/create3" },
{ index: 3, oldrev: '789015', newrev: '789016', ref: "#{ref_prefix}/changed1" },
{ index: 4, oldrev: '789017', newrev: '789018', ref: "#{ref_prefix}/changed2" },
{ index: 5, oldrev: '789019', newrev: Gitlab::Git::BLANK_SHA, ref: "#{ref_prefix}/removed1" },
{ index: 6, oldrev: '789020', newrev: Gitlab::Git::BLANK_SHA, ref: "#{ref_prefix}/removed2" }
] ]
end end
let(:git_changes) { double(branch_changes: branch_changes, tag_changes: tag_changes) } let(:git_changes) { double(branch_changes: branch_changes, tag_changes: tag_changes) }
it 'schedules job for existing merge requests' do before do
expect_next_instance_of(MergeRequests::PushedBranchesService) do |service| allow(MergeRequests::PushedBranchesService).to receive(:new).and_return(
expect(service).to receive(:execute).and_return(%w(create1 create2)) double(execute: %w(create1 create2)), double(execute: %w(changed1)), double(execute: %w(removed2))
)
end end
it 'schedules job for existing merge requests' do
expect(UpdateMergeRequestsWorker).to receive(:perform_async) expect(UpdateMergeRequestsWorker).to receive(:perform_async)
.with(project.id, user.id, Gitlab::Git::BLANK_SHA, '789012', "#{ref_prefix}/create1").ordered .with(project.id, user.id, Gitlab::Git::BLANK_SHA, '789012', "#{ref_prefix}/create1").ordered
expect(UpdateMergeRequestsWorker).to receive(:perform_async) expect(UpdateMergeRequestsWorker).to receive(:perform_async)
.with(project.id, user.id, Gitlab::Git::BLANK_SHA, '789013', "#{ref_prefix}/create2").ordered .with(project.id, user.id, Gitlab::Git::BLANK_SHA, '789013', "#{ref_prefix}/create2").ordered
expect(UpdateMergeRequestsWorker).not_to receive(:perform_async) expect(UpdateMergeRequestsWorker).to receive(:perform_async)
.with(project.id, user.id, Gitlab::Git::BLANK_SHA, '789014', "#{ref_prefix}/create3").ordered .with(project.id, user.id, '789015', '789016', "#{ref_prefix}/changed1").ordered
expect(UpdateMergeRequestsWorker).to receive(:perform_async)
.with(project.id, user.id, '789020', Gitlab::Git::BLANK_SHA, "#{ref_prefix}/removed2").ordered
subject.execute subject.execute
end end
......
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
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