Commit a6d5966a authored by Mike Greiling's avatar Mike Greiling

Merge branch '287978_05-geo-beta-primary-details' into 'master'

Geo Node Status 2.0 - Primary Details

See merge request gitlab-org/gitlab!55695
parents 2064b8d3 e36d606d
<script> <script>
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import GeoNodeCoreDetails from './geo_node_core_details.vue'; import GeoNodeCoreDetails from './geo_node_core_details.vue';
import GeoNodePrimaryOtherInfo from './primary_node/geo_node_primary_other_info.vue';
import GeoNodeVerificationInfo from './primary_node/geo_node_verification_info.vue';
export default { export default {
name: 'GeoNodeDetails', name: 'GeoNodeDetails',
i18n: { i18n: {
primaryDetails: s__('Geo|Primary Details'),
secondaryDetails: s__('Geo|Secondary Details'), secondaryDetails: s__('Geo|Secondary Details'),
}, },
components: { components: {
GeoNodeCoreDetails, GeoNodeCoreDetails,
GeoNodePrimaryOtherInfo,
GeoNodeVerificationInfo,
}, },
props: { props: {
node: { node: {
...@@ -27,7 +30,11 @@ export default { ...@@ -27,7 +30,11 @@ export default {
v-if="node.primary" v-if="node.primary"
class="gl-display-flex gl-sm-flex-direction-column gl-align-items-flex-start gl-h-full gl-w-full" class="gl-display-flex gl-sm-flex-direction-column gl-align-items-flex-start gl-h-full gl-w-full"
> >
<p data-testid="primary-node-details">{{ $options.i18n.primaryDetails }}</p> <geo-node-verification-info
class="gl-flex-fill-1 gl-mb-5 gl-md-mb-0 gl-md-mr-5 gl-h-full gl-w-full"
:node="node"
/>
<geo-node-primary-other-info class="gl-flex-fill-1 gl-h-full gl-w-full" :node="node" />
</div> </div>
<div v-else class="gl-display-flex gl-flex-direction-column gl-h-full gl-w-full"> <div v-else class="gl-display-flex gl-flex-direction-column gl-h-full gl-w-full">
<p data-testid="secondary-node-details">{{ $options.i18n.secondaryDetails }}</p> <p data-testid="secondary-node-details">{{ $options.i18n.secondaryDetails }}</p>
......
<script>
import { GlCard } from '@gitlab/ui';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { __, s__ } from '~/locale';
export default {
name: 'GeoNodePrimaryOtherInfo',
i18n: {
otherInformation: __('Other information'),
progressBarPlaceholder: s__('Geo|Progress Bar Placeholder'),
replicationSlotWAL: s__('Geo|Replication slot WAL'),
},
components: {
GlCard,
},
props: {
node: {
type: Object,
required: true,
},
},
computed: {
replicationSlotWAL() {
return numberToHumanSize(this.node.replicationSlotsMaxRetainedWalBytes);
},
replicationSlots() {
return {
title: s__('Geo|Replication slots'),
values: {
total: this.node.replicationSlotsCount || 0,
success: this.node.replicationSlotsUsedCount || 0,
},
};
},
},
};
</script>
<template>
<gl-card>
<template #header>
<h5 class="gl-my-0">{{ $options.i18n.otherInformation }}</h5>
</template>
<div class="gl-mb-5">
<span>{{ replicationSlots.title }}</span>
<p data-testid="replication-progress-bar">{{ $options.i18n.progressBarPlaceholder }}</p>
</div>
<div
v-if="node.replicationSlotsMaxRetainedWalBytes"
class="gl-display-flex gl-flex-direction-column gl-mb-5"
>
<span>{{ $options.i18n.replicationSlotWAL }}</span>
<span class="gl-font-weight-bold gl-mt-2" data-testid="replication-slot-wal">{{
replicationSlotWAL
}}</span>
</div>
</gl-card>
</template>
<script>
import { GlCard, GlIcon, GlPopover, GlLink } from '@gitlab/ui';
import { mapGetters } from 'vuex';
import { HELP_INFO_URL } from 'ee/geo_nodes_beta/constants';
import { sprintf, s__, __ } from '~/locale';
export default {
name: 'GeoNodeVerificationInfo',
i18n: {
verificationInfo: s__('Geo|Verificaton information'),
replicationHelpText: s__(
'Geo|Replicated data is verified with the secondary node(s) using checksums.',
),
learnMore: __('Learn more'),
progressBarPlaceholder: s__('Geo|Progress Bar Placeholder'),
},
components: {
GlCard,
GlIcon,
GlPopover,
GlLink,
},
props: {
node: {
type: Object,
required: true,
},
},
computed: {
...mapGetters(['verificationInfo']),
verificationInfoBars() {
return this.verificationInfo(this.node.id);
},
},
methods: {
buildTitle(title) {
return sprintf(s__('Geo|%{title} checksum progress'), { title });
},
},
HELP_INFO_URL,
};
</script>
<template>
<gl-card header-class="gl-display-flex gl-align-items-center">
<template #header>
<h5 class="gl-my-0">{{ $options.i18n.verificationInfo }}</h5>
<gl-icon
ref="verificationInfo"
name="question"
class="gl-text-blue-500 gl-cursor-pointer gl-ml-2"
/>
<gl-popover :target="() => $refs.verificationInfo.$el" placement="top" triggers="hover focus">
<p class="gl-font-base">
{{ $options.i18n.replicationHelpText }}
</p>
<gl-link :href="$options.HELP_INFO_URL" target="_blank">{{
$options.i18n.learnMore
}}</gl-link>
</gl-popover>
</template>
<div v-for="bar in verificationInfoBars" :key="bar.title" class="gl-mb-5">
<span data-testid="verification-bar-title">{{ buildTitle(bar.title) }}</span>
<p data-testid="verification-progress-bar">{{ $options.i18n.progressBarPlaceholder }}</p>
</div>
</gl-card>
</template>
...@@ -12,6 +12,11 @@ export const GEO_TROUBLESHOOTING_URL = helpPagePath( ...@@ -12,6 +12,11 @@ export const GEO_TROUBLESHOOTING_URL = helpPagePath(
'administration/geo/replication/troubleshooting.html', 'administration/geo/replication/troubleshooting.html',
); );
export const HELP_INFO_URL = helpPagePath(
'administration/geo/disaster_recovery/background_verification.html',
{ anchor: 'repository-verification' },
);
export const HEALTH_STATUS_UI = { export const HEALTH_STATUS_UI = {
healthy: { healthy: {
icon: 'status_success', icon: 'status_success',
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import GeoNodeCoreDetails from 'ee/geo_nodes_beta/components/details/geo_node_core_details.vue'; import GeoNodeCoreDetails from 'ee/geo_nodes_beta/components/details/geo_node_core_details.vue';
import GeoNodeDetails from 'ee/geo_nodes_beta/components/details/geo_node_details.vue'; import GeoNodeDetails from 'ee/geo_nodes_beta/components/details/geo_node_details.vue';
import GeoNodePrimaryOtherInfo from 'ee/geo_nodes_beta/components/details/primary_node/geo_node_primary_other_info.vue';
import GeoNodeVerificationInfo from 'ee/geo_nodes_beta/components/details/primary_node/geo_node_verification_info.vue';
import { MOCK_NODES } from 'ee_jest/geo_nodes_beta/mock_data'; import { MOCK_NODES } from 'ee_jest/geo_nodes_beta/mock_data';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
...@@ -27,7 +29,8 @@ describe('GeoNodeDetails', () => { ...@@ -27,7 +29,8 @@ describe('GeoNodeDetails', () => {
}); });
const findGeoNodeCoreDetails = () => wrapper.findComponent(GeoNodeCoreDetails); const findGeoNodeCoreDetails = () => wrapper.findComponent(GeoNodeCoreDetails);
const findGeoNodePrimaryDetails = () => wrapper.findByTestId('primary-node-details'); const findGeoNodePrimaryOtherInfo = () => wrapper.findComponent(GeoNodePrimaryOtherInfo);
const findGeoNodeVerificationInfo = () => wrapper.findComponent(GeoNodeVerificationInfo);
const findGeoNodeSecondaryDetails = () => wrapper.findByTestId('secondary-node-details'); const findGeoNodeSecondaryDetails = () => wrapper.findByTestId('secondary-node-details');
describe('template', () => { describe('template', () => {
...@@ -42,23 +45,32 @@ describe('GeoNodeDetails', () => { ...@@ -42,23 +45,32 @@ describe('GeoNodeDetails', () => {
}); });
describe.each` describe.each`
node | showPrimaryDetails | showSecondaryDetails node | showPrimaryOtherInfo | showPrimaryVerificationInfo | showSecondaryDetails
${MOCK_NODES[0]} | ${true} | ${false} ${MOCK_NODES[0]} | ${true} | ${true} | ${false}
${MOCK_NODES[1]} | ${false} | ${true} ${MOCK_NODES[1]} | ${false} | ${false} | ${true}
`(`conditionally`, ({ node, showPrimaryDetails, showSecondaryDetails }) => { `(
`conditionally`,
({ node, showPrimaryOtherInfo, showPrimaryVerificationInfo, showSecondaryDetails }) => {
beforeEach(() => { beforeEach(() => {
createComponent({ node }); createComponent({ node });
}); });
describe(`when primary is ${node.primary}`, () => { describe(`when primary is ${node.primary}`, () => {
it(`does ${showPrimaryDetails ? '' : 'not '}render GeoNodePrimaryDetails`, () => { it(`does ${showPrimaryOtherInfo ? '' : 'not '}render GeoNodePrimaryInfo`, () => {
expect(findGeoNodePrimaryDetails().exists()).toBe(showPrimaryDetails); expect(findGeoNodePrimaryOtherInfo().exists()).toBe(showPrimaryOtherInfo);
});
it(`does ${
showPrimaryVerificationInfo ? '' : 'not '
}render GeoNodeVerificationInfo`, () => {
expect(findGeoNodeVerificationInfo().exists()).toBe(showPrimaryVerificationInfo);
}); });
it(`does ${showSecondaryDetails ? '' : 'not '}render GeoNodeSecondaryDetails`, () => { it(`does ${showSecondaryDetails ? '' : 'not '}render GeoNodeSecondaryDetails`, () => {
expect(findGeoNodeSecondaryDetails().exists()).toBe(showSecondaryDetails); expect(findGeoNodeSecondaryDetails().exists()).toBe(showSecondaryDetails);
}); });
}); });
}); },
);
}); });
}); });
import { GlCard } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import GeoNodePrimaryOtherInfo from 'ee/geo_nodes_beta/components/details/primary_node/geo_node_primary_other_info.vue';
import { MOCK_NODES } from 'ee_jest/geo_nodes_beta/mock_data';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { numberToHumanSize } from '~/lib/utils/number_utils';
describe('GeoNodePrimaryOtherInfo', () => {
let wrapper;
const defaultProps = {
node: MOCK_NODES[0],
};
const createComponent = (props) => {
wrapper = extendedWrapper(
shallowMount(GeoNodePrimaryOtherInfo, {
propsData: {
...defaultProps,
...props,
},
}),
);
};
afterEach(() => {
wrapper.destroy();
});
const findGlCard = () => wrapper.findComponent(GlCard);
const findGeoNodeProgressBar = () => wrapper.findByTestId('replication-progress-bar');
const findReplicationSlotWAL = () => wrapper.findByTestId('replication-slot-wal');
describe('template', () => {
describe('always', () => {
beforeEach(() => {
createComponent();
});
it('renders the details card', () => {
expect(findGlCard().exists()).toBe(true);
});
it('renders the replicationSlots progress bar', () => {
expect(findGeoNodeProgressBar().exists()).toBe(true);
});
});
describe('when replicationSlotWAL exists', () => {
beforeEach(() => {
createComponent({ node: MOCK_NODES[0] });
});
it('renders the replicationSlotWAL section correctly', () => {
expect(findReplicationSlotWAL().exists()).toBe(true);
expect(findReplicationSlotWAL().text()).toBe(
numberToHumanSize(MOCK_NODES[0].replicationSlotsMaxRetainedWalBytes),
);
});
});
describe('when replicationSlotWAL is null', () => {
beforeEach(() => {
createComponent({ node: MOCK_NODES[1] });
});
it('does not render the replicationSlotWAL section', () => {
expect(findReplicationSlotWAL().exists()).toBe(false);
});
});
});
});
import { GlCard, GlIcon, GlPopover, GlLink } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import GeoNodeVerificationInfo from 'ee/geo_nodes_beta/components/details/primary_node/geo_node_verification_info.vue';
import { HELP_INFO_URL } from 'ee/geo_nodes_beta/constants';
import { MOCK_NODES, MOCK_PRIMARY_VERIFICATION_INFO } from 'ee_jest/geo_nodes_beta/mock_data';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
Vue.use(Vuex);
describe('GeoNodeVerificationInfo', () => {
let wrapper;
const defaultProps = {
node: MOCK_NODES[0],
};
const createComponent = (props) => {
const store = new Vuex.Store({
getters: {
verificationInfo: () => () => MOCK_PRIMARY_VERIFICATION_INFO,
},
});
wrapper = extendedWrapper(
mount(GeoNodeVerificationInfo, {
store,
propsData: {
...defaultProps,
...props,
},
}),
);
};
afterEach(() => {
wrapper.destroy();
});
const findGlCard = () => wrapper.findComponent(GlCard);
const findGlIcon = () => wrapper.findComponent(GlIcon);
const findGlPopover = () => wrapper.findComponent(GlPopover);
const findGlPopoverLink = () => findGlPopover().findComponent(GlLink);
const findGeoNodeProgressBarTitles = () => wrapper.findAllByTestId('verification-bar-title');
const findGeoNodeProgressBars = () => wrapper.findAllByTestId('verification-progress-bar');
describe('template', () => {
describe('always', () => {
beforeEach(() => {
createComponent();
});
it('renders the details card', () => {
expect(findGlCard().exists()).toBe(true);
});
it('renders the question icon correctly', () => {
expect(findGlIcon().exists()).toBe(true);
expect(findGlIcon().props('name')).toBe('question');
});
it('renders the GlPopover always', () => {
expect(findGlPopover().exists()).toBe(true);
});
it('renders the popover link correctly', () => {
expect(findGlPopoverLink().exists()).toBe(true);
expect(findGlPopoverLink().attributes('href')).toBe(HELP_INFO_URL);
});
it('renders a progress bar for each verification replicable', () => {
expect(findGeoNodeProgressBars()).toHaveLength(MOCK_PRIMARY_VERIFICATION_INFO.length);
});
it('renders progress bar titles correctly', () => {
expect(findGeoNodeProgressBarTitles().wrappers.map((w) => w.text())).toStrictEqual(
MOCK_PRIMARY_VERIFICATION_INFO.map((vI) => `${vI.title} checksum progress`),
);
});
});
});
});
...@@ -13833,6 +13833,9 @@ msgstr "" ...@@ -13833,6 +13833,9 @@ msgstr ""
msgid "Geo|%{name} is scheduled for re-verify" msgid "Geo|%{name} is scheduled for re-verify"
msgstr "" msgstr ""
msgid "Geo|%{title} checksum progress"
msgstr ""
msgid "Geo|Add site" msgid "Geo|Add site"
msgstr "" msgstr ""
...@@ -13947,15 +13950,15 @@ msgstr "" ...@@ -13947,15 +13950,15 @@ msgstr ""
msgid "Geo|Please refer to Geo Troubleshooting." msgid "Geo|Please refer to Geo Troubleshooting."
msgstr "" msgstr ""
msgid "Geo|Primary Details"
msgstr ""
msgid "Geo|Primary node" msgid "Geo|Primary node"
msgstr "" msgstr ""
msgid "Geo|Primary site" msgid "Geo|Primary site"
msgstr "" msgstr ""
msgid "Geo|Progress Bar Placeholder"
msgstr ""
msgid "Geo|Project" msgid "Geo|Project"
msgstr "" msgstr ""
...@@ -13980,6 +13983,15 @@ msgstr "" ...@@ -13980,6 +13983,15 @@ msgstr ""
msgid "Geo|Remove tracking database entry" msgid "Geo|Remove tracking database entry"
msgstr "" msgstr ""
msgid "Geo|Replicated data is verified with the secondary node(s) using checksums."
msgstr ""
msgid "Geo|Replication slot WAL"
msgstr ""
msgid "Geo|Replication slots"
msgstr ""
msgid "Geo|Resync" msgid "Geo|Resync"
msgstr "" msgstr ""
...@@ -14055,6 +14067,9 @@ msgstr "" ...@@ -14055,6 +14067,9 @@ msgstr ""
msgid "Geo|Verification failed - %{error}" msgid "Geo|Verification failed - %{error}"
msgstr "" msgstr ""
msgid "Geo|Verificaton information"
msgstr ""
msgid "Geo|Waiting for scheduler" msgid "Geo|Waiting for scheduler"
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