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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-22 18:10:48 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-22 18:10:48 +0300
commit232e7582b0e81e95bd721ad8094fcb50ecbc8a04 (patch)
treea68e00760d0a3446c2030addd679b33acfa39d2d /app/assets/javascripts/import_entities
parent980d813e90c70f09861e39b9269e73b2c9a4e936 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/import_entities')
-rw-r--r--app/assets/javascripts/import_entities/constants.js6
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_table.vue6
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue26
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js72
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js83
-rw-r--r--app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js12
-rw-r--r--app/assets/javascripts/import_entities/import_groups/index.js1
7 files changed, 141 insertions, 65 deletions
diff --git a/app/assets/javascripts/import_entities/constants.js b/app/assets/javascripts/import_entities/constants.js
index ad33ca158d2..c2f398cb8a8 100644
--- a/app/assets/javascripts/import_entities/constants.js
+++ b/app/assets/javascripts/import_entities/constants.js
@@ -7,6 +7,7 @@ export const STATUSES = {
FINISHED: 'finished',
FAILED: 'failed',
SCHEDULED: 'scheduled',
+ CREATED: 'created',
STARTED: 'started',
NONE: 'none',
SCHEDULING: 'scheduling',
@@ -23,6 +24,11 @@ const STATUS_MAP = {
text: __('Failed'),
textClass: 'text-danger',
},
+ [STATUSES.CREATED]: {
+ icon: 'pending',
+ text: __('Scheduled'),
+ textClass: 'text-warning',
+ },
[STATUSES.SCHEDULED]: {
icon: 'pending',
text: __('Scheduled'),
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 a0a44ee74dc..c0fd076ee6b 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
@@ -33,11 +33,6 @@ export default {
type: String,
required: true,
},
- canCreateGroup: {
- type: Boolean,
- required: false,
- default: false,
- },
},
data() {
@@ -176,7 +171,6 @@ export default {
:key="group.id"
:group="group"
:available-namespaces="availableNamespaces"
- :can-create-group="canCreateGroup"
@update-target-namespace="updateTargetNamespace(group.id, $event)"
@update-new-name="updateNewName(group.id, $event)"
@import-group="importGroup(group.id)"
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue
index 1f3eee0c141..3b8abeae122 100644
--- a/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue
@@ -24,11 +24,6 @@ export default {
type: Array,
required: true,
},
- canCreateGroup: {
- type: Boolean,
- required: false,
- default: false,
- },
},
computed: {
isDisabled() {
@@ -45,19 +40,18 @@ export default {
text: namespace.full_path,
}));
- if (!this.canCreateGroup) {
- return { data: availableNamespacesData };
+ const select2Config = {
+ data: [{ id: '', text: s__('BulkImport|No parent') }],
+ };
+
+ if (availableNamespacesData.length) {
+ select2Config.data.push({
+ text: s__('BulkImport|Existing groups'),
+ children: availableNamespacesData,
+ });
}
- return {
- data: [
- { id: '', text: s__('BulkImport|No parent') },
- {
- text: s__('BulkImport|Existing groups'),
- children: availableNamespacesData,
- },
- ],
- };
+ return select2Config;
},
},
methods: {
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 8110934efc4..8aeab59cb65 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
@@ -15,52 +15,71 @@ export const clientTypenames = {
BulkImportPageInfo: 'ClientBulkImportPageInfo',
};
-export function createResolvers({ endpoints }) {
+export function createResolvers({ endpoints, sourceUrl, GroupsManager = SourceGroupsManager }) {
let statusPoller;
+ let sourceGroupManager;
+ const getGroupsManager = (client) => {
+ if (!sourceGroupManager) {
+ sourceGroupManager = new GroupsManager({ client, sourceUrl });
+ }
+ return sourceGroupManager;
+ };
+
return {
Query: {
async bulkImportSourceGroups(_, vars, { client }) {
- const {
- data: { availableNamespaces },
- } = await client.query({ query: availableNamespacesQuery });
-
if (!statusPoller) {
statusPoller = new StatusPoller({
- client,
+ groupManager: getGroupsManager(client),
pollPath: endpoints.jobs,
});
statusPoller.startPolling();
}
- return axios
- .get(endpoints.status, {
+ const groupsManager = getGroupsManager(client);
+ return Promise.all([
+ axios.get(endpoints.status, {
params: {
page: vars.page,
per_page: vars.perPage,
filter: vars.filter,
},
- })
- .then(({ headers, data }) => {
+ }),
+ client.query({ query: availableNamespacesQuery }),
+ ]).then(
+ ([
+ { headers, data },
+ {
+ data: { availableNamespaces },
+ },
+ ]) => {
const pagination = parseIntPagination(normalizeHeaders(headers));
return {
__typename: clientTypenames.BulkImportSourceGroupConnection,
- nodes: data.importable_data.map((group) => ({
- __typename: clientTypenames.BulkImportSourceGroup,
- ...group,
- status: STATUSES.NONE,
- import_target: {
- new_name: group.full_path,
- target_namespace: availableNamespaces[0].full_path,
- },
- })),
+ nodes: data.importable_data.map((group) => {
+ const cachedImportState = groupsManager.getImportStateFromStorageByGroupId(
+ group.id,
+ );
+
+ return {
+ __typename: clientTypenames.BulkImportSourceGroup,
+ ...group,
+ status: cachedImportState?.status ?? STATUSES.NONE,
+ import_target: cachedImportState?.importTarget ?? {
+ new_name: group.full_path,
+ target_namespace: availableNamespaces[0]?.full_path ?? '',
+ },
+ };
+ }),
pageInfo: {
__typename: clientTypenames.BulkImportPageInfo,
...pagination,
},
};
- });
+ },
+ );
},
availableNamespaces: () =>
@@ -73,21 +92,21 @@ export function createResolvers({ endpoints }) {
},
Mutation: {
setTargetNamespace(_, { targetNamespace, sourceGroupId }, { client }) {
- new SourceGroupsManager({ client }).updateById(sourceGroupId, (sourceGroup) => {
+ getGroupsManager(client).updateById(sourceGroupId, (sourceGroup) => {
// eslint-disable-next-line no-param-reassign
sourceGroup.import_target.target_namespace = targetNamespace;
});
},
setNewName(_, { newName, sourceGroupId }, { client }) {
- new SourceGroupsManager({ client }).updateById(sourceGroupId, (sourceGroup) => {
+ getGroupsManager(client).updateById(sourceGroupId, (sourceGroup) => {
// eslint-disable-next-line no-param-reassign
sourceGroup.import_target.new_name = newName;
});
},
async importGroup(_, { sourceGroupId }, { client }) {
- const groupManager = new SourceGroupsManager({ client });
+ const groupManager = getGroupsManager(client);
const group = groupManager.findById(sourceGroupId);
groupManager.setImportStatus(group, STATUSES.SCHEDULING);
try {
@@ -101,8 +120,7 @@ export function createResolvers({ endpoints }) {
},
],
});
- groupManager.setImportStatus(group, STATUSES.STARTED);
- SourceGroupsManager.attachImportId(group, response.data.id);
+ groupManager.startImport({ group, importId: response.data.id });
} catch (e) {
createFlash({
message: s__('BulkImport|Importing the group failed'),
@@ -116,5 +134,5 @@ export function createResolvers({ endpoints }) {
};
}
-export const createApolloClient = ({ endpoints }) =>
- createDefaultClient(createResolvers({ endpoints }), { assumeImmutableResults: true });
+export const createApolloClient = ({ sourceUrl, endpoints }) =>
+ createDefaultClient(createResolvers({ sourceUrl, endpoints }), { assumeImmutableResults: true });
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js b/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js
index 261e30edbbb..2c88d25358f 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/services/source_groups_manager.js
@@ -1,5 +1,7 @@
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import produce from 'immer';
+import { debounce, merge } from 'lodash';
+import { STATUSES } from '../../../constants';
import ImportSourceGroupFragment from '../fragments/bulk_import_source_group_item.fragment.graphql';
function extractTypeConditionFromFragment(fragment) {
@@ -13,15 +15,24 @@ function generateGroupId(id) {
});
}
+export const KEY = 'gl-bulk-imports-import-state';
+export const DEBOUNCE_INTERVAL = 200;
+
export class SourceGroupsManager {
- static importMap = new Map();
+ constructor({ client, sourceUrl, storage = window.localStorage }) {
+ this.client = client;
+ this.sourceUrl = sourceUrl;
- static attachImportId(group, importId) {
- SourceGroupsManager.importMap.set(importId, group.id);
+ this.storage = storage;
+ this.importStates = this.loadImportStatesFromStorage();
}
- constructor({ client }) {
- this.client = client;
+ loadImportStatesFromStorage() {
+ try {
+ return JSON.parse(this.storage.getItem(KEY)) ?? {};
+ } catch {
+ return {};
+ }
}
findById(id) {
@@ -42,8 +53,48 @@ export class SourceGroupsManager {
this.update(group, fn);
}
- findByImportId(importId) {
- return this.findById(SourceGroupsManager.importMap.get(importId));
+ saveImportState(importId, group) {
+ this.importStates[this.getStorageKey(importId)] = {
+ id: group.id,
+ importTarget: group.import_target,
+ status: group.status,
+ };
+ this.saveImportStatesToStorage();
+ }
+
+ getImportStateFromStorage(importId) {
+ return this.importStates[this.getStorageKey(importId)];
+ }
+
+ getImportStateFromStorageByGroupId(groupId) {
+ const PREFIX = this.getStorageKey('');
+ const [, importState] =
+ Object.entries(this.importStates).find(
+ ([key, group]) => key.startsWith(PREFIX) && group.id === groupId,
+ ) ?? [];
+
+ return importState;
+ }
+
+ getStorageKey(importId) {
+ return `${this.sourceUrl}|${importId}`;
+ }
+
+ saveImportStatesToStorage = debounce(() => {
+ try {
+ // storage might be changed in other tab so fetch first
+ this.storage.setItem(
+ KEY,
+ JSON.stringify(merge({}, this.loadImportStatesFromStorage(), this.importStates)),
+ );
+ } catch {
+ // empty catch intentional: storage might be unavailable or full
+ }
+ }, DEBOUNCE_INTERVAL);
+
+ startImport({ group, importId }) {
+ this.setImportStatus(group, STATUSES.CREATED);
+ this.saveImportState(importId, group);
}
setImportStatus(group, status) {
@@ -52,4 +103,22 @@ export class SourceGroupsManager {
sourceGroup.status = status;
});
}
+
+ setImportStatusByImportId(importId, status) {
+ const importState = this.getImportStateFromStorage(importId);
+ if (!importState) {
+ return;
+ }
+
+ if (importState.status !== status) {
+ importState.status = status;
+ }
+
+ const group = this.findById(importState.id);
+ if (group?.id) {
+ this.setImportStatus(group, status);
+ }
+
+ this.saveImportStatesToStorage();
+ }
}
diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js b/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js
index 63cd6b48fc4..b80a575afce 100644
--- a/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js
+++ b/app/assets/javascripts/import_entities/import_groups/graphql/services/status_poller.js
@@ -3,12 +3,9 @@ import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import Poll from '~/lib/utils/poll';
import { s__ } from '~/locale';
-import { SourceGroupsManager } from './source_groups_manager';
export class StatusPoller {
- constructor({ client, pollPath }) {
- this.client = client;
-
+ constructor({ groupManager, pollPath }) {
this.eTagPoll = new Poll({
resource: {
fetchJobs: () => axios.get(pollPath),
@@ -29,7 +26,7 @@ export class StatusPoller {
}
});
- this.groupManager = new SourceGroupsManager({ client });
+ this.groupManager = groupManager;
}
startPolling() {
@@ -38,10 +35,7 @@ export class StatusPoller {
async updateImportsStatuses(importStatuses) {
importStatuses.forEach(({ id, status_name: statusName }) => {
- const group = this.groupManager.findByImportId(id);
- if (group.id) {
- this.groupManager.setImportStatus(group, statusName);
- }
+ this.groupManager.setImportStatusByImportId(id, statusName);
});
}
}
diff --git a/app/assets/javascripts/import_entities/import_groups/index.js b/app/assets/javascripts/import_entities/import_groups/index.js
index 0700358f6ce..cf56bb804d4 100644
--- a/app/assets/javascripts/import_entities/import_groups/index.js
+++ b/app/assets/javascripts/import_entities/import_groups/index.js
@@ -21,6 +21,7 @@ export function mountImportGroupsApp(mountElement) {
} = mountElement.dataset;
const apolloProvider = new VueApollo({
defaultClient: createApolloClient({
+ sourceUrl,
endpoints: {
status: statusPath,
availableNamespaces: availableNamespacesPath,