Commit f1b91890 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab-ce master

parents 16ac8dd6 ed823c35
<script> <script>
import { GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui'; import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
import _ from 'underscore'; import _ from 'underscore';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -23,12 +23,18 @@ export default { ...@@ -23,12 +23,18 @@ export default {
GraphGroup, GraphGroup,
EmptyState, EmptyState,
Icon, Icon,
GlButton,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
GlLink, GlLink,
}, },
props: { props: {
externalDashboardPath: {
type: String,
required: false,
default: '',
},
hasMetrics: { hasMetrics: {
type: Boolean, type: Boolean,
required: false, required: false,
...@@ -241,6 +247,15 @@ export default { ...@@ -241,6 +247,15 @@ export default {
> >
</gl-dropdown> </gl-dropdown>
</div> </div>
<gl-button
v-if="externalDashboardPath.length"
class="js-external-dashboard-link"
variant="primary"
:href="externalDashboardPath"
>
{{ __('View full dashboard') }}
<icon name="external-link" />
</gl-button>
</div> </div>
<graph-group <graph-group
v-for="(groupData, index) in store.groups" v-for="(groupData, index) in store.groups"
......
<script>
import { GlButton, GlFormGroup, GlFormInput, GlLink } from '@gitlab/ui';
export default {
components: {
GlButton,
GlFormGroup,
GlFormInput,
GlLink,
},
props: {
externalDashboardPath: {
type: String,
required: false,
default: '',
},
externalDashboardHelpPagePath: {
type: String,
required: true,
},
},
};
</script>
<template>
<section class="settings expanded">
<div class="settings-header">
<h4 class="js-section-header">
{{ s__('ExternalMetrics|External Dashboard') }}
</h4>
<p class="js-section-sub-header">
{{
s__(
'ExternalMetrics|Add a button to the metrics dashboard linking directly to your existing external dashboards.',
)
}}
<gl-link :href="externalDashboardHelpPagePath">{{ __('Learn more') }}</gl-link>
</p>
</div>
<div class="settings-content">
<form>
<gl-form-group
:label="s__('ExternalMetrics|Full dashboard URL')"
:description="s__('ExternalMetrics|Enter the URL of the dashboard you want to link to')"
>
<gl-form-input
:value="externalDashboardPath"
placeholder="https://my-org.gitlab.io/my-dashboards"
/>
</gl-form-group>
<gl-button variant="success">
{{ __('Save Changes') }}
</gl-button>
</form>
</div>
</section>
</template>
import Vue from 'vue';
import ExternalDashboardForm from './components/external_dashboard.vue';
export default () => {
/**
* This check can be removed when we remove
* the :grafana_dashboard_link feature flag
*/
if (!gon.features.grafanaDashboardLink) {
return null;
}
const el = document.querySelector('.js-operation-settings');
return new Vue({
el,
render(createElement) {
return createElement(ExternalDashboardForm, {
props: {
...el.dataset,
expanded: false,
},
});
},
});
};
import mountErrorTrackingForm from '~/error_tracking_settings'; import mountErrorTrackingForm from '~/error_tracking_settings';
import mountOperationSettings from '~/operation_settings';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
mountErrorTrackingForm(); mountErrorTrackingForm();
mountOperationSettings();
}); });
...@@ -14,6 +14,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -14,6 +14,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
push_frontend_feature_flag(:metrics_time_window) push_frontend_feature_flag(:metrics_time_window)
push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint) push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint)
push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards) push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards)
push_frontend_feature_flag(:grafana_dashboard_link)
end end
def index def index
......
...@@ -5,6 +5,10 @@ module Projects ...@@ -5,6 +5,10 @@ module Projects
class OperationsController < Projects::ApplicationController class OperationsController < Projects::ApplicationController
before_action :authorize_update_environment! before_action :authorize_update_environment!
before_action do
push_frontend_feature_flag(:grafana_dashboard_link)
end
helper_method :error_tracking_setting helper_method :error_tracking_setting
def show def show
......
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
= render_if_exists 'projects/issues/related_issues' = render_if_exists 'projects/issues/related_issues'
#js-related-merge-requests{ data: { endpoint: expose_url(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid)), project_namespace: @project.namespace.path, project_path: @project.path } } #js-related-merge-requests{ data: { endpoint: expose_path(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid)), project_namespace: @project.namespace.path, project_path: @project.path } }
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
#related-branches{ data: { url: related_branches_project_issue_path(@project, @issue) } } #related-branches{ data: { url: related_branches_project_issue_path(@project, @issue) } }
......
.js-operation-settings{ data: { external_dashboard: { path: '',
help_page_path: help_page_path('user/project/operations/link_to_external_dashboard') } } }
...@@ -4,4 +4,5 @@ ...@@ -4,4 +4,5 @@
= render_if_exists 'projects/settings/operations/incidents' = render_if_exists 'projects/settings/operations/incidents'
= render 'projects/settings/operations/error_tracking', expanded: true = render 'projects/settings/operations/error_tracking', expanded: true
= render 'projects/settings/operations/external_dashboard'
= render_if_exists 'projects/settings/operations/tracing' = render_if_exists 'projects/settings/operations/tracing'
---
title: Use a path for the related merge requests endpoint
merge_request: 28171
author:
type: fixed
...@@ -13,6 +13,10 @@ module API ...@@ -13,6 +13,10 @@ module API
available?(:merge_requests, project, options[:current_user]) available?(:merge_requests, project, options[:current_user])
end end
def expose_path(path)
Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, path)
end
def expose_url(path) def expose_url(path)
url_options = Gitlab::Application.routes.default_url_options url_options = Gitlab::Application.routes.default_url_options
protocol, host, port, script_name = url_options.values_at(:protocol, :host, :port, :script_name) protocol, host, port, script_name = url_options.values_at(:protocol, :host, :port, :script_name)
......
...@@ -5000,6 +5000,18 @@ msgstr "" ...@@ -5000,6 +5000,18 @@ msgstr ""
msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used." msgid "ExternalAuthorizationService|When no classification label is set the default label `%{default_label}` will be used."
msgstr "" msgstr ""
msgid "ExternalMetrics|Add a button to the metrics dashboard linking directly to your existing external dashboards."
msgstr ""
msgid "ExternalMetrics|Enter the URL of the dashboard you want to link to"
msgstr ""
msgid "ExternalMetrics|External Dashboard"
msgstr ""
msgid "ExternalMetrics|Full dashboard URL"
msgstr ""
msgid "ExternalWikiService|External Wiki" msgid "ExternalWikiService|External Wiki"
msgstr "" msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import { GlButton, GlLink, GlFormGroup, GlFormInput } from '@gitlab/ui';
import ExternalDashboard from '~/operation_settings/components/external_dashboard.vue';
import { TEST_HOST } from 'helpers/test_constants';
describe('operation settings external dashboard component', () => {
let wrapper;
const externalDashboardPath = `http://mock-external-domain.com/external/dashboard/path`;
const externalDashboardHelpPagePath = `${TEST_HOST}/help/page/path`;
beforeEach(() => {
wrapper = shallowMount(ExternalDashboard, {
propsData: {
externalDashboardPath,
externalDashboardHelpPagePath,
},
});
});
it('renders header text', () => {
expect(wrapper.find('.js-section-header').text()).toBe('External Dashboard');
});
describe('sub-header', () => {
let subHeader;
beforeEach(() => {
subHeader = wrapper.find('.js-section-sub-header');
});
it('renders descriptive text', () => {
expect(subHeader.text()).toContain(
'Add a button to the metrics dashboard linking directly to your existing external dashboards.',
);
});
it('renders help page link', () => {
const link = subHeader.find(GlLink);
expect(link.text()).toBe('Learn more');
expect(link.attributes().href).toBe(externalDashboardHelpPagePath);
});
});
describe('form', () => {
let form;
beforeEach(() => {
form = wrapper.find('form');
});
describe('external dashboard url', () => {
describe('input label', () => {
let formGroup;
beforeEach(() => {
formGroup = form.find(GlFormGroup);
});
it('uses label text', () => {
expect(formGroup.attributes().label).toBe('Full dashboard URL');
});
it('uses description text', () => {
expect(formGroup.attributes().description).toBe(
'Enter the URL of the dashboard you want to link to',
);
});
});
describe('input field', () => {
let input;
beforeEach(() => {
input = form.find(GlFormInput);
});
it('defaults to externalDashboardPath prop', () => {
expect(input.attributes().value).toBe(externalDashboardPath);
});
it('uses a placeholder', () => {
expect(input.attributes().placeholder).toBe('https://my-org.gitlab.io/my-dashboards');
});
});
describe('submit button', () => {
let submit;
beforeEach(() => {
submit = form.find(GlButton);
});
it('renders button label', () => {
expect(submit.text()).toBe('Save Changes');
});
});
});
});
});
...@@ -37,6 +37,9 @@ describe('Dashboard', () => { ...@@ -37,6 +37,9 @@ describe('Dashboard', () => {
window.gon = { window.gon = {
...window.gon, ...window.gon,
ee: false, ee: false,
features: {
grafanaDashboardLink: true,
},
}; };
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
...@@ -323,4 +326,63 @@ describe('Dashboard', () => { ...@@ -323,4 +326,63 @@ describe('Dashboard', () => {
.catch(done.fail); .catch(done.fail);
}); });
}); });
describe('external dashboard link', () => {
let component;
beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
});
afterEach(() => {
component.$destroy();
});
describe('with feature flag enabled', () => {
beforeEach(() => {
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
showTimeWindowDropdown: false,
externalDashboardPath: '/mockPath',
},
});
});
it('shows the link', done => {
setTimeout(() => {
expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
'View full dashboard',
);
done();
});
});
});
describe('without feature flage enabled', () => {
beforeEach(() => {
window.gon.features.grafanaDashboardLink = false;
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
showTimeWindowDropdown: false,
externalDashboardPath: '',
},
});
});
it('does not show the link', done => {
setTimeout(() => {
expect(component.$el.querySelector('.js-external-dashboard-link')).toBe(null);
done();
});
});
});
});
}); });
...@@ -5,6 +5,40 @@ describe API::Helpers::RelatedResourcesHelpers do ...@@ -5,6 +5,40 @@ describe API::Helpers::RelatedResourcesHelpers do
Class.new.include(described_class).new Class.new.include(described_class).new
end end
describe '#expose_path' do
let(:path) { '/api/v4/awesome_endpoint' }
context 'empty relative URL root' do
before do
stub_config_setting(relative_url_root: '')
end
it 'returns the existing path' do
expect(helpers.expose_path(path)).to eq(path)
end
end
context 'slash relative URL root' do
before do
stub_config_setting(relative_url_root: '/')
end
it 'returns the existing path' do
expect(helpers.expose_path(path)).to eq(path)
end
end
context 'with relative URL root' do
before do
stub_config_setting(relative_url_root: '/gitlab/root')
end
it 'returns the existing path' do
expect(helpers.expose_path(path)).to eq("/gitlab/root" + path)
end
end
end
describe '#expose_url' do describe '#expose_url' do
let(:path) { '/api/v4/awesome_endpoint' } let(:path) { '/api/v4/awesome_endpoint' }
subject(:url) { helpers.expose_url(path) } subject(:url) { helpers.expose_url(path) }
......
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