Commit c3efd8e0 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'kp-fix-geo-admin-bugs' into 'master'

Handle empty event timestamp and larger memory units

Closes #4653 and #4664

See merge request gitlab-org/gitlab-ee!4206
parents 041100ee 6d16eb9b
---
title: Handle empty event timestamp and larger memory units
merge_request: 4206
author:
type: fixed
......@@ -2,7 +2,7 @@
/* eslint-disable vue/no-side-effects-in-computed-properties */
import { s__, __ } from '~/locale';
import { parseSeconds, stringifyTime } from '~/lib/utils/pretty_time';
import { bytesToMiB } from '~/lib/utils/number_utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import icon from '~/vue_shared/components/icon.vue';
import { VALUE_TYPE, CUSTOM_TYPE } from '../constants';
......@@ -100,7 +100,7 @@
return `${this.nodeDetails.version} (${this.nodeDetails.revision})`;
},
replicationSlotWAL() {
return `${bytesToMiB(this.nodeDetails.replicationSlotWAL)} MB`;
return numberToHumanSize(this.nodeDetails.replicationSlotWAL);
},
dbReplicationLag() {
// Replication lag can be nil if the secondary isn't actually streaming
......
......@@ -35,16 +35,24 @@
<div
class="node-detail-value"
>
<strong>
{{ eventId }}
</strong>
<span
v-tooltip
class="event-status-timestamp"
data-placement="bottom"
:title="timeStampString"
<template v-if="eventTimeStamp">
<strong>
{{ eventId }}
</strong>
<span
v-tooltip
v-if="eventTimeStamp"
class="event-status-timestamp"
data-placement="bottom"
:title="timeStampString"
>
({{ timeFormated(timeStamp) }})
</span>
</template>
<strong
v-else
>
({{ timeFormated(timeStamp) }})
</span>
{{ __('Not available') }}
</strong>
</div>
</template>
......@@ -32,6 +32,9 @@
syncType() {
return this.namespaces.length > 0 ? s__('GeoNodes|Selective') : s__('GeoNodes|Full');
},
eventTimestampEmpty() {
return this.lastEvent.timeStamp === 0 || this.cursorLastEvent.timeStamp === 0;
},
syncLagInSeconds() {
return this.lagInSeconds(this.lastEvent.timeStamp, this.cursorLastEvent.timeStamp);
},
......@@ -79,7 +82,8 @@
return `${timeAgoStr} (${pendingEvents} events)`;
},
statusTooltip(lagInSeconds) {
if (lagInSeconds <= TIME_DIFF.FIVE_MINS) {
if (this.eventTimestampEmpty ||
lagInSeconds <= TIME_DIFF.FIVE_MINS) {
return '';
} else if (lagInSeconds > TIME_DIFF.FIVE_MINS &&
lagInSeconds <= TIME_DIFF.HOUR) {
......@@ -107,6 +111,7 @@
css-classes="sync-status-icon prepend-left-5"
/>
<span
v-if="!eventTimestampEmpty"
class="sync-status-event-info prepend-left-5"
>
{{ syncStatusEventInfo }}
......
......@@ -74,11 +74,11 @@ export default class GeoNodesStore {
failureCount: rawNodeDetails.attachments_failed_count || 0,
},
lastEvent: {
id: rawNodeDetails.last_event_id,
id: rawNodeDetails.last_event_id || 0,
timeStamp: rawNodeDetails.last_event_timestamp,
},
cursorLastEvent: {
id: rawNodeDetails.cursor_last_event_id,
id: rawNodeDetails.cursor_last_event_id || 0,
timeStamp: rawNodeDetails.cursor_last_event_timestamp,
},
namespaces: rawNodeDetails.namespaces,
......
......@@ -85,7 +85,7 @@ describe('GeoNodeDetailsComponent', () => {
describe('replicationSlotWAL', () => {
it('returns replication slot WAL in Megabytes', () => {
expect(vm.replicationSlotWAL).toBe('0 MB');
expect(vm.replicationSlotWAL).toBe('479.37 MiB');
});
});
......
......@@ -5,12 +5,15 @@ import { mockNodeDetails } from '../mock_data';
import mountComponent from '../../helpers/vue_mount_component_helper';
const createComponent = () => {
const createComponent = (
eventId = mockNodeDetails.lastEvent.id,
eventTimeStamp = mockNodeDetails.lastEvent.timeStamp,
) => {
const Component = Vue.extend(geoNodeEventStatusComponent);
return mountComponent(Component, {
eventId: mockNodeDetails.lastEvent.id,
eventTimeStamp: mockNodeDetails.lastEvent.timeStamp,
eventId,
eventTimeStamp,
});
};
......@@ -46,5 +49,13 @@ describe('GeoNodeEventStatus', () => {
expect(vm.$el.querySelector('strong').innerText.trim()).toBe(`${mockNodeDetails.lastEvent.id}`);
expect(vm.$el.querySelector('.event-status-timestamp').innerText).toContain('ago');
});
it('renders empty state when timestamp is not present', () => {
const vmWithoutTimestamp = createComponent(0, 0);
expect(vmWithoutTimestamp.$el.querySelectorAll('strong').length).not.toBe(0);
expect(vmWithoutTimestamp.$el.querySelectorAll('.event-status-timestamp').length).toBe(0);
expect(vmWithoutTimestamp.$el.querySelector('strong').innerText.trim()).toBe('Not available');
vmWithoutTimestamp.$destroy();
});
});
});
......@@ -27,6 +27,26 @@ describe('GeoNodeSyncSettingsComponent', () => {
vm.$destroy();
});
});
describe('eventTimestampEmpty', () => {
it('returns `true` if one of the event timestamp is empty', () => {
const vmEmptyTimestamp = createComponent(mockNodeDetails.namespaces, {
id: 0,
timeStamp: 0,
}, {
id: 0,
timeStamp: 0,
});
expect(vmEmptyTimestamp.eventTimestampEmpty).toBeTruthy();
vmEmptyTimestamp.$destroy();
});
it('return `false` if one of the event timestamp is present', () => {
const vm = createComponent();
expect(vm.eventTimestampEmpty).toBeFalsy();
vm.$destroy();
});
});
});
describe('methods', () => {
......
......@@ -110,7 +110,7 @@ export const mockNodeDetails = {
revision: 'b93c51849b',
primaryVersion: '10.4.0-pre',
primaryRevision: 'b93c51849b',
replicationSlotWAL: null,
replicationSlotWAL: 502658737,
missingOAuthApplication: false,
storageShardsMatch: false,
replicationSlots: {
......
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