Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/import_entities')
-rw-r--r--app/assets/javascripts/import_entities/components/pagination_bar.vue90
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue2
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_table.vue135
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue126
-rw-r--r--app/assets/javascripts/import_entities/import_groups/constants.js4
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js31
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql1
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql1
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql9
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js9
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql16
11 files changed, 208 insertions, 216 deletions
diff --git a/app/assets/javascripts/import_entities/components/pagination_bar.vue b/app/assets/javascripts/import_entities/components/pagination_bar.vue
deleted file mode 100644
index 33bd3e08bb1..00000000000
--- a/app/assets/javascripts/import_entities/components/pagination_bar.vue
+++ /dev/null
@@ -1,90 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownItem, GlIcon, GlSprintf } from '@gitlab/ui';
-import { __ } from '~/locale';
-import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
-
-const DEFAULT_PAGE_SIZES = [20, 50, 100];
-
-export default {
- components: {
- PaginationLinks,
- GlDropdown,
- GlDropdownItem,
- GlIcon,
- GlSprintf,
- },
- props: {
- pageInfo: {
- required: true,
- type: Object,
- },
- pageSizes: {
- required: false,
- type: Array,
- default: () => DEFAULT_PAGE_SIZES,
- },
- itemsCount: {
- required: true,
- type: Number,
- },
- },
-
- computed: {
- humanizedTotal() {
- return this.pageInfo.total >= 1000 ? __('1000+') : this.pageInfo.total;
- },
-
- paginationInfo() {
- const { page, perPage } = this.pageInfo;
- const start = (page - 1) * perPage + 1;
- const end = start + this.itemsCount - 1;
-
- return { start, end };
- },
- },
-
- methods: {
- setPage(page) {
- this.$emit('set-page', page);
- },
- },
-};
-</script>
-
-<template>
- <div class="gl-display-flex gl-align-items-center">
- <pagination-links :change="setPage" :page-info="pageInfo" class="gl-m-0" />
- <gl-dropdown category="tertiary" class="gl-ml-auto">
- <template #button-content>
- <span class="gl-font-weight-bold">
- <gl-sprintf :message="__('%{count} items per page')">
- <template #count>
- {{ pageInfo.perPage }}
- </template>
- </gl-sprintf>
- </span>
- <gl-icon class="gl-button-icon dropdown-chevron" name="chevron-down" />
- </template>
- <gl-dropdown-item v-for="size in pageSizes" :key="size" @click="$emit('set-page-size', size)">
- <gl-sprintf :message="__('%{count} items per page')">
- <template #count>
- {{ size }}
- </template>
- </gl-sprintf>
- </gl-dropdown-item>
- </gl-dropdown>
- <div class="gl-ml-2" data-testid="information">
- <gl-sprintf :message="s__('BulkImport|Showing %{start}-%{end} of %{total}')">
- <template #start>
- {{ paginationInfo.start }}
- </template>
- <template #end>
- {{ paginationInfo.end }}
- </template>
- <template #total>
- {{ humanizedTotal }}
- </template>
- </gl-sprintf>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue b/app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue
index e004bc35087..deaf2654424 100644
--- a/app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_actions_cell.vue
@@ -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"
/>
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
index ec6025c84bb..028197ec9b1 100644
--- a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue
@@ -1,9 +1,8 @@
<script>
import {
+ GlAlert,
GlButton,
GlEmptyState,
- GlDropdown,
- GlDropdownItem,
GlIcon,
GlLink,
GlLoadingIcon,
@@ -14,8 +13,8 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
import createFlash from '~/flash';
-import { s__, __, n__ } from '~/locale';
-import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
+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';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
@@ -42,10 +41,9 @@ const DEFAULT_TD_CLASSES = 'gl-vertical-align-top!';
export default {
components: {
+ GlAlert,
GlButton,
GlEmptyState,
- GlDropdown,
- GlDropdownItem,
GlIcon,
GlLink,
GlLoadingIcon,
@@ -57,7 +55,7 @@ export default {
ImportTargetCell,
ImportStatusCell,
ImportActionsCell,
- PaginationLinks,
+ PaginationBar,
},
props: {
@@ -83,6 +81,7 @@ export default {
selectedGroupsIds: [],
pendingGroupsIds: [],
importTargets: {},
+ unavailableFeaturesAlertVisible: true,
};
},
@@ -170,7 +169,7 @@ export default {
},
availableGroupsForImport() {
- return this.groupsTableData.filter((g) => g.flags.isAvailableForImport && g.flags.isInvalid);
+ return this.groupsTableData.filter((g) => g.flags.isAvailableForImport && !g.flags.isInvalid);
},
humanizedTotal() {
@@ -204,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: {
@@ -314,9 +330,8 @@ export default {
variables: { importRequests },
});
} catch (error) {
- const message = error?.networkError?.response?.data?.error ?? i18n.ERROR_IMPORT;
createFlash({
- message,
+ message: i18n.ERROR_IMPORT,
captureError: true,
error,
});
@@ -476,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"
>
@@ -495,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>
@@ -521,13 +568,15 @@ export default {
/>
<template v-else>
<div
- class="gl-bg-gray-10 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-p-4 gl-display-flex gl-align-items-center"
+ class="gl-bg-gray-10 gl-border-solid gl-border-gray-200 gl-border-0 gl-border-b-1 gl-px-4 gl-display-flex gl-align-items-center import-table-bar"
>
- <gl-sprintf :message="__('%{count} selected')">
- <template #count>
- {{ selectedGroupsIds.length }}
- </template>
- </gl-sprintf>
+ <span data-test-id="selection-count">
+ <gl-sprintf :message="__('%{count} selected')">
+ <template #count>
+ {{ selectedGroupsIds.length }}
+ </template>
+ </gl-sprintf>
+ </span>
<gl-button
category="primary"
variant="confirm"
@@ -539,7 +588,7 @@ export default {
</div>
<gl-table
ref="table"
- class="gl-w-full"
+ class="gl-w-full import-table"
data-qa-selector="import_table"
:tbody-tr-class="rowClasses"
:tbody-tr-attr="qaRowAttributes"
@@ -599,49 +648,13 @@ export default {
/>
</template>
</gl-table>
- <div v-if="hasGroups" class="gl-display-flex gl-mt-3 gl-align-items-center">
- <pagination-links
- :change="setPage"
- :page-info="bulkImportSourceGroups.pageInfo"
- class="gl-m-0"
- />
- <gl-dropdown category="tertiary" :aria-label="__('Page size')" class="gl-ml-auto">
- <template #button-content>
- <span class="font-weight-bold">
- <gl-sprintf :message="__('%{count} items per page')">
- <template #count>
- {{ perPage }}
- </template>
- </gl-sprintf>
- </span>
- <gl-icon class="gl-button-icon dropdown-chevron" name="chevron-down" />
- </template>
- <gl-dropdown-item
- v-for="size in $options.PAGE_SIZES"
- :key="size"
- @click="setPageSize(size)"
- >
- <gl-sprintf :message="__('%{count} items per page')">
- <template #count>
- {{ size }}
- </template>
- </gl-sprintf>
- </gl-dropdown-item>
- </gl-dropdown>
- <div class="gl-ml-2">
- <gl-sprintf :message="s__('BulkImport|Showing %{start}-%{end} of %{total}')">
- <template #start>
- {{ paginationInfo.start }}
- </template>
- <template #end>
- {{ paginationInfo.end }}
- </template>
- <template #total>
- {{ humanizedTotal }}
- </template>
- </gl-sprintf>
- </div>
- </div>
+ <pagination-bar
+ v-if="hasGroups"
+ :page-info="bulkImportSourceGroups.pageInfo"
+ class="gl-mt-3"
+ @set-page="setPage"
+ @set-page-size="setPageSize"
+ />
</template>
</template>
</div>
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue b/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue
index ca9ae9447d0..344a6e45370 100644
--- a/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue
@@ -32,72 +32,84 @@ export default {
fullPath() {
return this.group.importTarget.targetNamespace.fullPath || s__('BulkImport|No parent');
},
- invalidNameValidationMessage() {
- return getInvalidNameValidationMessage(this.group.importTarget);
+ validationMessage() {
+ return (
+ this.group.progress?.message || getInvalidNameValidationMessage(this.group.importTarget)
+ );
+ },
+ validNameState() {
+ // bootstrap-vue requires null for "indifferent" state, if we return true
+ // this will highlight field in green like "passed validation"
+ return this.group.flags.isInvalid && this.group.flags.isAvailableForImport ? false : null;
},
},
};
</script>
<template>
- <div class="gl-display-flex gl-align-items-stretch">
- <import-group-dropdown
- #default="{ namespaces }"
- :text="fullPath"
- :disabled="!group.flags.isAvailableForImport"
- :namespaces="availableNamespaces"
- toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
- class="gl-h-7 gl-flex-grow-1"
- data-qa-selector="target_namespace_selector_dropdown"
- >
- <gl-dropdown-item @click="$emit('update-target-namespace', { fullPath: '', id: null })">{{
- s__('BulkImport|No parent')
- }}</gl-dropdown-item>
- <template v-if="namespaces.length">
- <gl-dropdown-divider />
- <gl-dropdown-section-header>
- {{ s__('BulkImport|Existing groups') }}
- </gl-dropdown-section-header>
- <gl-dropdown-item
- v-for="ns in namespaces"
- :key="ns.fullPath"
- data-qa-selector="target_group_dropdown_item"
- :data-qa-group-name="ns.fullPath"
- @click="$emit('update-target-namespace', ns)"
- >
- {{ ns.fullPath }}
- </gl-dropdown-item>
- </template>
- </import-group-dropdown>
- <div
- class="gl-h-7 gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1 gl-bg-gray-10"
- :class="{
- 'gl-text-gray-400 gl-border-gray-100': !group.flags.isAvailableForImport,
- 'gl-border-gray-200': group.flags.isAvailableForImport,
- }"
- >
- /
- </div>
- <div class="gl-flex-grow-1">
- <gl-form-input
- class="gl-rounded-top-left-none gl-rounded-bottom-left-none"
+ <div>
+ <div class="gl-display-flex gl-align-items-stretch">
+ <import-group-dropdown
+ #default="{ namespaces }"
+ :text="fullPath"
+ :disabled="!group.flags.isAvailableForImport"
+ :namespaces="availableNamespaces"
+ toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
+ class="gl-h-7 gl-flex-grow-1"
+ data-qa-selector="target_namespace_selector_dropdown"
+ >
+ <gl-dropdown-item @click="$emit('update-target-namespace', { fullPath: '', id: null })">{{
+ s__('BulkImport|No parent')
+ }}</gl-dropdown-item>
+ <template v-if="namespaces.length">
+ <gl-dropdown-divider />
+ <gl-dropdown-section-header>
+ {{ s__('BulkImport|Existing groups') }}
+ </gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="ns in namespaces"
+ :key="ns.fullPath"
+ data-qa-selector="target_group_dropdown_item"
+ :data-qa-group-name="ns.fullPath"
+ @click="$emit('update-target-namespace', ns)"
+ >
+ {{ ns.fullPath }}
+ </gl-dropdown-item>
+ </template>
+ </import-group-dropdown>
+ <div
+ class="gl-h-7 gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1 gl-bg-gray-10"
:class="{
- 'gl-inset-border-1-gray-200!': group.flags.isAvailableForImport,
- 'gl-inset-border-1-gray-100!': !group.flags.isAvailableForImport,
- 'is-invalid': group.flags.isInvalid && group.flags.isAvailableForImport,
+ 'gl-text-gray-400 gl-border-gray-100': !group.flags.isAvailableForImport,
+ 'gl-border-gray-200': group.flags.isAvailableForImport,
}"
- debounce="500"
- :disabled="!group.flags.isAvailableForImport"
- :value="group.importTarget.newName"
- :aria-label="__('New name')"
- @input="$emit('update-new-name', $event)"
- />
- <p
- v-if="group.flags.isAvailableForImport && group.flags.isInvalid"
- class="gl-text-red-500 gl-m-0 gl-mt-2"
>
- {{ invalidNameValidationMessage }}
- </p>
+ /
+ </div>
+ <div class="gl-flex-grow-1">
+ <gl-form-input
+ class="gl-rounded-top-left-none gl-rounded-bottom-left-none"
+ :class="{
+ 'gl-inset-border-1-gray-200!':
+ group.flags.isAvailableForImport && !group.flags.isInvalid,
+ 'gl-inset-border-1-gray-100!':
+ !group.flags.isAvailableForImport && !group.flags.isInvalid,
+ }"
+ debounce="500"
+ :disabled="!group.flags.isAvailableForImport"
+ :value="group.importTarget.newName"
+ :aria-label="__('New name')"
+ :state="validNameState"
+ @input="$emit('update-new-name', $event)"
+ />
+ </div>
+ </div>
+ <div
+ v-if="group.flags.isAvailableForImport && (group.flags.isInvalid || validationMessage)"
+ class="gl-text-red-500 gl-m-0 gl-mt-2"
+ role="alert"
+ >
+ {{ validationMessage }}
</div>
</div>
</template>
diff --git a/app/assets/javascripts/import_entities/import_groups/constants.js b/app/assets/javascripts/import_entities/import_groups/constants.js
index aa9cf3897e6..ac1466238d0 100644
--- a/app/assets/javascripts/import_entities/import_groups/constants.js
+++ b/app/assets/javascripts/import_entities/import_groups/constants.js
@@ -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';
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
index bce6e7bcb1f..36da996ea17 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js
@@ -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;
},
@@ -142,9 +157,7 @@ export function createResolvers({ endpoints }) {
};
});
- const {
- data: { id: jobId },
- } = await axios.post(endpoints.createBulkImport, {
+ const { data: originalResponse } = await axios.post(endpoints.createBulkImport, {
bulk_import: importOperations.map((op) => ({
source_type: 'group_entity',
source_full_path: op.group.fullPath,
@@ -153,15 +166,21 @@ export function createResolvers({ endpoints }) {
})),
});
- return importOperations.map((op) => {
+ const responses = Array.isArray(originalResponse)
+ ? originalResponse
+ : [{ success: true, id: originalResponse.id }];
+
+ return importOperations.map((op, idx) => {
+ const response = responses[idx];
const lastImportTarget = {
targetNamespace: op.targetNamespace,
newName: op.newName,
};
const progress = {
- id: jobId,
- status: STATUSES.CREATED,
+ id: response.id || `local-${Date.now()}-${idx}`,
+ status: response.success ? STATUSES.CREATED : STATUSES.FAILED,
+ message: response.message || null,
};
localStorageCache.set(op.group.webUrl, { progress, lastImportTarget });
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql
index 2d60bf82d65..33c564f36a8 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql
@@ -1,4 +1,5 @@
fragment BulkImportSourceGroupProgress on ClientBulkImportProgress {
id
status
+ message
}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql
index 75215471d0f..39289887b75 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql
@@ -9,6 +9,7 @@ mutation importGroups($importRequests: [ImportGroupInput!]!) {
progress {
id
status
+ message
}
}
}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql
index 28dfefdf8a7..ace8bffc012 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/queries/bulk_import_source_groups.query.graphql
@@ -11,5 +11,14 @@ query bulkImportSourceGroups($page: Int = 1, $perPage: Int = 20, $filter: String
total
totalPages
}
+ versionValidation {
+ features {
+ sourceInstanceVersion
+ projectMigration {
+ available
+ minVersion
+ }
+ }
+ }
}
}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js b/app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js
index 09bc7b33692..1aad22f0f3f 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js
@@ -22,7 +22,14 @@ export class LocalStorageCache {
loadCacheFromStorage() {
try {
- return JSON.parse(this.storage.getItem(KEY)) ?? {};
+ const storage = JSON.parse(this.storage.getItem(KEY)) ?? {};
+ Object.values(storage).forEach((entry) => {
+ if (entry.progress && !('message' in entry.progress)) {
+ // eslint-disable-next-line no-param-reassign
+ entry.progress.message = '';
+ }
+ });
+ return storage;
} catch {
return {};
}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql
index b8dd79a5000..c48e22a7717 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql
@@ -11,11 +11,13 @@ type ClientBulkImportTarget {
type ClientBulkImportSourceGroupConnection {
nodes: [ClientBulkImportSourceGroup!]!
pageInfo: ClientBulkImportPageInfo!
+ versionValidation: ClientBulkImportVersionValidation!
}
type ClientBulkImportProgress {
id: ID!
status: String!
+ message: String
}
type ClientBulkImportValidationError {
@@ -45,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!