Commit 084fccbf authored by Zack Cuddy's avatar Zack Cuddy Committed by Douglas Barbosa Alexandre

Geo Replication - Confirm Resync/Reverify Action

parent bd83254b
<script> <script>
import { GlSearchBoxByType, GlDropdown, GlDropdownItem, GlButton } from '@gitlab/ui'; import {
GlSearchBoxByType,
GlDropdown,
GlDropdownItem,
GlButton,
GlModal,
GlSprintf,
GlModalDirective,
} from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import { __, sprintf } from '~/locale'; import { s__, __, sprintf } from '~/locale';
import { DEFAULT_SEARCH_DELAY, ACTION_TYPES, FILTER_STATES } from '../constants'; import { DEFAULT_SEARCH_DELAY, ACTION_TYPES, FILTER_STATES, RESYNC_MODAL_ID } from '../constants';
export default { export default {
name: 'GeoReplicableFilterBar', name: 'GeoReplicableFilterBar',
i18n: {
resyncAll: s__('Geo|Resync all'),
resyncAllReplicables: s__('Geo|Resync all %{replicableType}'),
dropdownTitle: s__('Geo|Filter by status'),
searchPlaceholder: s__('Geo|Filter by name'),
modalBody: s__(
'Geo|This will resync all %{replicableType}. It may take some time to complete. Are you sure you want to continue?',
),
},
components: { components: {
GlSearchBoxByType, GlSearchBoxByType,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
GlButton, GlButton,
GlModal,
GlSprintf,
},
directives: {
GlModalDirective,
}, },
computed: { computed: {
...mapState(['currentFilterIndex', 'filterOptions', 'searchFilter']), ...mapState(['currentFilterIndex', 'filterOptions', 'searchFilter']),
...@@ -25,7 +47,7 @@ export default { ...@@ -25,7 +47,7 @@ export default {
}, },
}, },
resyncText() { resyncText() {
return sprintf(__('Resync all %{replicableType}'), { return sprintf(this.$options.i18n.resyncAllReplicables, {
replicableType: this.replicableTypeName, replicableType: this.replicableTypeName,
}); });
}, },
...@@ -40,6 +62,13 @@ export default { ...@@ -40,6 +62,13 @@ export default {
actionTypes: ACTION_TYPES, actionTypes: ACTION_TYPES,
filterStates: FILTER_STATES, filterStates: FILTER_STATES,
debounce: DEFAULT_SEARCH_DELAY, debounce: DEFAULT_SEARCH_DELAY,
MODAL_PRIMARY_ACTION: {
text: s__('Geo|Resync all'),
},
MODAL_CANCEL_ACTION: {
text: __('Cancel'),
},
RESYNC_MODAL_ID,
}; };
</script> </script>
...@@ -48,7 +77,7 @@ export default { ...@@ -48,7 +77,7 @@ export default {
<div class="row d-flex flex-column flex-sm-row"> <div class="row d-flex flex-column flex-sm-row">
<div class="col"> <div class="col">
<div class="d-sm-flex mx-n1"> <div class="d-sm-flex mx-n1">
<gl-dropdown :text="__('Filter by status')" class="px-1 my-1 my-sm-0 w-100"> <gl-dropdown :text="$options.i18n.dropdownTitle" class="px-1 my-1 my-sm-0 w-100">
<gl-dropdown-item <gl-dropdown-item
v-for="(filter, index) in filterOptions" v-for="(filter, index) in filterOptions"
:key="index" :key="index"
...@@ -66,15 +95,27 @@ export default { ...@@ -66,15 +95,27 @@ export default {
:debounce="$options.debounce" :debounce="$options.debounce"
class="px-1 my-1 my-sm-0 bg-white w-100" class="px-1 my-1 my-sm-0 bg-white w-100"
type="text" type="text"
:placeholder="__('Filter by name')" :placeholder="$options.i18n.searchPlaceholder"
/> />
</div> </div>
</div> </div>
<div class="col col-sm-5 d-flex justify-content-end my-1 my-sm-0 w-100"> <div class="col col-sm-5 d-flex justify-content-end my-1 my-sm-0 w-100">
<gl-button @click="initiateAllReplicableSyncs($options.actionTypes.RESYNC)">{{ <gl-button v-gl-modal-directive="$options.RESYNC_MODAL_ID">{{
__('Resync all') $options.i18n.resyncAll
}}</gl-button> }}</gl-button>
</div> </div>
</div> </div>
<gl-modal
:modal-id="$options.RESYNC_MODAL_ID"
:title="resyncText"
:action-primary="$options.MODAL_PRIMARY_ACTION"
:action-cancel="$options.MODAL_CANCEL_ACTION"
size="sm"
@primary="initiateAllReplicableSyncs($options.actionTypes.RESYNC)"
>
<gl-sprintf :message="$options.i18n.modalBody">
<template #replicableType>{{ replicableTypeName }}</template>
</gl-sprintf>
</gl-modal>
</nav> </nav>
</template> </template>
...@@ -49,3 +49,5 @@ export const PREV = 'prev'; ...@@ -49,3 +49,5 @@ export const PREV = 'prev';
export const NEXT = 'next'; export const NEXT = 'next';
export const DEFAULT_PAGE_SIZE = 20; export const DEFAULT_PAGE_SIZE = 20;
export const RESYNC_MODAL_ID = 'resync-all-geo';
...@@ -145,11 +145,39 @@ module EE ...@@ -145,11 +145,39 @@ module EE
end end
def resync_all_button def resync_all_button
button_to(s_("Geo|Resync all"), { controller: controller_name, action: :resync_all }, class: "btn btn-default btn-md mr-2") # This is deprecated and Hard Coded for Projects.
# All new replicable types should be using geo_replicable/app.vue
resync_all_projects_modal_data = {
path: resync_all_admin_geo_projects_url,
method: 'post',
modal_attributes: {
title: s_('Geo|Resync all projects'),
message: s_('Geo|This will resync all projects. It may take some time to complete. Are you sure you want to continue?'),
okTitle: s_('Geo|Resync all'),
size: 'sm'
}
}
button_tag(s_("Geo|Resync all"), type: "button", class: 'gl-button btn btn-default gl-mr-3 js-confirm-modal-button', data: resync_all_projects_modal_data)
end end
def reverify_all_button def reverify_all_button
button_to(s_("Geo|Reverify all"), { controller: controller_name, action: :reverify_all }, class: "btn btn-default btn-md") # This is deprecated and Hard Coded for Projects.
# All new replicable types should be using geo_replicable/app.vue
reverify_all_projects_modal_data = {
path: reverify_all_admin_geo_projects_url,
method: 'post',
modal_attributes: {
title: s_('Geo|Reverify all projects'),
message: s_('Geo|This will reverify all projects. It may take some time to complete. Are you sure you want to continue?'),
okTitle: s_('Geo|Reverify all'),
size: 'sm'
}
}
button_tag(s_("Geo|Reverify all"), type: "button", class: 'gl-button btn btn-default gl-mr-3 js-confirm-modal-button', data: reverify_all_projects_modal_data)
end end
def replicable_types def replicable_types
......
...@@ -209,9 +209,13 @@ RSpec.describe 'admin Geo Projects', :js, :geo do ...@@ -209,9 +209,13 @@ RSpec.describe 'admin Geo Projects', :js, :geo do
wait_for_requests wait_for_requests
end end
it 'fires job to resync all projects' do it 'opens confirm modal and then fires job to resync all projects' do
page.click_button('Resync all') page.click_button('Resync all')
page.within('.modal') do
page.click_button('Resync all')
end
expect(find_toast).to have_text('All projects are being scheduled for resync') expect(find_toast).to have_text('All projects are being scheduled for resync')
end end
end end
...@@ -222,9 +226,13 @@ RSpec.describe 'admin Geo Projects', :js, :geo do ...@@ -222,9 +226,13 @@ RSpec.describe 'admin Geo Projects', :js, :geo do
wait_for_requests wait_for_requests
end end
it 'fires job to reverify all projects' do it 'opens confirm modal and then fires job to reverify all projects' do
page.click_button('Reverify all') page.click_button('Reverify all')
page.within('.modal') do
page.click_button('Reverify all')
end
expect(find_toast).to have_text('All projects are being scheduled for reverify') expect(find_toast).to have_text('All projects are being scheduled for reverify')
end end
end end
......
import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlButton } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlButton, GlModal } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import GeoReplicableFilterBar from 'ee/geo_replicable/components/geo_replicable_filter_bar.vue'; import GeoReplicableFilterBar from 'ee/geo_replicable/components/geo_replicable_filter_bar.vue';
import { DEFAULT_SEARCH_DELAY } from 'ee/geo_replicable/constants'; import { DEFAULT_SEARCH_DELAY, RESYNC_MODAL_ID } from 'ee/geo_replicable/constants';
import { getStoreConfig } from 'ee/geo_replicable/store'; import { getStoreConfig } from 'ee/geo_replicable/store';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { MOCK_REPLICABLE_TYPE } from '../mock_data'; import { MOCK_REPLICABLE_TYPE } from '../mock_data';
const localVue = createLocalVue(); Vue.use(Vuex);
localVue.use(Vuex);
describe('GeoReplicableFilterBar', () => { describe('GeoReplicableFilterBar', () => {
let wrapper; let wrapper;
...@@ -25,8 +26,10 @@ describe('GeoReplicableFilterBar', () => { ...@@ -25,8 +26,10 @@ describe('GeoReplicableFilterBar', () => {
actions: actionSpies, actions: actionSpies,
}); });
wrapper = shallowMount(GeoReplicableFilterBar, { wrapper = shallowMount(GeoReplicableFilterBar, {
localVue,
store: fakeStore, store: fakeStore,
directives: {
GlModalDirective: createMockDirective(),
},
}); });
}; };
...@@ -35,11 +38,12 @@ describe('GeoReplicableFilterBar', () => { ...@@ -35,11 +38,12 @@ describe('GeoReplicableFilterBar', () => {
}); });
const findNavContainer = () => wrapper.find('nav'); const findNavContainer = () => wrapper.find('nav');
const findGlDropdown = () => findNavContainer().find(GlDropdown); const findGlDropdown = () => findNavContainer().findComponent(GlDropdown);
const findGlDropdownItems = () => findNavContainer().findAll(GlDropdownItem); const findGlDropdownItems = () => findNavContainer().findAllComponents(GlDropdownItem);
const findDropdownItemsText = () => findGlDropdownItems().wrappers.map((w) => w.text()); const findDropdownItemsText = () => findGlDropdownItems().wrappers.map((w) => w.text());
const findGlSearchBox = () => findNavContainer().find(GlSearchBoxByType); const findGlSearchBox = () => findNavContainer().findComponent(GlSearchBoxByType);
const findGlButton = () => findNavContainer().find(GlButton); const findGlButton = () => findNavContainer().findComponent(GlButton);
const findGlModal = () => findNavContainer().findComponent(GlModal);
describe('template', () => { describe('template', () => {
beforeEach(() => { beforeEach(() => {
...@@ -47,11 +51,11 @@ describe('GeoReplicableFilterBar', () => { ...@@ -47,11 +51,11 @@ describe('GeoReplicableFilterBar', () => {
}); });
it('renders nav container always', () => { it('renders nav container always', () => {
expect(findNavContainer().exists()).toBeTruthy(); expect(findNavContainer().exists()).toBe(true);
}); });
it('renders dropdown always', () => { it('renders dropdown always', () => {
expect(findGlDropdown().exists()).toBeTruthy(); expect(findGlDropdown().exists()).toBe(true);
}); });
describe('Filter options', () => { describe('Filter options', () => {
...@@ -100,11 +104,23 @@ describe('GeoReplicableFilterBar', () => { ...@@ -100,11 +104,23 @@ describe('GeoReplicableFilterBar', () => {
describe('Re-sync all button', () => { describe('Re-sync all button', () => {
it('renders always', () => { it('renders always', () => {
expect(findGlButton().exists()).toBeTruthy(); expect(findGlButton().exists()).toBe(true);
}); });
it('calls initiateAllReplicableSyncs when clicked', () => { it('triggers GlModal', () => {
findGlButton().vm.$emit('click'); const binding = getBinding(findGlButton().element, 'gl-modal-directive');
expect(binding.value).toBe(RESYNC_MODAL_ID);
});
});
describe('GlModal', () => {
it('renders always', () => {
expect(findGlModal().exists()).toBe(true);
});
it('calls initiateAllReplicableSyncs when primary action is emitted', () => {
findGlModal().vm.$emit('primary');
expect(actionSpies.initiateAllReplicableSyncs).toHaveBeenCalled(); expect(actionSpies.initiateAllReplicableSyncs).toHaveBeenCalled();
}); });
}); });
......
...@@ -14083,9 +14083,6 @@ msgstr "" ...@@ -14083,9 +14083,6 @@ msgstr ""
msgid "Filter by name" msgid "Filter by name"
msgstr "" msgstr ""
msgid "Filter by status"
msgstr ""
msgid "Filter by test cases that are currently archived." msgid "Filter by test cases that are currently archived."
msgstr "" msgstr ""
...@@ -14725,6 +14722,9 @@ msgstr "" ...@@ -14725,6 +14722,9 @@ msgstr ""
msgid "Geo|Failed" msgid "Geo|Failed"
msgstr "" msgstr ""
msgid "Geo|Filter by name"
msgstr ""
msgid "Geo|Filter by status" msgid "Geo|Filter by status"
msgstr "" msgstr ""
...@@ -14899,6 +14899,12 @@ msgstr "" ...@@ -14899,6 +14899,12 @@ msgstr ""
msgid "Geo|Resync all" msgid "Geo|Resync all"
msgstr "" msgstr ""
msgid "Geo|Resync all %{replicableType}"
msgstr ""
msgid "Geo|Resync all projects"
msgstr ""
msgid "Geo|Retry count" msgid "Geo|Retry count"
msgstr "" msgstr ""
...@@ -14908,6 +14914,9 @@ msgstr "" ...@@ -14908,6 +14914,9 @@ msgstr ""
msgid "Geo|Reverify all" msgid "Geo|Reverify all"
msgstr "" msgstr ""
msgid "Geo|Reverify all projects"
msgstr ""
msgid "Geo|Review replication status, and resynchronize and reverify items with the primary node." msgid "Geo|Review replication status, and resynchronize and reverify items with the primary node."
msgstr "" msgstr ""
...@@ -14962,6 +14971,15 @@ msgstr "" ...@@ -14962,6 +14971,15 @@ msgstr ""
msgid "Geo|There was an error fetching the Geo Nodes" msgid "Geo|There was an error fetching the Geo Nodes"
msgstr "" msgstr ""
msgid "Geo|This will resync all %{replicableType}. It may take some time to complete. Are you sure you want to continue?"
msgstr ""
msgid "Geo|This will resync all projects. It may take some time to complete. Are you sure you want to continue?"
msgstr ""
msgid "Geo|This will reverify all projects. It may take some time to complete. Are you sure you want to continue?"
msgstr ""
msgid "Geo|Tracking database entry will be removed. Are you sure?" msgid "Geo|Tracking database entry will be removed. Are you sure?"
msgstr "" msgstr ""
...@@ -28140,12 +28158,6 @@ msgstr "" ...@@ -28140,12 +28158,6 @@ msgstr ""
msgid "Resync" msgid "Resync"
msgstr "" msgstr ""
msgid "Resync all"
msgstr ""
msgid "Resync all %{replicableType}"
msgstr ""
msgid "Retry" msgid "Retry"
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