Commit 2dd8d142 authored by Zack Cuddy's avatar Zack Cuddy

Make Geo Selective Sync more clear

Currently, it is not very clear when a
Node is set up for selective sync.

This MR moves the selective sync data
to the header section of the Node card.

It also unifies the verbiage "Groups" to replace
the limited usage of "Namespaces" in some sections.
parent d0ccf9f2
<script> <script>
import { sprintf, s__ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility'; import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import icon from '~/vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
...@@ -40,7 +40,11 @@ export default { ...@@ -40,7 +40,11 @@ export default {
return s__('GeoNodes|Full'); return s__('GeoNodes|Full');
} }
return `${s__('GeoNodes|Selective')} (${this.selectiveSyncType})`; // Renaming namespaces to groups in the UI for Geo Selective Sync
const syncLabel =
this.selectiveSyncType === 'namespaces' ? __('groups') : this.selectiveSyncType;
return sprintf(s__('GeoNodes|Selective (%{syncLabel})'), { syncLabel });
}, },
eventTimestampEmpty() { eventTimestampEmpty() {
return this.lastEvent.timeStamp === 0 || this.cursorLastEvent.timeStamp === 0; return this.lastEvent.timeStamp === 0 || this.cursorLastEvent.timeStamp === 0;
...@@ -117,7 +121,7 @@ export default { ...@@ -117,7 +121,7 @@ export default {
class="d-flex align-items-center" class="d-flex align-items-center"
data-placement="bottom" data-placement="bottom"
> >
<strong>{{ syncType }}</strong> <strong data-testid="syncType">{{ syncType }}</strong>
<icon name="retry" class="ml-2" /> <icon name="retry" class="ml-2" />
<span v-if="!eventTimestampEmpty" class="ml-2"> <span v-if="!eventTimestampEmpty" class="ml-2">
{{ syncStatusEventInfo }} {{ syncStatusEventInfo }}
......
<script> <script>
import { __ } from '~/locale'; import { __, sprintf } from '~/locale';
import GeoNodeHealthStatus from '../geo_node_health_status.vue'; import GeoNodeHealthStatus from '../geo_node_health_status.vue';
import GeoNodeActions from '../geo_node_actions.vue'; import GeoNodeActions from '../geo_node_actions.vue';
...@@ -45,6 +45,23 @@ export default { ...@@ -45,6 +45,23 @@ export default {
nodeHealthStatus() { nodeHealthStatus() {
return this.nodeDetails.healthy ? this.nodeDetails.health : this.nodeDetails.healthStatus; return this.nodeDetails.healthy ? this.nodeDetails.health : this.nodeDetails.healthStatus;
}, },
selectiveSyncronization() {
const { selectiveSyncType } = this.nodeDetails;
if (selectiveSyncType === 'shards') {
return sprintf(__('Shards (%{shards})'), {
shards: this.node.selectiveSyncShards.join(', '),
});
}
if (selectiveSyncType === 'namespaces') {
return sprintf(__('Groups (%{groups})'), {
groups: this.nodeDetails.namespaces.map(n => n.full_path).join(', '),
});
}
return null;
},
}, },
}; };
</script> </script>
...@@ -77,6 +94,12 @@ export default { ...@@ -77,6 +94,12 @@ export default {
{{ nodeVersion }} {{ nodeVersion }}
</span> </span>
</div> </div>
<div v-if="selectiveSyncronization" class="d-flex flex-column mt-2">
<span class="text-secondary-700">{{ s__('GeoNodes|Selective synchronization') }}</span>
<span data-testid="selectiveSync" class="mt-1 font-weight-bold">
{{ selectiveSyncronization }}
</span>
</div>
<geo-node-health-status :status="nodeHealthStatus" /> <geo-node-health-status :status="nodeHealthStatus" />
</div> </div>
</div> </div>
......
...@@ -56,6 +56,7 @@ export default class GeoNodesStore { ...@@ -56,6 +56,7 @@ export default class GeoNodesStore {
editPath: rawNode.web_edit_url, editPath: rawNode.web_edit_url,
geoProjectsUrl: rawNode.web_geo_projects_url, geoProjectsUrl: rawNode.web_geo_projects_url,
statusPath: rawNode._links.status, statusPath: rawNode._links.status,
selectiveSyncShards: rawNode.selective_sync_shards,
}; };
} }
...@@ -151,6 +152,7 @@ export default class GeoNodesStore { ...@@ -151,6 +152,7 @@ export default class GeoNodesStore {
timeStamp: rawNodeDetails.cursor_last_event_timestamp, timeStamp: rawNodeDetails.cursor_last_event_timestamp,
}, },
selectiveSyncType: rawNodeDetails.selective_sync_type, selectiveSyncType: rawNodeDetails.selective_sync_type,
namespaces: rawNodeDetails.namespaces,
dbReplicationLag: rawNodeDetails.db_replication_lag_seconds, dbReplicationLag: rawNodeDetails.db_replication_lag_seconds,
}; };
} }
......
---
title: Make Geo Selective Sync More Clear from the Node Details view
merge_request: 29596
author:
type: changed
...@@ -23,11 +23,37 @@ const createComponent = ( ...@@ -23,11 +23,37 @@ const createComponent = (
describe('GeoNodeSyncSettingsComponent', () => { describe('GeoNodeSyncSettingsComponent', () => {
describe('computed', () => { describe('computed', () => {
describe('syncType', () => { describe('syncType', () => {
it('returns string representing sync type', () => { let vm;
const vm = createComponent(); describe('when syncType is namespaces', () => {
beforeEach(() => {
vm = createComponent(false, 'namespaces');
});
afterEach(() => {
vm.$destroy();
});
it('renders the correct sync title', () => {
expect(vm.$el.querySelector('[data-testid="syncType"]').innerText.trim()).toBe(
'Selective (groups)',
);
});
});
expect(vm.syncType).toBe('Selective (namespaces)'); describe('when syncType is shards', () => {
vm.$destroy(); beforeEach(() => {
vm = createComponent(false, 'shards');
});
afterEach(() => {
vm.$destroy();
});
it('renders the correct sync title', () => {
expect(vm.$el.querySelector('[data-testid="syncType"]').innerText.trim()).toBe(
'Selective (shards)',
);
});
}); });
}); });
...@@ -35,7 +61,7 @@ describe('GeoNodeSyncSettingsComponent', () => { ...@@ -35,7 +61,7 @@ describe('GeoNodeSyncSettingsComponent', () => {
it('returns `true` if one of the event timestamp is empty', () => { it('returns `true` if one of the event timestamp is empty', () => {
const vmEmptyTimestamp = createComponent( const vmEmptyTimestamp = createComponent(
false, false,
mockNodeDetails.namespaces, mockNodeDetails.selectiveSyncType,
{ {
id: 0, id: 0,
timeStamp: 0, timeStamp: 0,
......
...@@ -71,6 +71,51 @@ describe('NodeDetailsSectionMain', () => { ...@@ -71,6 +71,51 @@ describe('NodeDetailsSectionMain', () => {
.catch(done.fail); .catch(done.fail);
}); });
}); });
describe('selectiveSyncronization', () => {
describe('when selectiveSyncronization is not enabled', () => {
beforeEach(() => {
vm = createComponent({ nodeDetails: { ...mockNodeDetails, selectiveSyncType: null } });
});
it('does not render selective sync information', () => {
expect(vm.$el.querySelector('[data-testid="selectiveSync"]')).toBeFalsy();
});
});
describe('when selectiveSyncronization is shards', () => {
beforeEach(() => {
vm = createComponent({
node: { ...mockNode, selectiveSyncShards: ['default', 'extra'] },
nodeDetails: { ...mockNodeDetails, selectiveSyncType: 'shards' },
});
});
it('renders Shards information correctly', () => {
expect(vm.$el.querySelector('[data-testid="selectiveSync"]').innerText.trim()).toBe(
'Shards (default, extra)',
);
});
});
describe('when selectiveSyncronization is namespaces', () => {
beforeEach(() => {
vm = createComponent({
nodeDetails: {
...mockNodeDetails,
selectiveSyncType: 'namespaces',
namespaces: [{ full_path: 'gitlab-org' }, { full_path: 'gitlab-com' }],
},
});
});
it('renders Groups information correctly', () => {
expect(vm.$el.querySelector('[data-testid="selectiveSync"]').innerText.trim()).toBe(
'Groups (gitlab-org, gitlab-com)',
);
});
});
});
}); });
describe('template', () => { describe('template', () => {
......
...@@ -46,7 +46,6 @@ describe('NodeDetailsSectionSync', () => { ...@@ -46,7 +46,6 @@ describe('NodeDetailsSectionSync', () => {
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
const syncSettings = wrapper.vm.syncSettings(); const syncSettings = wrapper.vm.syncSettings();
expect(syncSettings.syncStatusUnavailable).toBe(true); expect(syncSettings.syncStatusUnavailable).toBe(true);
expect(syncSettings.namespaces).toBe(mockNodeDetails.namespaces);
expect(syncSettings.lastEvent).toBe(mockNodeDetails.lastEvent); expect(syncSettings.lastEvent).toBe(mockNodeDetails.lastEvent);
expect(syncSettings.cursorLastEvent).toBe(mockNodeDetails.cursorLastEvent); expect(syncSettings.cursorLastEvent).toBe(mockNodeDetails.cursorLastEvent);
}); });
......
...@@ -19,6 +19,7 @@ export const mockNodes = [ ...@@ -19,6 +19,7 @@ export const mockNodes = [
verification_max_capacity: 100, verification_max_capacity: 100,
clone_protocol: 'http', clone_protocol: 'http',
web_edit_url: 'http://127.0.0.1:3001/admin/geo/nodes/1/edit', web_edit_url: 'http://127.0.0.1:3001/admin/geo/nodes/1/edit',
selective_sync_shards: [],
_links: { _links: {
self: 'http://127.0.0.1:3001/api/v4/geo_nodes/1', self: 'http://127.0.0.1:3001/api/v4/geo_nodes/1',
repair: 'http://127.0.0.1:3001/api/v4/geo_nodes/1/repair', repair: 'http://127.0.0.1:3001/api/v4/geo_nodes/1/repair',
...@@ -39,6 +40,7 @@ export const mockNodes = [ ...@@ -39,6 +40,7 @@ export const mockNodes = [
sync_object_storage: true, sync_object_storage: true,
clone_protocol: 'http', clone_protocol: 'http',
web_edit_url: 'http://127.0.0.1:3001/admin/geo/nodes/1/edit', web_edit_url: 'http://127.0.0.1:3001/admin/geo/nodes/1/edit',
selective_sync_shards: [],
_links: { _links: {
self: 'http://127.0.0.1:3001/api/v4/geo_nodes/2', self: 'http://127.0.0.1:3001/api/v4/geo_nodes/2',
repair: 'http://127.0.0.1:3001/api/v4/geo_nodes/2/repair', repair: 'http://127.0.0.1:3001/api/v4/geo_nodes/2/repair',
...@@ -61,6 +63,7 @@ export const mockNode = { ...@@ -61,6 +63,7 @@ export const mockNode = {
repairPath: 'http://127.0.0.1:3001/api/v4/geo_nodes/1/repair', repairPath: 'http://127.0.0.1:3001/api/v4/geo_nodes/1/repair',
statusPath: 'http://127.0.0.1:3001/api/v4/geo_nodes/1/status', statusPath: 'http://127.0.0.1:3001/api/v4/geo_nodes/1/status',
editPath: 'http://127.0.0.1:3001/admin/geo/nodes/1/edit', editPath: 'http://127.0.0.1:3001/admin/geo/nodes/1/edit',
selective_sync_shards: [],
}; };
export const rawMockNodeDetails = { export const rawMockNodeDetails = {
...@@ -239,5 +242,6 @@ export const mockNodeDetails = { ...@@ -239,5 +242,6 @@ export const mockNodeDetails = {
timeStamp: 1511255200, timeStamp: 1511255200,
}, },
selectiveSyncType: 'namespaces', selectiveSyncType: 'namespaces',
namespaces: [],
dbReplicationLag: 0, dbReplicationLag: 0,
}; };
...@@ -9509,7 +9509,10 @@ msgstr "" ...@@ -9509,7 +9509,10 @@ msgstr ""
msgid "GeoNodes|Repository verification progress" msgid "GeoNodes|Repository verification progress"
msgstr "" msgstr ""
msgid "GeoNodes|Selective" msgid "GeoNodes|Selective (%{syncLabel})"
msgstr ""
msgid "GeoNodes|Selective synchronization"
msgstr "" msgstr ""
msgid "GeoNodes|Something went wrong while changing node status" msgid "GeoNodes|Something went wrong while changing node status"
...@@ -10538,6 +10541,9 @@ msgstr "" ...@@ -10538,6 +10541,9 @@ msgstr ""
msgid "Groups (%{count})" msgid "Groups (%{count})"
msgstr "" msgstr ""
msgid "Groups (%{groups})"
msgstr ""
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}." msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr "" msgstr ""
...@@ -18657,6 +18663,9 @@ msgstr "" ...@@ -18657,6 +18663,9 @@ msgstr ""
msgid "Severity: %{severity}" msgid "Severity: %{severity}"
msgstr "" msgstr ""
msgid "Shards (%{shards})"
msgstr ""
msgid "Shards to synchronize" msgid "Shards to synchronize"
msgstr "" msgstr ""
...@@ -24641,6 +24650,9 @@ msgstr "" ...@@ -24641,6 +24650,9 @@ msgstr ""
msgid "group" msgid "group"
msgstr "" msgstr ""
msgid "groups"
msgstr ""
msgid "has already been linked to another vulnerability" msgid "has already been linked to another vulnerability"
msgstr "" 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