Commit 9f788640 authored by Illya Klymov's avatar Illya Klymov Committed by Natalia Tepluhina

Expose information about what will not be migrated during GitLab migration

parent ed5fbfb7
......@@ -44,7 +44,7 @@ export default {
:size="16"
name="information-o"
:title="
s__('BulkImports|Re-import creates a new group. It does not sync with the existing group.')
s__('BulkImport|Re-import creates a new group. It does not sync with the existing group.')
"
class="gl-ml-3"
/>
......
<script>
import {
GlAlert,
GlButton,
GlEmptyState,
GlIcon,
......@@ -12,7 +13,7 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
import createFlash from '~/flash';
import { s__, __, n__ } from '~/locale';
import { s__, __, n__, sprintf } from '~/locale';
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
import { getGroupPathAvailability } from '~/rest_api';
import axios from '~/lib/utils/axios_utils';
......@@ -40,6 +41,7 @@ const DEFAULT_TD_CLASSES = 'gl-vertical-align-top!';
export default {
components: {
GlAlert,
GlButton,
GlEmptyState,
GlIcon,
......@@ -79,6 +81,7 @@ export default {
selectedGroupsIds: [],
pendingGroupsIds: [],
importTargets: {},
unavailableFeaturesAlertVisible: true,
};
},
......@@ -200,6 +203,23 @@ export default {
return { start, end, total };
},
unavailableFeatures() {
if (!this.hasGroups) {
return [];
}
return Object.entries(this.bulkImportSourceGroups.versionValidation.features)
.filter(([, { available }]) => available === false)
.map(([k, v]) => ({ title: i18n.features[k] || k, version: v.minVersion }));
},
unavailableFeaturesAlertTitle() {
return sprintf(s__('BulkImport| %{host} is running outdated GitLab version (v%{version})'), {
host: this.sourceUrl,
version: this.bulkImportSourceGroups.versionValidation.features.sourceInstanceVersion,
});
},
},
watch: {
......@@ -471,6 +491,38 @@ export default {
<img :src="$options.gitlabLogo" class="gl-w-6 gl-h-6 gl-mb-2 gl-display-inline gl-mr-2" />
{{ s__('BulkImport|Import groups from GitLab') }}
</h1>
<gl-alert
v-if="unavailableFeatures.length > 0 && unavailableFeaturesAlertVisible"
variant="warning"
:title="unavailableFeaturesAlertTitle"
@dismiss="unavailableFeaturesAlertVisible = false"
>
<gl-sprintf
:message="
s__(
'BulkImport|Following data will not be migrated: %{bullets} Contact system administrator of %{host} to upgrade GitLab if you need this data in your migration',
)
"
>
<template #host>
<gl-link :href="sourceUrl" target="_blank">
{{ sourceUrl }}<gl-icon name="external-link" class="vertical-align-middle" />
</gl-link>
</template>
<template #bullets>
<ul>
<li v-for="feature in unavailableFeatures" :key="feature.title">
<gl-sprintf :message="s__('BulkImport|%{feature} (require v%{version})')">
<template #feature>{{ feature.title }}</template>
<template #version>
<strong>{{ feature.version }}</strong>
</template>
</gl-sprintf>
</li>
</ul>
</template>
</gl-sprintf>
</gl-alert>
<div
class="gl-py-5 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-display-flex"
>
......@@ -490,7 +542,7 @@ export default {
</template>
<template #link>
<gl-link :href="sourceUrl" target="_blank">
{{ sourceUrl }} <gl-icon name="external-link" class="vertical-align-middle" />
{{ sourceUrl }}<gl-icon name="external-link" class="vertical-align-middle" />
</gl-link>
</template>
</gl-sprintf>
......
......@@ -11,6 +11,10 @@ export const i18n = {
),
ERROR_IMPORT: s__('BulkImport|Importing the group failed.'),
ERROR_IMPORT_COMPLETED: s__('BulkImport|Import is finished. Pick another name for re-import'),
features: {
projectMigration: __('projects'),
},
};
export const NEW_NAME_FIELD = 'newName';
......@@ -14,6 +14,9 @@ export const clientTypenames = {
BulkImportPageInfo: 'ClientBulkImportPageInfo',
BulkImportTarget: 'ClientBulkImportTarget',
BulkImportProgress: 'ClientBulkImportProgress',
BulkImportVersionValidation: 'ClientBulkImportVersionValidation',
BulkImportVersionValidationFeature: 'ClientBulkImportVersionValidationFeature',
BulkImportVersionValidationFeatures: 'ClientBulkImportVersionValidationFeatures',
};
function makeLastImportTarget(data) {
......@@ -92,6 +95,18 @@ export function createResolvers({ endpoints }) {
__typename: clientTypenames.BulkImportPageInfo,
...pagination,
},
versionValidation: {
__typename: clientTypenames.BulkImportVersionValidation,
features: {
__typename: clientTypenames.BulkImportVersionValidationFeatures,
sourceInstanceVersion: data.version_validation.features.source_instance_version,
projectMigration: {
__typename: clientTypenames.BulkImportVersionValidationFeature,
available: data.version_validation.features.project_migration.available,
minVersion: data.version_validation.features.project_migration.min_version,
},
},
},
};
return response;
},
......
......@@ -11,5 +11,14 @@ query bulkImportSourceGroups($page: Int = 1, $perPage: Int = 20, $filter: String
total
totalPages
}
versionValidation {
features {
sourceInstanceVersion
projectMigration {
available
minVersion
}
}
}
}
}
......@@ -11,6 +11,7 @@ type ClientBulkImportTarget {
type ClientBulkImportSourceGroupConnection {
nodes: [ClientBulkImportSourceGroup!]!
pageInfo: ClientBulkImportPageInfo!
versionValidation: ClientBulkImportVersionValidation!
}
type ClientBulkImportProgress {
......@@ -46,6 +47,20 @@ type ClientBulkImportNamespaceSuggestion {
suggestions: [String!]!
}
type ClientBulkImportVersionValidation {
features: ClientBulkImportVersionValidationFeatures!
}
type ClientBulkImportVersionValidationFeatures {
project_migration: ClientBulkImportVersionValidationFeature!
sourceInstanceVersion: String!
}
type ClientBulkImportVersionValidationFeature {
available: Boolean!
min_version: String!
}
extend type Query {
bulkImportSourceGroups(
page: Int!
......
......@@ -6087,7 +6087,10 @@ msgstr ""
msgid "Bulk update"
msgstr ""
msgid "BulkImports|Re-import creates a new group. It does not sync with the existing group."
msgid "BulkImport| %{host} is running outdated GitLab version (v%{version})"
msgstr ""
msgid "BulkImport|%{feature} (require v%{version})"
msgstr ""
msgid "BulkImport|Existing groups"
......@@ -6096,6 +6099,9 @@ msgstr ""
msgid "BulkImport|Filter by source group"
msgstr ""
msgid "BulkImport|Following data will not be migrated: %{bullets} Contact system administrator of %{host} to upgrade GitLab if you need this data in your migration"
msgstr ""
msgid "BulkImport|From source group"
msgstr ""
......@@ -6135,6 +6141,9 @@ msgstr ""
msgid "BulkImport|No parent"
msgstr ""
msgid "BulkImport|Re-import creates a new group. It does not sync with the existing group."
msgstr ""
msgid "BulkImport|Showing %{start}-%{end} of %{total}"
msgstr ""
......
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { GlAlert, GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
......@@ -33,6 +33,12 @@ describe('import table', () => {
generateFakeEntry({ id: 2, status: STATUSES.FINISHED }),
];
const FAKE_PAGE_INFO = { page: 1, perPage: 20, total: 40, totalPages: 2 };
const FAKE_VERSION_VALIDATION = {
features: {
projectMigration: { available: false, minVersion: '14.8.0' },
sourceInstanceVersion: '14.6.0',
},
};
const findImportSelectedButton = () =>
wrapper.findAll('button').wrappers.find((w) => w.text() === 'Import selected');
......@@ -108,6 +114,7 @@ describe('import table', () => {
bulkImportSourceGroups: () => ({
nodes: [],
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
......@@ -121,6 +128,7 @@ describe('import table', () => {
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
......@@ -133,6 +141,7 @@ describe('import table', () => {
bulkImportSourceGroups: jest.fn().mockResolvedValue({
nodes: [],
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
......@@ -142,7 +151,11 @@ describe('import table', () => {
it('invokes importGroups mutation when row button is clicked', async () => {
createComponent({
bulkImportSourceGroups: () => ({ nodes: [FAKE_GROUP], pageInfo: FAKE_PAGE_INFO }),
bulkImportSourceGroups: () => ({
nodes: [FAKE_GROUP],
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
jest.spyOn(apolloProvider.defaultClient, 'mutate');
......@@ -166,7 +179,11 @@ describe('import table', () => {
it('displays error if importing group fails', async () => {
createComponent({
bulkImportSourceGroups: () => ({ nodes: [FAKE_GROUP], pageInfo: FAKE_PAGE_INFO }),
bulkImportSourceGroups: () => ({
nodes: [FAKE_GROUP],
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
importGroups: () => {
throw new Error();
},
......@@ -186,9 +203,11 @@ describe('import table', () => {
});
describe('pagination', () => {
const bulkImportSourceGroupsQueryMock = jest
.fn()
.mockResolvedValue({ nodes: [FAKE_GROUP], pageInfo: FAKE_PAGE_INFO });
const bulkImportSourceGroupsQueryMock = jest.fn().mockResolvedValue({
nodes: [FAKE_GROUP],
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
});
beforeEach(() => {
createComponent({
......@@ -212,6 +231,7 @@ describe('import table', () => {
bulkImportSourceGroupsQueryMock.mockResolvedValue({
nodes: [FAKE_GROUP],
pageInfo: { ...FAKE_PAGE_INFO, perPage: 50 },
versionValidation: FAKE_VERSION_VALIDATION,
});
await otherOption.trigger('click');
......@@ -243,6 +263,7 @@ describe('import table', () => {
perPage: 20,
totalPages: 2,
},
versionValidation: FAKE_VERSION_VALIDATION,
});
wrapper.find(PaginationLinks).props().change(REQUESTED_PAGE);
await waitForPromises();
......@@ -252,9 +273,11 @@ describe('import table', () => {
});
describe('filters', () => {
const bulkImportSourceGroupsQueryMock = jest
.fn()
.mockResolvedValue({ nodes: [FAKE_GROUP], pageInfo: FAKE_PAGE_INFO });
const bulkImportSourceGroupsQueryMock = jest.fn().mockResolvedValue({
nodes: [FAKE_GROUP],
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
});
beforeEach(() => {
createComponent({
......@@ -327,6 +350,7 @@ describe('import table', () => {
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
......@@ -342,6 +366,7 @@ describe('import table', () => {
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
......@@ -354,6 +379,7 @@ describe('import table', () => {
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
......@@ -370,6 +396,7 @@ describe('import table', () => {
bulkImportSourceGroups: () => ({
nodes: NEW_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
......@@ -392,6 +419,7 @@ describe('import table', () => {
bulkImportSourceGroups: () => ({
nodes: NEW_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
......@@ -415,6 +443,7 @@ describe('import table', () => {
bulkImportSourceGroups: () => ({
nodes: NEW_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
jest.spyOn(apolloProvider.defaultClient, 'mutate');
......@@ -445,4 +474,38 @@ describe('import table', () => {
});
});
});
describe('unavailable features warning', () => {
it('renders alert when there are unavailable features', async () => {
createComponent({
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: FAKE_VERSION_VALIDATION,
}),
});
await waitForPromises();
expect(wrapper.find(GlAlert).exists()).toBe(true);
expect(wrapper.find(GlAlert).text()).toContain('projects (require v14.8.0)');
});
it('does not renders alert when there are no unavailable features', async () => {
createComponent({
bulkImportSourceGroups: () => ({
nodes: FAKE_GROUPS,
pageInfo: FAKE_PAGE_INFO,
versionValidation: {
features: {
projectMigration: { available: true, minVersion: '14.8.0' },
sourceInstanceVersion: '14.6.0',
},
},
}),
});
await waitForPromises();
expect(wrapper.find(GlAlert).exists()).toBe(false);
});
});
});
......@@ -50,6 +50,12 @@ export const statusEndpointFixture = {
web_url: 'https://gitlab.com/groups/gitlab-examples',
},
],
version_validation: {
features: {
project_migration: { available: false, min_version: '14.8.0' },
source_instance_version: '14.6.0',
},
},
};
export const availableNamespacesFixture = Object.freeze([
......
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