Commit 0c90b5a9 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch '7126-geo-open-projects' into 'master'

Geo: Added a button to Admin UI > Geo Nodes to open Geo Projects screen of any secondary node

Closes #7126

See merge request gitlab-org/gitlab-ee!7512
parents 1ac860fb 7fa619ea
......@@ -2,13 +2,21 @@
import { __, s__ } from '~/locale';
import eventHub from '../event_hub';
import { NODE_ACTIONS } from '../constants';
import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
Icon,
},
props: {
node: {
type: Object,
required: true,
},
nodeActionsAllowed: {
type: Boolean,
required: true,
},
nodeEditAllowed: {
type: Boolean,
required: true,
......@@ -25,6 +33,9 @@
nodeToggleLabel() {
return this.node.enabled ? __('Disable') : __('Enable');
},
isSecondaryNode() {
return !this.node.primary;
},
},
methods: {
onToggleNode() {
......@@ -54,52 +65,69 @@
<template>
<div class="geo-node-actions">
<div
v-if="nodeMissingOauth"
class="node-action-container"
>
<button
type="button"
class="btn btn-default btn-sm btn-node-action"
@click="onRepairNode"
>
{{ s__('Repair authentication') }}
</button>
</div>
<div
v-if="isToggleAllowed"
class="node-action-container"
>
<button
:class="{
'btn-warning': node.enabled,
'btn-success': !node.enabled
}"
type="button"
class="btn btn-sm btn-node-action"
@click="onToggleNode"
>
{{ nodeToggleLabel }}
</button>
</div>
<div
v-if="nodeEditAllowed"
v-if="isSecondaryNode"
class="node-action-container"
>
<a
:href="node.editPath"
:href="node.geoProjectsUrl"
class="btn btn-sm btn-node-action"
target="_blank"
>
{{ __('Edit') }}
<icon
name="external-link"
/>
{{ __('Open projects') }}
</a>
</div>
<div class="node-action-container">
<button
type="button"
class="btn btn-sm btn-node-action btn-danger"
@click="onRemoveNode"
<template v-if="nodeActionsAllowed">
<div
v-if="nodeMissingOauth"
class="node-action-container"
>
{{ __('Remove') }}
</button>
</div>
<button
type="button"
class="btn btn-default btn-sm btn-node-action"
@click="onRepairNode"
>
{{ s__('Repair authentication') }}
</button>
</div>
<div
v-if="isToggleAllowed"
class="node-action-container"
>
<button
:class="{
'btn-warning': node.enabled,
'btn-success': !node.enabled
}"
type="button"
class="btn btn-sm btn-node-action"
@click="onToggleNode"
>
{{ nodeToggleLabel }}
</button>
</div>
<div
v-if="nodeEditAllowed"
class="node-action-container"
>
<a
:href="node.editPath"
class="btn btn-sm btn-node-action"
>
{{ __('Edit') }}
</a>
</div>
<div class="node-action-container">
<button
type="button"
class="btn btn-sm btn-node-action btn-danger"
@click="onRemoveNode"
>
{{ __('Remove') }}
</button>
</div>
</template>
</div>
</template>
......@@ -65,8 +65,8 @@
/>
</div>
<geo-node-actions
v-if="nodeActionsAllowed"
:node="node"
:node-actions-allowed="nodeActionsAllowed"
:node-edit-allowed="nodeEditAllowed"
:node-missing-oauth="nodeDetails.missingOAuthApplication"
/>
......
......@@ -54,6 +54,7 @@ export default class GeoNodesStore {
basePath: rawNode._links.self,
repairPath: rawNode._links.repair,
editPath: rawNode.web_edit_url,
geoProjectsUrl: rawNode.web_geo_projects_url,
statusPath: rawNode._links.status,
};
}
......
......@@ -153,6 +153,12 @@ class GeoNode < ActiveRecord::Base
Gitlab::Routing.url_helpers.oauth_geo_logout_url(url_helper_args.merge(state: state))
end
def geo_projects_url
return unless self.secondary?
Gitlab::Routing.url_helpers.admin_geo_projects_url(url_helper_args)
end
def missing_oauth_application?
self.primary? ? false : !oauth_application.present?
end
......
---
title: 'Geo: Added a button to Admin UI > Geo Nodes to open Geo Projects screen of any secondary node'
merge_request: 7512
author:
type: added
......@@ -263,6 +263,10 @@ module EE
::Gitlab::Routing.url_helpers.edit_admin_geo_node_url(geo_node)
end
expose :web_geo_projects_url, if: ->(geo_node, _) { geo_node.secondary? } do |geo_node|
geo_node.geo_projects_url
end
expose :_links do
expose :self do |geo_node|
expose_url api_v4_geo_nodes_path(id: geo_node.id)
......
require 'spec_helper'
describe 'GEO Nodes' do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:geo_url) { 'http://geo.example.com' }
include ::EE::GeoHelpers
set(:user) { create(:user) }
set(:geo_primary) { create(:geo_node, :primary) }
set(:geo_secondary) { create(:geo_node) }
context 'Geo Secondary Node' do
let(:project) { create(:project) }
before do
allow(Gitlab::Geo).to receive(:secondary?) { true }
allow(Gitlab::Geo).to receive_message_chain(:primary_node, :url) { geo_url }
stub_current_geo_node(geo_secondary)
project.add_maintainer(user)
sign_in(user)
......@@ -26,4 +29,31 @@ describe 'GEO Nodes' do
end
end
end
context 'Primary Geo Node' do
let(:admin_user) { create(:user, :admin) }
before do
stub_current_geo_node(geo_primary)
stub_licensed_features(geo: true)
sign_in(admin_user)
end
describe 'Geo Nodes admin screen' do
it "has a 'Open projects' button on listed secondary geo nodes pointing to correct URL", :js do
visit admin_geo_nodes_path
expect(page).to have_content(geo_primary.url)
expect(page).to have_content(geo_secondary.url)
wait_for_requests
geo_node_actions = all('div.geo-node-actions')
expected_url = File.join(geo_secondary.url, '/admin/geo/projects')
expect(geo_node_actions.last).to have_link('Open projects', href: expected_url)
end
end
end
end
......@@ -21,8 +21,9 @@
"files_max_capacity": { "type": "integer" },
"repos_max_capacity": { "type": "integer" },
"verification_max_capacity": { "type": "integer" },
"clone_protocol": { "type": ["string"] },
"clone_protocol": { "type": "string" },
"web_edit_url": { "type": "string" },
"web_geo_projects_url" : { "type": ["string", "null"] },
"_links": {
"type": "object",
"required": ["self", "repair"],
......
......@@ -6,12 +6,14 @@ import eventHub from 'ee/geo_nodes/event_hub';
import { NODE_ACTIONS } from 'ee/geo_nodes/constants';
import { mockNodes } from '../mock_data';
const createComponent = (node = mockNodes[0], nodeEditAllowed = true, nodeMissingOauth = false) => {
const createComponent = (node = mockNodes[0], nodeEditAllowed = true,
nodeActionsAllowed = true, nodeMissingOauth = false) => {
const Component = Vue.extend(geoNodeActionsComponent);
return mountComponent(Component, {
node,
nodeEditAllowed,
nodeActionsAllowed,
nodeMissingOauth,
});
};
......
......@@ -47,6 +47,7 @@ export const mockNode = {
current: true,
enabled: true,
nodeActionActive: false,
nodeActionsAllowed: false,
basePath: 'http://127.0.0.1:3001/api/v4/geo_nodes/1',
repairPath: 'http://127.0.0.1:3001/api/v4/geo_nodes/1/repair',
statusPath: 'http://127.0.0.1:3001/api/v4/geo_nodes/1/status',
......
......@@ -328,6 +328,18 @@ describe GeoNode, type: :model do
end
end
describe '#geo_projects_url' do
it 'returns the Geo Projects url for the specific node' do
expected_url = 'https://localhost:3000/gitlab/admin/geo/projects'
expect(new_node.geo_projects_url).to eq(expected_url)
end
it 'returns nil when node is a primary one' do
expect(primary_node.geo_projects_url).to be_nil
end
end
describe '#missing_oauth_application?' do
context 'on a primary node' do
it 'returns false' do
......
......@@ -5446,6 +5446,9 @@ msgstr ""
msgid "Open in Xcode"
msgstr ""
msgid "Open projects"
msgstr ""
msgid "Open sidebar"
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