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 {
</script>
<template>
<nav
class="row d-flex flex-column flex-sm-row align-items-center bg-secondary border-bottom border-secondary-100 p-3"
>
<gl-dropdown :text="__('Filter by status')" class="col px-1 my-1 my-sm-0 w-100">
<gl-dropdown-item
v-for="(filter, index) in filterOptions"
:key="index"
:class="{ 'bg-secondary-100': index === currentFilterIndex }"
@click="filterChange(index)"
>
<span
>{{ filter.label }} <span v-if="filter.label === 'All'">{{ replicableType }}</span></span
>
</gl-dropdown-item>
</gl-dropdown>
<gl-search-box-by-type
v-model="search"
class="col px-1 my-1 my-sm-0 bg-white w-100"
type="text"
:placeholder="__(`Filter by name`)"
/>
<div class="col col-sm-6 d-flex justify-content-end my-1 my-sm-0 w-100">
<gl-button
class="text-secondary-700"
@click="initiateAllReplicableSyncs($options.actionTypes.RESYNC)"
>{{ __('Resync all') }}</gl-button
>
<nav class="bg-secondary border-bottom border-secondary-100 p-3">
<div class="row d-flex flex-column flex-sm-row">
<div class="col">
<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-item
v-for="(filter, index) in filterOptions"
:key="index"
:class="{ 'bg-secondary-100': index === currentFilterIndex }"
@click="filterChange(index)"
>
<span
>{{ filter.label }}
<span v-if="filter.label === 'All'">{{ replicableType }}</span></span
>
</gl-dropdown-item>
</gl-dropdown>
<gl-search-box-by-type
v-model="search"
class="px-1 my-1 my-sm-0 bg-white w-100"
type="text"
:placeholder="__('Filter by name')"
/>
</div>
</div>
<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>
</nav>
</template>
<script>
import { mapActions } from 'vuex';
import { GlLink, GlDeprecatedButton } from '@gitlab/ui';
import { GlLink, GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import { ACTION_TYPES } from '../store/constants';
import GeoReplicableStatus from './geo_replicable_status.vue';
......@@ -10,7 +10,7 @@ export default {
name: 'GeoReplicableItem',
components: {
GlLink,
GlDeprecatedButton,
GlButton,
GeoReplicableTimeAgo,
GeoReplicableStatus,
},
......@@ -77,9 +77,10 @@ export default {
<div class="card-header d-flex align-center">
<gl-link class="font-weight-bold" :href="`/${name}`" target="_blank">{{ name }}</gl-link>
<div class="ml-auto">
<gl-deprecated-button
<gl-button
size="small"
@click="initiateReplicableSync({ projectId, name, action: $options.actionTypes.RESYNC })"
>{{ __('Resync') }}</gl-deprecated-button
>{{ __('Resync') }}</gl-button
>
</div>
</div>
......
......@@ -157,11 +157,11 @@ module EE
end
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
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
- 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'),
"geo-troubleshooting-link" => help_page_path('administration/geo/replication/troubleshooting.html'),
"replicable-type" => 'designs' } }
- page_title 'Geo Projects'
- params[:sync_status] ||= []
= render 'admin/geo/shared/replication_nav'
= render 'admin/geo/shared/filter_nav', replicable_name: _('projects'), replicable_controller: 'admin/geo/projects', action_buttons: @action_buttons
- case params[:sync_status]
- when 'failed'
......
- 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
.dropdown.col.px-1.my-1.my-sm-0.w-100
%a.btn.d-flex.align-items-center.justify-content-between.w-100{ href: '#', data: { toggle: 'dropdown' }, 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
= s_('Geo|Filter by status')
= sprite_icon("chevron-down", size: 16)
%ul.dropdown-menu
= nav_link(html_options: { class: ('bg-secondary-100' if !params[:sync_status].present?) }) do
= link_to controller: replicable_controller do
= sprintf(s_('Geo|All %{replicable_name}'), { replicable_name: replicable_name })
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'pending') }) do
= link_to controller: replicable_controller, sync_status: 'pending' do
= s_('Geo|In progress')
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'failed') }) do
= link_to controller: replicable_controller, sync_status: 'failed' do
= s_('Geo|Failed')
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'synced') }) do
= link_to controller: replicable_controller, sync_status: 'synced' do
= s_('Geo|Synced')
.col.replicable-search.px-1.my-1.my-sm-0.w-100
= render 'shared/projects/search_form', autofocus: true, search_form_placeholder: _("Filter by name"), icon: true
.col.col-sm-6.d-flex.justify-content-end.my-1.my-sm-0.w-100
- action_buttons.each do |action_button|
= action_button
%nav.bg-secondary.border-bottom.border-secondary-100.p-3
.row.d-flex.flex-column.flex-sm-row
.col
.d-sm-flex.mx-n1
.dropdown.px-1.my-1.my-sm-0.w-100
%a.btn.d-flex.align-items-center.justify-content-between.w-100{ href: '#', data: { toggle: 'dropdown' }, 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
= s_('Geo|Filter by status')
= sprite_icon("chevron-down", size: 16)
%ul.dropdown-menu
= nav_link(html_options: { class: ('bg-secondary-100' if !params[:sync_status].present?) }) do
= link_to controller: replicable_controller do
= sprintf(s_('Geo|All %{replicable_name}'), { replicable_name: replicable_name })
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'pending') }) do
= link_to controller: replicable_controller, sync_status: 'pending' do
= s_('Geo|In progress')
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'failed') }) do
= link_to controller: replicable_controller, sync_status: 'failed' do
= s_('Geo|Failed')
= nav_link(html_options: { class: ('bg-secondary-100' if params[:sync_status] == 'synced') }) do
= link_to controller: replicable_controller, sync_status: 'synced' do
= s_('Geo|Synced')
.replicable-search.px-1.my-1.my-sm-0.w-100
= 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'
- params[:sync_status] ||= []
= render 'admin/geo/shared/replication_nav'
= render 'admin/geo/shared/filter_nav', replicable_name: _('uploads'), replicable_controller: 'admin/geo/uploads'
- if @registries.any?
- @registries.each do |upload_registry|
......
......@@ -10,23 +10,15 @@
%strong.fly-out-top-item-name
#{ _('Geo') }
= 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
= _('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
= link_to admin_geo_settings_path, title: 'Settings' do
= link_to admin_geo_settings_path, title: _('Settings') do
%span
= _('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
namespace :geo do
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 :projects, only: [:index, :destroy] do
member do
post :reverify
post :resync
post :force_redownload
end
scope '/replication' do
get '/', to: redirect(path: 'admin/geo/replication/projects')
collection do
post :reverify_all
post :resync_all
resources :projects, only: [:index, :destroy] do
member do
post :reverify
post :resync
post :force_redownload
end
collection do
post :reverify_all
post :resync_all
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
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
end
end
describe 'clicking the nodes link' do
describe 'visiting geo nodes' do
it_behaves_like 'active sidebar link', 'Nodes' do
let(:path) { admin_geo_nodes_path }
end
end
describe 'clicking the settings link' do
describe 'visiting geo settings' do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
......@@ -46,20 +46,20 @@ describe 'admin Geo Sidebar', :js, :geo do
stub_secondary_node
end
describe 'clicking the projects link' do
it_behaves_like 'active sidebar link', 'Projects' do
describe 'visiting geo projects' do
it_behaves_like 'active sidebar link', 'Replication' do
let(:path) { admin_geo_projects_path }
end
end
describe 'clicking the designs link' do
it_behaves_like 'active sidebar link', 'Designs' do
describe 'visiting geo designs' do
it_behaves_like 'active sidebar link', 'Replication' do
let(:path) { admin_geo_designs_path }
end
end
describe 'clicking the uploads link' do
it_behaves_like 'active sidebar link', 'Uploads' do
describe 'visiting geo uploads' do
it_behaves_like 'active sidebar link', 'Replication' do
let(:path) { admin_geo_uploads_path }
end
end
......
import Vuex from 'vuex';
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 store from 'ee/geo_replicable/store';
import { ACTION_TYPES } from 'ee/geo_replicable/store/constants';
......@@ -43,7 +43,7 @@ describe('GeoReplicableItem', () => {
const findCard = () => wrapper.find('.card');
const findGlLink = () => findCard().find(GlLink);
const findGlButton = () => findCard().find(GlDeprecatedButton);
const findGlButton = () => findCard().find(GlButton);
const findCardHeader = () => findCard().find('.card-header');
const findCardBody = () => findCard().find('.card-body');
......
......@@ -55,19 +55,19 @@ describe Gitlab::Middleware::ReadOnly do
context 'whitelisted requests' do
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
it 'expects geo replication node api requests to be allowed' do
......
......@@ -6,23 +6,41 @@ describe 'EE-specific admin routing' do
let(:project_registry) { create(:geo_project_registry) }
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
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
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
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
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
......
......@@ -9722,15 +9722,15 @@ msgstr ""
msgid "Geo"
msgstr ""
msgid "Geo Designs"
msgstr ""
msgid "Geo Nodes"
msgstr ""
msgid "Geo Nodes|Cannot remove a primary node if there is a secondary node"
msgstr ""
msgid "Geo Replication"
msgstr ""
msgid "Geo Settings"
msgstr ""
......@@ -10022,6 +10022,9 @@ msgstr ""
msgid "Geo|Reverify all"
msgstr ""
msgid "Geo|Review replication status, and resynchronize and reverify items with the primary node."
msgstr ""
msgid "Geo|Status"
msgstr ""
......@@ -17708,6 +17711,9 @@ msgstr ""
msgid "Replaces the clone URL root."
msgstr ""
msgid "Replication"
msgstr ""
msgid "Reply by email"
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