Commit 361f86b0 authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '213219_05-merge-geo-replicables' into 'master'

Merge Geo Replication Views

Closes #213219

See merge request gitlab-org/gitlab!30890
parents ec375a2d 371d30ea
...@@ -40,33 +40,36 @@ export default { ...@@ -40,33 +40,36 @@ export default {
</script> </script>
<template> <template>
<nav <nav class="bg-secondary border-bottom border-secondary-100 p-3">
class="row d-flex flex-column flex-sm-row align-items-center bg-secondary border-bottom border-secondary-100 p-3" <div class="row d-flex flex-column flex-sm-row">
> <div class="col">
<gl-dropdown :text="__('Filter by status')" class="col px-1 my-1 my-sm-0 w-100"> <div class="d-sm-flex mx-n1">
<gl-dropdown-item <gl-dropdown :text="__('Filter by status')" class="px-1 my-1 my-sm-0 w-100">
v-for="(filter, index) in filterOptions" <gl-dropdown-item
:key="index" v-for="(filter, index) in filterOptions"
:class="{ 'bg-secondary-100': index === currentFilterIndex }" :key="index"
@click="filterChange(index)" :class="{ 'bg-secondary-100': index === currentFilterIndex }"
> @click="filterChange(index)"
<span >
>{{ filter.label }} <span v-if="filter.label === 'All'">{{ replicableType }}</span></span <span
> >{{ filter.label }}
</gl-dropdown-item> <span v-if="filter.label === 'All'">{{ replicableType }}</span></span
</gl-dropdown> >
<gl-search-box-by-type </gl-dropdown-item>
v-model="search" </gl-dropdown>
class="col px-1 my-1 my-sm-0 bg-white w-100" <gl-search-box-by-type
type="text" v-model="search"
:placeholder="__(`Filter by name`)" class="px-1 my-1 my-sm-0 bg-white w-100"
/> type="text"
<div class="col col-sm-6 d-flex justify-content-end my-1 my-sm-0 w-100"> :placeholder="__('Filter by name')"
<gl-button />
class="text-secondary-700" </div>
@click="initiateAllReplicableSyncs($options.actionTypes.RESYNC)" </div>
>{{ __('Resync all') }}</gl-button <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)">{{
__('Resync all')
}}</gl-button>
</div>
</div> </div>
</nav> </nav>
</template> </template>
<script> <script>
import { mapActions } from 'vuex'; import { mapActions } from 'vuex';
import { GlLink, GlDeprecatedButton } from '@gitlab/ui'; import { GlLink, GlButton } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { ACTION_TYPES } from '../store/constants'; import { ACTION_TYPES } from '../store/constants';
import GeoReplicableStatus from './geo_replicable_status.vue'; import GeoReplicableStatus from './geo_replicable_status.vue';
...@@ -10,7 +10,7 @@ export default { ...@@ -10,7 +10,7 @@ export default {
name: 'GeoReplicableItem', name: 'GeoReplicableItem',
components: { components: {
GlLink, GlLink,
GlDeprecatedButton, GlButton,
GeoReplicableTimeAgo, GeoReplicableTimeAgo,
GeoReplicableStatus, GeoReplicableStatus,
}, },
...@@ -77,9 +77,10 @@ export default { ...@@ -77,9 +77,10 @@ export default {
<div class="card-header d-flex align-center"> <div class="card-header d-flex align-center">
<gl-link class="font-weight-bold" :href="`/${name}`" target="_blank">{{ name }}</gl-link> <gl-link class="font-weight-bold" :href="`/${name}`" target="_blank">{{ name }}</gl-link>
<div class="ml-auto"> <div class="ml-auto">
<gl-deprecated-button <gl-button
size="small"
@click="initiateReplicableSync({ projectId, name, action: $options.actionTypes.RESYNC })" @click="initiateReplicableSync({ projectId, name, action: $options.actionTypes.RESYNC })"
>{{ __('Resync') }}</gl-deprecated-button >{{ __('Resync') }}</gl-button
> >
</div> </div>
</div> </div>
......
...@@ -157,11 +157,11 @@ module EE ...@@ -157,11 +157,11 @@ 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 mr-2") button_to(s_("Geo|Resync all"), { controller: controller_name, action: :resync_all }, class: "btn btn-default btn-md mr-2")
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") button_to(s_("Geo|Reverify all"), { controller: controller_name, action: :reverify_all }, class: "btn btn-default btn-md")
end end
end end
end end
- page_title _('Geo Designs') = render 'admin/geo/shared/replication_nav'
#js-geo-replicable{ data: { "geo-replicable-empty-svg-path" => image_path('illustrations/empty-state/geo-replication-empty.svg'), #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'), "geo-troubleshooting-link" => help_page_path('administration/geo/replication/troubleshooting.html'),
"replicable-type" => 'designs' } } "replicable-type" => 'designs' } }
- page_title 'Geo Projects' = render 'admin/geo/shared/replication_nav'
- params[:sync_status] ||= []
= render 'admin/geo/shared/filter_nav', replicable_name: _('projects'), replicable_controller: 'admin/geo/projects', action_buttons: @action_buttons = render 'admin/geo/shared/filter_nav', replicable_name: _('projects'), replicable_controller: 'admin/geo/projects', action_buttons: @action_buttons
- case params[:sync_status] - case params[:sync_status]
- when 'failed' - when 'failed'
......
- action_buttons = local_assigns[:action_buttons] ? action_buttons : [] - action_buttons = local_assigns[:action_buttons] ? action_buttons : []
- params[:sync_status] ||= []
%nav.row.d-flex.flex-column.flex-sm-row.align-items-center.bg-secondary.border-bottom.border-secondary-100.p-3 %nav.bg-secondary.border-bottom.border-secondary-100.p-3
.dropdown.col.px-1.my-1.my-sm-0.w-100 .row.d-flex.flex-column.flex-sm-row
%a.btn.d-flex.align-items-center.justify-content-between.w-100{ href: '#', data: { toggle: 'dropdown' }, 'aria-haspopup' => 'true', 'aria-expanded' => 'false' } .col
= s_('Geo|Filter by status') .d-sm-flex.mx-n1
= sprite_icon("chevron-down", size: 16) .dropdown.px-1.my-1.my-sm-0.w-100
%ul.dropdown-menu %a.btn.d-flex.align-items-center.justify-content-between.w-100{ href: '#', data: { toggle: 'dropdown' }, 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
= nav_link(html_options: { class: ('bg-secondary-100' if !params[:sync_status].present?) }) do = s_('Geo|Filter by status')
= link_to controller: replicable_controller do = sprite_icon("chevron-down", size: 16)
= sprintf(s_('Geo|All %{replicable_name}'), { replicable_name: replicable_name }) %ul.dropdown-menu
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'pending') }) do = nav_link(html_options: { class: ('bg-secondary-100' if !params[:sync_status].present?) }) do
= link_to controller: replicable_controller, sync_status: 'pending' do = link_to controller: replicable_controller do
= s_('Geo|In progress') = sprintf(s_('Geo|All %{replicable_name}'), { replicable_name: replicable_name })
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'failed') }) do = nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'pending') }) do
= link_to controller: replicable_controller, sync_status: 'failed' do = link_to controller: replicable_controller, sync_status: 'pending' do
= s_('Geo|Failed') = s_('Geo|In progress')
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'synced') }) do = nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'failed') }) do
= link_to controller: replicable_controller, sync_status: 'synced' do = link_to controller: replicable_controller, sync_status: 'failed' do
= s_('Geo|Synced') = s_('Geo|Failed')
.col.replicable-search.px-1.my-1.my-sm-0.w-100 = nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'synced') }) do
= render 'shared/projects/search_form', autofocus: true, search_form_placeholder: _("Filter by name"), icon: true = link_to controller: replicable_controller, sync_status: 'synced' do
.col.col-sm-6.d-flex.justify-content-end.my-1.my-sm-0.w-100 = s_('Geo|Synced')
- action_buttons.each do |action_button| .replicable-search.px-1.my-1.my-sm-0.w-100
= action_button = render 'shared/projects/search_form', autofocus: true, search_form_placeholder: _("Filter by name"), icon: true
.col.col-sm-5.d-flex.justify-content-end.my-1.my-sm-0.w-100
- action_buttons.each do |action_button|
= action_button
- page_title _('Geo Replication')
%header.py-2
%h2.page-title
= _('Geo Replication')
%p
= 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
= nav_link(path: 'admin/geo/projects#index', html_options: { class: 'pr-2' }) do
= link_to admin_geo_projects_path, title: _('Projects') do
%span
= _('Projects')
= nav_link(path: 'admin/geo/uploads#index', html_options: { class: 'pr-2' }) do
= link_to admin_geo_uploads_path, title: _('Uploads') do
%span
= _('Uploads')
= nav_link(path: 'admin/geo/designs#index', html_options: { class: 'pr-2' }) do
= link_to admin_geo_designs_path, title: _('Designs') do
%span
= _('Designs')
- page_title 'Geo Uploads' = render 'admin/geo/shared/replication_nav'
- params[:sync_status] ||= []
= render 'admin/geo/shared/filter_nav', replicable_name: _('uploads'), replicable_controller: 'admin/geo/uploads' = render 'admin/geo/shared/filter_nav', replicable_name: _('uploads'), replicable_controller: 'admin/geo/uploads'
- if @registries.any? - if @registries.any?
- @registries.each do |upload_registry| - @registries.each do |upload_registry|
......
...@@ -10,23 +10,15 @@ ...@@ -10,23 +10,15 @@
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
#{ _('Geo') } #{ _('Geo') }
= nav_link(path: 'admin/geo/nodes#index') do = nav_link(path: 'admin/geo/nodes#index') do
= link_to admin_geo_nodes_path, title: 'Nodes' do = link_to admin_geo_nodes_path, title: _('Nodes') do
%span %span
= _('Nodes') = _('Nodes')
- if Gitlab::Geo.secondary?
= nav_link(controller: %w(admin/geo/projects admin/geo/uploads admin/geo/designs)) do
= link_to admin_geo_projects_path, title: _('Replication') do
%span
= _('Replication')
= nav_link(path: 'admin/geo/settings#show') do = nav_link(path: 'admin/geo/settings#show') do
= link_to admin_geo_settings_path, title: 'Settings' do = link_to admin_geo_settings_path, title: _('Settings') do
%span %span
= _('Settings') = _('Settings')
- if Gitlab::Geo.secondary?
= nav_link(path: 'admin/geo/projects#index') do
= link_to admin_geo_projects_path, title: 'Projects' do
%span
= _('Projects')
= nav_link(path: 'admin/geo/designs#index') do
= link_to admin_geo_designs_path, title: _('Designs') do
%span
= _('Designs')
= nav_link(path: 'admin/geo/uploads#index') do
= link_to admin_geo_uploads_path, title: 'Uploads' do
%span
= _('Uploads')
---
title: Geo Replication View
merge_request: 30890
author:
type: added
...@@ -36,26 +36,35 @@ namespace :admin do ...@@ -36,26 +36,35 @@ namespace :admin do
namespace :geo do namespace :geo do
get '/' => 'nodes#index' get '/' => 'nodes#index'
# Old Routes Replaced in 13.0
get '/projects', to: redirect(path: 'admin/geo/replication/projects')
get '/uploads', to: redirect(path: 'admin/geo/replication/uploads')
get '/designs', to: redirect(path: 'admin/geo/replication/designs')
resources :nodes, only: [:index, :create, :new, :edit, :update] resources :nodes, only: [:index, :create, :new, :edit, :update]
resources :projects, only: [:index, :destroy] do scope '/replication' do
member do get '/', to: redirect(path: 'admin/geo/replication/projects')
post :reverify
post :resync
post :force_redownload
end
collection do resources :projects, only: [:index, :destroy] do
post :reverify_all member do
post :resync_all post :reverify
post :resync
post :force_redownload
end
collection do
post :reverify_all
post :resync_all
end
end end
end
resource :settings, only: [:show, :update] resources :designs, only: [:index]
resources :designs, only: [:index] resources :uploads, only: [:index, :destroy]
end
resources :uploads, only: [:index, :destroy] resource :settings, only: [:show, :update]
end end
namespace :elasticsearch do namespace :elasticsearch do
......
# frozen_string_literal: true
require 'spec_helper'
describe 'admin Geo Replication Nav', :js, :geo do
include ::EE::GeoHelpers
include StubENV
let_it_be(:admin) { create(:admin) }
before do
stub_licensed_features(geo: true)
sign_in(admin)
stub_secondary_node
end
shared_examples 'active sidebar link' do |link_name|
before do
visit path
wait_for_requests
end
it 'has active class' do
navigation_link = page.find("a[title=\"#{link_name}\"]").find(:xpath, '..')
expect(navigation_link[:class]).to include('active')
end
end
describe 'visit admin/geo/replication/projects' do
it_behaves_like 'active sidebar link', 'Projects' do
let(:path) { admin_geo_projects_path }
end
end
describe 'visit admin/geo/replication/uploads' do
it_behaves_like 'active sidebar link', 'Uploads' do
let(:path) { admin_geo_uploads_path }
end
end
describe 'visit admin/geo/replication/designs' do
it_behaves_like 'active sidebar link', 'Designs' do
let(:path) { admin_geo_designs_path }
end
end
end
...@@ -25,13 +25,13 @@ describe 'admin Geo Sidebar', :js, :geo do ...@@ -25,13 +25,13 @@ describe 'admin Geo Sidebar', :js, :geo do
end end
end end
describe 'clicking the nodes link' do describe 'visiting geo nodes' do
it_behaves_like 'active sidebar link', 'Nodes' do it_behaves_like 'active sidebar link', 'Nodes' do
let(:path) { admin_geo_nodes_path } let(:path) { admin_geo_nodes_path }
end end
end end
describe 'clicking the settings link' do describe 'visiting geo settings' do
before do before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end end
...@@ -46,20 +46,20 @@ describe 'admin Geo Sidebar', :js, :geo do ...@@ -46,20 +46,20 @@ describe 'admin Geo Sidebar', :js, :geo do
stub_secondary_node stub_secondary_node
end end
describe 'clicking the projects link' do describe 'visiting geo projects' do
it_behaves_like 'active sidebar link', 'Projects' do it_behaves_like 'active sidebar link', 'Replication' do
let(:path) { admin_geo_projects_path } let(:path) { admin_geo_projects_path }
end end
end end
describe 'clicking the designs link' do describe 'visiting geo designs' do
it_behaves_like 'active sidebar link', 'Designs' do it_behaves_like 'active sidebar link', 'Replication' do
let(:path) { admin_geo_designs_path } let(:path) { admin_geo_designs_path }
end end
end end
describe 'clicking the uploads link' do describe 'visiting geo uploads' do
it_behaves_like 'active sidebar link', 'Uploads' do it_behaves_like 'active sidebar link', 'Replication' do
let(:path) { admin_geo_uploads_path } let(:path) { admin_geo_uploads_path }
end end
end end
......
import Vuex from 'vuex'; import Vuex from 'vuex';
import { createLocalVue, mount } from '@vue/test-utils'; import { createLocalVue, mount } from '@vue/test-utils';
import { GlLink, GlDeprecatedButton } from '@gitlab/ui'; import { GlLink, GlButton } from '@gitlab/ui';
import GeoReplicableItem from 'ee/geo_replicable/components/geo_replicable_item.vue'; import GeoReplicableItem from 'ee/geo_replicable/components/geo_replicable_item.vue';
import store from 'ee/geo_replicable/store'; import store from 'ee/geo_replicable/store';
import { ACTION_TYPES } from 'ee/geo_replicable/store/constants'; import { ACTION_TYPES } from 'ee/geo_replicable/store/constants';
...@@ -43,7 +43,7 @@ describe('GeoReplicableItem', () => { ...@@ -43,7 +43,7 @@ describe('GeoReplicableItem', () => {
const findCard = () => wrapper.find('.card'); const findCard = () => wrapper.find('.card');
const findGlLink = () => findCard().find(GlLink); const findGlLink = () => findCard().find(GlLink);
const findGlButton = () => findCard().find(GlDeprecatedButton); const findGlButton = () => findCard().find(GlButton);
const findCardHeader = () => findCard().find('.card-header'); const findCardHeader = () => findCard().find('.card-header');
const findCardBody = () => findCard().find('.card-body'); const findCardBody = () => findCard().find('.card-body');
......
...@@ -55,19 +55,19 @@ describe Gitlab::Middleware::ReadOnly do ...@@ -55,19 +55,19 @@ describe Gitlab::Middleware::ReadOnly do
context 'whitelisted requests' do context 'whitelisted requests' do
it_behaves_like 'whitelisted request', :patch, '/admin/geo/nodes/1' it_behaves_like 'whitelisted request', :patch, '/admin/geo/nodes/1'
it_behaves_like 'whitelisted request', :delete, '/admin/geo/projects/1' it_behaves_like 'whitelisted request', :delete, '/admin/geo/replication/projects/1'
it_behaves_like 'whitelisted request', :post, '/admin/geo/projects/1/resync' it_behaves_like 'whitelisted request', :post, '/admin/geo/replication/projects/1/resync'
it_behaves_like 'whitelisted request', :post, '/admin/geo/projects/1/reverify' it_behaves_like 'whitelisted request', :post, '/admin/geo/replication/projects/1/reverify'
it_behaves_like 'whitelisted request', :post, '/admin/geo/projects/reverify_all' it_behaves_like 'whitelisted request', :post, '/admin/geo/replication/projects/reverify_all'
it_behaves_like 'whitelisted request', :post, '/admin/geo/projects/resync_all' it_behaves_like 'whitelisted request', :post, '/admin/geo/replication/projects/resync_all'
it_behaves_like 'whitelisted request', :post, '/admin/geo/projects/1/force_redownload' it_behaves_like 'whitelisted request', :post, '/admin/geo/replication/projects/1/force_redownload'
it_behaves_like 'whitelisted request', :delete, '/admin/geo/uploads/1' it_behaves_like 'whitelisted request', :delete, '/admin/geo/replication/uploads/1'
end end
it 'expects geo replication node api requests to be allowed' do it 'expects geo replication node api requests to be allowed' do
......
...@@ -6,23 +6,41 @@ describe 'EE-specific admin routing' do ...@@ -6,23 +6,41 @@ describe 'EE-specific admin routing' do
let(:project_registry) { create(:geo_project_registry) } let(:project_registry) { create(:geo_project_registry) }
it 'routes / to #index' do it 'routes / to #index' do
expect(get('/admin/geo/projects')).to route_to('admin/geo/projects#index') expect(get('/admin/geo/replication/projects')).to route_to('admin/geo/projects#index')
end end
it 'routes delete /:id to #destroy' do it 'routes delete /:id to #destroy' do
expect(delete("/admin/geo/projects/#{project_registry.id}")).to route_to('admin/geo/projects#destroy', id: project_registry.to_param) expect(delete("/admin/geo/replication/projects/#{project_registry.id}")).to route_to('admin/geo/projects#destroy', id: project_registry.to_param)
end end
it 'routes post /:id/reverify to #reverify' do it 'routes post /:id/reverify to #reverify' do
expect(post("admin/geo/projects/#{project_registry.id}/reverify")).to route_to('admin/geo/projects#reverify', id: project_registry.to_param) expect(post("/admin/geo/replication/projects/#{project_registry.id}/reverify")).to route_to('admin/geo/projects#reverify', id: project_registry.to_param)
end end
it 'routes post /:id/resync to #resync' do it 'routes post /:id/resync to #resync' do
expect(post("admin/geo/projects/#{project_registry.id}/resync")).to route_to('admin/geo/projects#resync', id: project_registry.to_param) expect(post("/admin/geo/replication/projects/#{project_registry.id}/resync")).to route_to('admin/geo/projects#resync', id: project_registry.to_param)
end end
it 'routes post /:id/force_redownload to #force_redownload' do it 'routes post /:id/force_redownload to #force_redownload' do
expect(post("admin/geo/projects/#{project_registry.id}/force_redownload")).to route_to('admin/geo/projects#force_redownload', id: project_registry.to_param) expect(post("/admin/geo/replication/projects/#{project_registry.id}/force_redownload")).to route_to('admin/geo/projects#force_redownload', id: project_registry.to_param)
end
end
describe Admin::Geo::UploadsController, 'routing' do
let!(:upload_registry) { create(:geo_upload_registry, :with_file, :attachment, success: true) }
it 'routes / to #index' do
expect(get('/admin/geo/replication/uploads')).to route_to('admin/geo/uploads#index')
end
it 'routes delete /:id to #destroy' do
expect(delete("/admin/geo/replication/uploads/#{upload_registry.id}")).to route_to('admin/geo/uploads#destroy', id: upload_registry.to_param)
end
end
describe Admin::Geo::DesignsController, 'routing' do
it 'routes / to #index' do
expect(get('/admin/geo/replication/designs')).to route_to('admin/geo/designs#index')
end end
end end
......
...@@ -9722,15 +9722,15 @@ msgstr "" ...@@ -9722,15 +9722,15 @@ msgstr ""
msgid "Geo" msgid "Geo"
msgstr "" msgstr ""
msgid "Geo Designs"
msgstr ""
msgid "Geo Nodes" msgid "Geo Nodes"
msgstr "" msgstr ""
msgid "Geo Nodes|Cannot remove a primary node if there is a secondary node" msgid "Geo Nodes|Cannot remove a primary node if there is a secondary node"
msgstr "" msgstr ""
msgid "Geo Replication"
msgstr ""
msgid "Geo Settings" msgid "Geo Settings"
msgstr "" msgstr ""
...@@ -10022,6 +10022,9 @@ msgstr "" ...@@ -10022,6 +10022,9 @@ msgstr ""
msgid "Geo|Reverify all" msgid "Geo|Reverify all"
msgstr "" msgstr ""
msgid "Geo|Review replication status, and resynchronize and reverify items with the primary node."
msgstr ""
msgid "Geo|Status" msgid "Geo|Status"
msgstr "" msgstr ""
...@@ -17708,6 +17711,9 @@ msgstr "" ...@@ -17708,6 +17711,9 @@ msgstr ""
msgid "Replaces the clone URL root." msgid "Replaces the clone URL root."
msgstr "" msgstr ""
msgid "Replication"
msgstr ""
msgid "Reply by email" msgid "Reply by email"
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