Commit 2b880c09 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '208470_01-package-file-hookup' into 'master'

Geo Package Files - Initialize

See merge request gitlab-org/gitlab!33425
parents 759fd23e 3efae6f3
<script> <script>
import { mapState } from 'vuex'; import { mapGetters } from 'vuex';
import { GlEmptyState } from '@gitlab/ui'; import { GlEmptyState } from '@gitlab/ui';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
...@@ -19,7 +19,7 @@ export default { ...@@ -19,7 +19,7 @@ export default {
}, },
}, },
computed: { computed: {
...mapState(['replicableType']), ...mapGetters(['replicableTypeName']),
linkText() { linkText() {
return sprintf( return sprintf(
s__( s__(
...@@ -38,7 +38,7 @@ export default { ...@@ -38,7 +38,7 @@ export default {
<template> <template>
<gl-empty-state <gl-empty-state
:title="sprintf(__('There are no %{replicableType} to show'), { replicableType })" :title="sprintf(__('There are no %{replicableTypeName} to show'), { replicableTypeName })"
:svg-path="geoReplicableEmptySvgPath" :svg-path="geoReplicableEmptySvgPath"
> >
<template #description> <template #description>
......
<script> <script>
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { GlSearchBoxByType, GlDropdown, GlDropdownItem, GlButton } from '@gitlab/ui'; import { GlSearchBoxByType, GlDropdown, GlDropdownItem, GlButton } from '@gitlab/ui';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { DEFAULT_SEARCH_DELAY, ACTION_TYPES } from '../store/constants'; import { DEFAULT_SEARCH_DELAY, ACTION_TYPES, FILTER_STATES } from '../store/constants';
export default { export default {
name: 'GeoReplicableFilterBar', name: 'GeoReplicableFilterBar',
...@@ -14,7 +14,8 @@ export default { ...@@ -14,7 +14,8 @@ export default {
GlButton, GlButton,
}, },
computed: { computed: {
...mapState(['currentFilterIndex', 'filterOptions', 'searchFilter', 'replicableType']), ...mapState(['currentFilterIndex', 'filterOptions', 'searchFilter']),
...mapGetters(['replicableTypeName']),
search: { search: {
get() { get() {
return this.searchFilter; return this.searchFilter;
...@@ -25,7 +26,9 @@ export default { ...@@ -25,7 +26,9 @@ export default {
}, DEFAULT_SEARCH_DELAY), }, DEFAULT_SEARCH_DELAY),
}, },
resyncText() { resyncText() {
return sprintf(__('Resync all %{replicableType}'), { replicableType: this.replicableType }); return sprintf(__('Resync all %{replicableType}'), {
replicableType: this.replicableTypeName,
});
}, },
}, },
methods: { methods: {
...@@ -36,6 +39,7 @@ export default { ...@@ -36,6 +39,7 @@ export default {
}, },
}, },
actionTypes: ACTION_TYPES, actionTypes: ACTION_TYPES,
filterStates: FILTER_STATES,
}; };
</script> </script>
...@@ -51,10 +55,10 @@ export default { ...@@ -51,10 +55,10 @@ export default {
:class="{ 'bg-secondary-100': index === currentFilterIndex }" :class="{ 'bg-secondary-100': index === currentFilterIndex }"
@click="filterChange(index)" @click="filterChange(index)"
> >
<span <span v-if="filter === $options.filterStates.ALL"
>{{ filter.label }} >{{ filter.label }} {{ replicableTypeName }}</span
<span v-if="filter.label === 'All'">{{ replicableType }}</span></span
> >
<span v-else>{{ filter.label }}</span>
</gl-dropdown-item> </gl-dropdown-item>
</gl-dropdown> </gl-dropdown>
<gl-search-box-by-type <gl-search-box-by-type
......
// eslint-disable-next-line import/prefer-default-export
export const replicableTypeName = state => state.replicableType.split('_').join(' ');
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import * as actions from './actions'; import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations'; import mutations from './mutations';
import createState from './state'; import createState from './state';
...@@ -9,6 +10,7 @@ Vue.use(Vuex); ...@@ -9,6 +10,7 @@ Vue.use(Vuex);
const createStore = replicableType => const createStore = replicableType =>
new Vuex.Store({ new Vuex.Store({
actions, actions,
getters,
mutations, mutations,
state: createState(replicableType), state: createState(replicableType),
}); });
......
import initGeoReplicable from 'ee/geo_replicable';
if (gon?.features?.geoSelfServiceFramework) {
document.addEventListener('DOMContentLoaded', initGeoReplicable);
}
# frozen_string_literal: true
class Admin::Geo::PackageFilesController < Admin::Geo::ApplicationController
before_action :check_license!
before_action do
push_frontend_feature_flag(:geo_self_service_framework)
end
def index
end
end
= render "admin/geo/shared/replication_nav"
- if Feature.enabled?(:geo_self_service_framework)
#js-geo-replicable{ data: { "geo-replicable-empty-svg-path" => image_path("illustrations/empty-state/geo-replication-empty.svg"),
"geo-troubleshooting-link" => help_page_path("administration/geo/replication/troubleshooting.html"),
"replicable-type" => "package_files",
"graphql" => "true" } }
...@@ -6,16 +6,21 @@ ...@@ -6,16 +6,21 @@
%p %p
= s_('Geo|Review replication status, and resynchronize and reverify items with the primary node.') = s_('Geo|Review replication status, and resynchronize and reverify items with the primary node.')
%ul.nav-links.nav.nav-tabs.border-top.border-bottom.border-secondary-100 %ul.nav-links.nav.nav-tabs.border-top.border-bottom.border-secondary-100
= nav_link(path: 'admin/geo/projects#index', html_options: { class: 'pr-2' }) do = nav_link(path: 'admin/geo/projects#index', html_options: { class: 'gl-pr-2' }) do
= link_to admin_geo_projects_path, title: _('Projects') do = link_to admin_geo_projects_path, title: _('Projects') do
%span %span
= _('Projects') = _('Projects')
= nav_link(path: 'admin/geo/uploads#index', html_options: { class: 'pr-2' }) do = nav_link(path: 'admin/geo/uploads#index', html_options: { class: 'gl-pr-2' }) do
= link_to admin_geo_uploads_path, title: _('Uploads') do = link_to admin_geo_uploads_path, title: _('Uploads') do
%span %span
= _('Uploads') = _('Uploads')
= nav_link(path: 'admin/geo/designs#index', html_options: { class: 'pr-2' }) do = nav_link(path: 'admin/geo/designs#index', html_options: { class: 'gl-pr-2' }) do
= link_to admin_geo_designs_path, title: _('Designs') do = link_to admin_geo_designs_path, title: _('Designs') do
%span %span
= _('Designs') = _('Designs')
- if Feature.enabled?(:geo_self_service_framework)
= nav_link(path: 'admin/geo/package_files#index', html_options: { class: 'gl-pr-2' }) do
= link_to admin_geo_package_files_path, title: _('Package Files') do
%span
= _('Package Files')
= nav_link(controller: %w(admin/geo/nodes admin/geo/projects admin/geo/uploads admin/geo/settings admin/geo/designs)) do = nav_link(controller: %w(admin/geo/nodes admin/geo/projects admin/geo/uploads admin/geo/settings admin/geo/designs admin/geo/package_files)) do
= link_to admin_geo_nodes_path, class: "qa-link-geo-menu" do = link_to admin_geo_nodes_path, class: "qa-link-geo-menu" do
.nav-icon-container .nav-icon-container
= sprite_icon('location-dot') = sprite_icon('location-dot')
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
%span %span
= _('Nodes') = _('Nodes')
- if Gitlab::Geo.secondary? - if Gitlab::Geo.secondary?
= nav_link(controller: %w(admin/geo/projects admin/geo/uploads admin/geo/designs)) do = nav_link(controller: %w(admin/geo/projects admin/geo/uploads admin/geo/designs admin/geo/package_files)) do
= link_to admin_geo_projects_path, title: _('Replication') do = link_to admin_geo_projects_path, title: _('Replication') do
%span %span
= _('Replication') = _('Replication')
......
...@@ -60,6 +60,7 @@ namespace :admin do ...@@ -60,6 +60,7 @@ namespace :admin do
end end
resources :designs, only: [:index] resources :designs, only: [:index]
resources :package_files, only: [:index]
resources :uploads, only: [:index, :destroy] resources :uploads, only: [:index, :destroy]
end end
......
...@@ -43,4 +43,23 @@ describe 'admin Geo Replication Nav', :js, :geo do ...@@ -43,4 +43,23 @@ describe 'admin Geo Replication Nav', :js, :geo do
let(:path) { admin_geo_designs_path } let(:path) { admin_geo_designs_path }
end end
end end
describe 'visit admin/geo/replication/package_files' do
it_behaves_like 'active sidebar link', 'Package Files' do
let(:path) { admin_geo_package_files_path }
end
context 'when geo_self_service_framework feature is disabled' do
before do
stub_feature_flags(geo_self_service_framework: false)
visit admin_geo_projects_path
wait_for_requests
end
it 'does not render navigational element' do
expect(page).not_to have_selector("a[title=\"Package Files\"]")
end
end
end
end end
...@@ -63,5 +63,11 @@ describe 'admin Geo Sidebar', :js, :geo do ...@@ -63,5 +63,11 @@ describe 'admin Geo Sidebar', :js, :geo do
let(:path) { admin_geo_uploads_path } let(:path) { admin_geo_uploads_path }
end end
end end
describe 'visiting geo package files' do
it_behaves_like 'active sidebar link', 'Replication' do
let(:path) { admin_geo_package_files_path }
end
end
end end
end end
import Vuex from 'vuex'; import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlEmptyState } from '@gitlab/ui'; import { GlEmptyState } from '@gitlab/ui';
import store from 'ee/geo_replicable/store'; import createStore from 'ee/geo_replicable/store';
import GeoReplicableEmptyState from 'ee/geo_replicable/components/geo_replicable_empty_state.vue'; import GeoReplicableEmptyState from 'ee/geo_replicable/components/geo_replicable_empty_state.vue';
import { MOCK_GEO_REPLICATION_SVG_PATH, MOCK_GEO_TROUBLESHOOTING_LINK } from '../mock_data'; import {
MOCK_GEO_REPLICATION_SVG_PATH,
MOCK_GEO_TROUBLESHOOTING_LINK,
MOCK_REPLICABLE_TYPE,
} from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -19,7 +23,7 @@ describe('GeoReplicableEmptyState', () => { ...@@ -19,7 +23,7 @@ describe('GeoReplicableEmptyState', () => {
const createComponent = () => { const createComponent = () => {
wrapper = shallowMount(GeoReplicableEmptyState, { wrapper = shallowMount(GeoReplicableEmptyState, {
localVue, localVue,
store, store: createStore(MOCK_REPLICABLE_TYPE),
propsData, propsData,
}); });
}; };
......
...@@ -2,8 +2,9 @@ import Vuex from 'vuex'; ...@@ -2,8 +2,9 @@ import Vuex from 'vuex';
import { createLocalVue, mount } from '@vue/test-utils'; import { createLocalVue, mount } from '@vue/test-utils';
import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlButton } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlButton } from '@gitlab/ui';
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 store from 'ee/geo_replicable/store'; import createStore from 'ee/geo_replicable/store';
import { DEFAULT_SEARCH_DELAY } from 'ee/geo_replicable/store/constants'; import { DEFAULT_SEARCH_DELAY } from 'ee/geo_replicable/store/constants';
import { MOCK_REPLICABLE_TYPE } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -21,7 +22,7 @@ describe('GeoReplicableFilterBar', () => { ...@@ -21,7 +22,7 @@ describe('GeoReplicableFilterBar', () => {
const createComponent = () => { const createComponent = () => {
wrapper = mount(GeoReplicableFilterBar, { wrapper = mount(GeoReplicableFilterBar, {
localVue, localVue,
store, store: createStore(MOCK_REPLICABLE_TYPE),
methods: { methods: {
...actionSpies, ...actionSpies,
}, },
...@@ -54,7 +55,15 @@ describe('GeoReplicableFilterBar', () => { ...@@ -54,7 +55,15 @@ describe('GeoReplicableFilterBar', () => {
describe('Filter options', () => { describe('Filter options', () => {
it('renders a dropdown item for each filterOption', () => { it('renders a dropdown item for each filterOption', () => {
expect(findDropdownItemsText()).toStrictEqual(wrapper.vm.filterOptions.map(n => n.label)); expect(findDropdownItemsText()).toStrictEqual(
wrapper.vm.filterOptions.map(n => {
if (n.label === 'All') {
return `${n.label} ${MOCK_REPLICABLE_TYPE}`;
}
return n.label;
}),
);
}); });
it('clicking a dropdown item calls setFilter with its index', () => { it('clicking a dropdown item calls setFilter with its index', () => {
......
import * as getters from 'ee/geo_replicable/store/getters';
import createState from 'ee/geo_replicable/store/state';
describe('GeoReplicable Store Getters', () => {
let state;
beforeEach(() => {
state = createState();
});
describe('replicableTypeName', () => {
it('handles a single word replicable type', () => {
state.replicableType = 'designs';
expect(getters.replicableTypeName(state)).toBe('designs');
});
it('handles a multi-word replicable type', () => {
state.replicableType = 'package_files';
expect(getters.replicableTypeName(state)).toBe('package files');
});
});
});
...@@ -44,6 +44,12 @@ RSpec.describe 'EE-specific admin routing' do ...@@ -44,6 +44,12 @@ RSpec.describe 'EE-specific admin routing' do
end end
end end
describe Admin::Geo::PackageFilesController, 'routing' do
it 'routes / to #index' do
expect(get('/admin/geo/replication/package_files')).to route_to('admin/geo/package_files#index')
end
end
describe Admin::Geo::NodesController, 'routing' do describe Admin::Geo::NodesController, 'routing' do
let(:geo_node) { create(:geo_node) } let(:geo_node) { create(:geo_node) }
......
...@@ -15332,6 +15332,9 @@ msgstr "" ...@@ -15332,6 +15332,9 @@ msgstr ""
msgid "Owner" msgid "Owner"
msgstr "" msgstr ""
msgid "Package Files"
msgstr ""
msgid "Package Registry" msgid "Package Registry"
msgstr "" msgstr ""
...@@ -22080,7 +22083,7 @@ msgstr "" ...@@ -22080,7 +22083,7 @@ msgstr ""
msgid "The vulnerability is no longer detected. Verify the vulnerability has been remediated before changing its status." msgid "The vulnerability is no longer detected. Verify the vulnerability has been remediated before changing its status."
msgstr "" msgstr ""
msgid "There are no %{replicableType} to show" msgid "There are no %{replicableTypeName} to show"
msgstr "" msgstr ""
msgid "There are no GPG keys associated with this account." msgid "There are no GPG keys associated with this account."
......
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