Commit 46b67b33 authored by Zack Cuddy's avatar Zack Cuddy

Disabled vs Empty Geo Sync

Currenlty in the UI we do not differentiate
between when a sync is empty (0) and when
a sync is actually disabled.

This MR changes that by first exposing
the data from the API to tell when
a feature is disabled. Then reflecting that
in the UI.
parent 57bfac17
<script>
import { __ } from '~/locale';
import { roundOffFloat } from '~/lib/utils/common_utils';
import tooltip from '~/vue_shared/directives/tooltip';
......@@ -27,6 +28,11 @@ export default {
required: false,
default: 'neutral',
},
unavailableLabel: {
type: String,
required: false,
default: __('Not available'),
},
successCount: {
type: Number,
required: true,
......@@ -103,7 +109,7 @@ export default {
<template>
<div :class="cssClass" class="stacked-progress-bar">
<span v-if="!totalCount" class="status-unavailable"> {{ __('Not available') }} </span>
<span v-if="!totalCount" class="status-unavailable">{{ unavailableLabel }}</span>
<span
v-if="successPercent"
v-tooltip
......
<script>
import { GlIcon, GlPopover, GlLink } from '@gitlab/ui';
import popover from '~/vue_shared/directives/popover';
import { VALUE_TYPE, CUSTOM_TYPE } from '../constants';
import { VALUE_TYPE, CUSTOM_TYPE, REPLICATION_HELP_URL } from '../constants';
import GeoNodeSyncSettings from './geo_node_sync_settings.vue';
import GeoNodeEventStatus from './geo_node_event_status.vue';
......@@ -12,6 +13,9 @@ export default {
GeoNodeSyncSettings,
GeoNodeEventStatus,
GeoNodeSyncProgress,
GlIcon,
GlPopover,
GlLink,
},
directives: {
popover,
......@@ -26,6 +30,11 @@ export default {
required: false,
default: '',
},
itemEnabled: {
type: Boolean,
required: false,
default: true,
},
itemValue: {
type: [Object, String, Number],
required: true,
......@@ -54,11 +63,6 @@ export default {
required: false,
default: false,
},
featureDisabled: {
type: Boolean,
required: false,
default: false,
},
detailsPath: {
type: String,
required: false,
......@@ -82,41 +86,68 @@ export default {
return this.customType === CUSTOM_TYPE.SYNC;
},
},
replicationHelpUrl: REPLICATION_HELP_URL,
};
</script>
<template>
<div v-if="!featureDisabled" class="mt-2 ml-2 node-detail-item">
<div class="mt-2 ml-2 node-detail-item">
<div class="d-flex align-items-center text-secondary-700">
<span class="node-detail-title">{{ itemTitle }}</span>
</div>
<div v-if="isValueTypePlain" :class="cssClass" class="mt-1 node-detail-value">
{{ itemValue }}
</div>
<geo-node-sync-progress
v-if="isValueTypeGraph"
:item-title="itemTitle"
:item-value="itemValue"
:item-value-stale="itemValueStale"
:item-value-stale-tooltip="itemValueStaleTooltip"
:details-path="detailsPath"
:class="{ 'd-flex': itemValueStale }"
class="mt-1"
/>
<template v-if="isValueTypeCustom">
<geo-node-sync-settings
v-if="isCustomTypeSync"
:sync-status-unavailable="itemValue.syncStatusUnavailable"
:selective-sync-type="itemValue.selectiveSyncType"
:last-event="itemValue.lastEvent"
:cursor-last-event="itemValue.cursorLastEvent"
/>
<geo-node-event-status
v-else
:event-id="itemValue.eventId"
:event-time-stamp="itemValue.eventTimeStamp"
:event-type-log-status="eventTypeLogStatus"
<div v-if="itemEnabled">
<div v-if="isValueTypePlain" :class="cssClass" class="mt-1 node-detail-value">
{{ itemValue }}
</div>
<geo-node-sync-progress
v-if="isValueTypeGraph"
:item-enabled="itemEnabled"
:item-title="itemTitle"
:item-value="itemValue"
:item-value-stale="itemValueStale"
:item-value-stale-tooltip="itemValueStaleTooltip"
:details-path="detailsPath"
:class="{ 'd-flex': itemValueStale }"
class="mt-1"
/>
</template>
<template v-if="isValueTypeCustom">
<geo-node-sync-settings v-if="isCustomTypeSync" v-bind="itemValue" />
<geo-node-event-status
v-else
:event-id="itemValue.eventId"
:event-time-stamp="itemValue.eventTimeStamp"
:event-type-log-status="eventTypeLogStatus"
/>
</template>
</div>
<div v-else class="mt-1">
<div
:id="`syncDisabled-${itemTitle}`"
class="d-inline-flex align-items-center cursor-pointer"
>
<gl-icon name="canceled-circle" :size="14" class="mr-1 text-secondary-300" />
<span ref="disabledText" class="text-secondary-600 gl-font-size-small">{{
__('Synchronization disabled')
}}</span>
</div>
<gl-popover
:target="`syncDisabled-${itemTitle}`"
placement="right"
triggers="hover focus"
:css-classes="['w-100']"
>
<section>
<p>{{ __('Synchronization of container repositories is disabled.') }}</p>
<div class="mt-3">
<gl-link
class="gl-font-size-small"
:href="$options.replicationHelpUrl"
target="_blank"
>{{ __('Learn how to enable synchronization') }}</gl-link
>
</div>
</section>
</gl-popover>
</div>
</div>
</template>
......@@ -58,6 +58,7 @@ export default {
tabindex="0"
:css-class="itemValueStale ? 'flex-fill' : ''"
:hide-tooltips="true"
:unavailable-label="__('Nothing to synchronize')"
:success-count="itemValue.successCount"
:failure-count="itemValue.failureCount"
:total-count="itemValue.totalCount"
......
......@@ -36,42 +36,48 @@ export default {
customType: CUSTOM_TYPE.SYNC,
},
{
itemEnabled: this.nodeDetails.repositories.enabled,
itemTitle: s__('GeoNodes|Repositories'),
itemValue: this.nodeDetails.repositories,
itemValueType: VALUE_TYPE.GRAPH,
detailsPath: `${this.node.url}admin/geo/projects`,
},
{
itemEnabled: this.nodeDetails.repositories.enabled,
itemTitle: s__('GeoNodes|Wikis'),
itemValue: this.nodeDetails.wikis,
itemValueType: VALUE_TYPE.GRAPH,
},
{
itemEnabled: this.nodeDetails.lfs.enabled,
itemTitle: s__('GeoNodes|LFS objects'),
itemValue: this.nodeDetails.lfs,
itemValueType: VALUE_TYPE.GRAPH,
},
{
itemEnabled: this.nodeDetails.attachments.enabled,
itemTitle: s__('GeoNodes|Attachments'),
itemValue: this.nodeDetails.attachments,
itemValueType: VALUE_TYPE.GRAPH,
detailsPath: `${this.node.url}admin/geo/uploads`,
},
{
itemEnabled: this.nodeDetails.jobArtifacts.enabled,
itemTitle: s__('GeoNodes|Job artifacts'),
itemValue: this.nodeDetails.jobArtifacts,
itemValueType: VALUE_TYPE.GRAPH,
},
{
itemEnabled: this.nodeDetails.containerRepositories.enabled,
itemTitle: s__('GeoNodes|Container repositories'),
itemValue: this.nodeDetails.containerRepositories,
itemValueType: VALUE_TYPE.GRAPH,
},
{
itemEnabled: this.nodeDetails.designRepositories.enabled,
itemTitle: s__('GeoNodes|Design repositories'),
itemValue: this.nodeDetails.designRepositories,
itemValueType: VALUE_TYPE.GRAPH,
featureDisabled: !gon.features.enableGeoDesignSync,
detailsPath: `${this.node.url}admin/geo/designs`,
},
{
......@@ -149,6 +155,7 @@ export default {
v-for="(nodeDetailItem, index) in nodeDetailItems"
:key="index"
:css-class="nodeDetailItem.cssClass"
:item-enabled="nodeDetailItem.itemEnabled"
:item-title="nodeDetailItem.itemTitle"
:item-value="nodeDetailItem.itemValue"
:item-value-type="nodeDetailItem.itemValueType"
......@@ -156,7 +163,6 @@ export default {
:item-value-stale-tooltip="statusInfoStaleMessage"
:custom-type="nodeDetailItem.customType"
:event-type-log-status="nodeDetailItem.eventTypeLogStatus"
:feature-disabled="nodeDetailItem.featureDisabled"
:details-path="nodeDetailItem.detailsPath"
/>
</div>
......
......@@ -40,3 +40,6 @@ export const STATUS_DELAY_THRESHOLD_MS = 60000;
export const HELP_INFO_URL =
'https://docs.gitlab.com/ee/administration/geo/disaster_recovery/background_verification.html#repository-verification';
export const REPLICATION_HELP_URL =
'https://docs.gitlab.com/ee/administration/geo/replication/datatypes.html#limitations-on-replicationverification';
......@@ -81,11 +81,13 @@ export default class GeoNodesStore {
failureCount: 0,
},
repositories: {
enabled: rawNodeDetails.repositories_replication_enabled,
totalCount: rawNodeDetails.projects_count || 0,
successCount: rawNodeDetails.repositories_synced_count || 0,
failureCount: rawNodeDetails.repositories_failed_count || 0,
},
wikis: {
enabled: rawNodeDetails.repositories_replication_enabled,
totalCount: rawNodeDetails.projects_count || 0,
successCount: rawNodeDetails.wikis_synced_count || 0,
failureCount: rawNodeDetails.wikis_failed_count || 0,
......@@ -111,26 +113,31 @@ export default class GeoNodesStore {
failureCount: rawNodeDetails.wikis_verification_failed_count || 0,
},
lfs: {
enabled: rawNodeDetails.lfs_objects_replication_enabled,
totalCount: rawNodeDetails.lfs_objects_count || 0,
successCount: rawNodeDetails.lfs_objects_synced_count || 0,
failureCount: rawNodeDetails.lfs_objects_failed_count || 0,
},
jobArtifacts: {
enabled: rawNodeDetails.job_artifacts_replication_enabled,
totalCount: rawNodeDetails.job_artifacts_count || 0,
successCount: rawNodeDetails.job_artifacts_synced_count || 0,
failureCount: rawNodeDetails.job_artifacts_failed_count || 0,
},
containerRepositories: {
enabled: rawNodeDetails.container_repositories_replication_enabled,
totalCount: rawNodeDetails.container_repositories_count || 0,
successCount: rawNodeDetails.container_repositories_synced_count || 0,
failureCount: rawNodeDetails.container_repositories_failed_count || 0,
},
designRepositories: {
enabled: rawNodeDetails.design_repositories_replication_enabled,
totalCount: rawNodeDetails.design_repositories_count || 0,
successCount: rawNodeDetails.design_repositories_synced_count || 0,
failureCount: rawNodeDetails.design_repositories_failed_count || 0,
},
attachments: {
enabled: rawNodeDetails.attachments_replication_enabled,
totalCount: rawNodeDetails.attachments_count || 0,
successCount: rawNodeDetails.attachments_synced_count || 0,
failureCount: rawNodeDetails.attachments_failed_count || 0,
......
---
title: Differentiate between empty and disabled Geo sync
merge_request: 28963
author:
type: changed
import { shallowMount } from '@vue/test-utils';
import { GlPopover, GlLink } from '@gitlab/ui';
import GeoNodeDetailItemComponent from 'ee/geo_nodes/components/geo_node_detail_item.vue';
import GeoNodeSyncSettings from 'ee/geo_nodes/components/geo_node_sync_settings.vue';
import GeoNodeEventStatus from 'ee/geo_nodes/components/geo_node_event_status.vue';
import GeoNodeSyncProgress from 'ee/geo_nodes/components/geo_node_sync_progress.vue';
import { VALUE_TYPE, CUSTOM_TYPE } from 'ee/geo_nodes/constants';
import { VALUE_TYPE, CUSTOM_TYPE, REPLICATION_HELP_URL } from 'ee/geo_nodes/constants';
import { rawMockNodeDetails } from '../mock_data';
describe('GeoNodeDetailItemComponent', () => {
......@@ -127,16 +128,51 @@ describe('GeoNodeDetailItemComponent', () => {
expect(wrapper.find(GeoNodeSyncProgress).exists()).toBeFalsy();
});
});
});
describe('itemEnabled', () => {
describe('when false', () => {
beforeEach(() => {
createComponent({
itemEnabled: false,
});
});
it('renders synchronization disabled text', () => {
expect(
wrapper
.find({ ref: 'disabledText' })
.text()
.trim(),
).toBe('Synchronization disabled');
});
it('renders GlPopover', () => {
expect(wrapper.find(GlPopover).exists()).toBeTruthy();
});
it('renders link to replication help documentation in popover', () => {
const popoverLink = wrapper.find(GlPopover).find(GlLink);
describe('when featureDisabled is true', () => {
expect(popoverLink.exists()).toBeTruthy();
expect(popoverLink.text()).toBe('Learn how to enable synchronization');
expect(popoverLink.attributes('href')).toBe(REPLICATION_HELP_URL);
});
});
describe('when true', () => {
beforeEach(() => {
createComponent({
featureDisabled: true,
itemEnabled: true,
});
});
it('does not render', () => {
expect(wrapper.vm.$el.innerHTML).toBeUndefined();
it('does not render synchronization disabled text', () => {
expect(wrapper.find('.node-detail-item').text()).not.toContain('Synchronization disabled');
});
it('does not render GlPopover', () => {
expect(wrapper.find(GlPopover).exists()).toBeFalsy();
});
});
});
......
......@@ -11879,6 +11879,9 @@ msgstr ""
msgid "Learn how to %{no_packages_link_start}publish and share your packages%{no_packages_link_end} with GitLab."
msgstr ""
msgid "Learn how to enable synchronization"
msgstr ""
msgid "Learn more"
msgstr ""
......@@ -13815,6 +13818,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
msgid "Nothing to synchronize"
msgstr ""
msgid "Notification events"
msgstr ""
......@@ -19845,6 +19851,12 @@ msgstr ""
msgid "Synced"
msgstr ""
msgid "Synchronization disabled"
msgstr ""
msgid "Synchronization of container repositories is disabled."
msgstr ""
msgid "System"
msgstr ""
......
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