Commit df90667f authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'sy-clean-gfm-filter-examples' into 'master'

Refactor metric embeds to ease additional embed types

See merge request gitlab-org/gitlab!24925
parents 958588b8 61c70eb6
...@@ -20,6 +20,7 @@ module Banzai ...@@ -20,6 +20,7 @@ module Banzai
# the cost of doing a full regex match. # the cost of doing a full regex match.
def xpath_search def xpath_search
"descendant-or-self::a[contains(@href,'metrics') and \ "descendant-or-self::a[contains(@href,'metrics') and \
contains(@href,'environments') and \
starts-with(@href, '#{Gitlab.config.gitlab.url}')]" starts-with(@href, '#{Gitlab.config.gitlab.url}')]"
end end
......
...@@ -9,8 +9,8 @@ module Banzai ...@@ -9,8 +9,8 @@ module Banzai
METRICS_CSS_CLASS = '.js-render-metrics' METRICS_CSS_CLASS = '.js-render-metrics'
EMBED_LIMIT = 100 EMBED_LIMIT = 100
URL = Gitlab::Metrics::Dashboard::Url
Route = Struct.new(:regex, :permission)
Embed = Struct.new(:project_path, :permission) Embed = Struct.new(:project_path, :permission)
# Finds all embeds based on the css class the FE # Finds all embeds based on the css class the FE
...@@ -59,14 +59,28 @@ module Banzai ...@@ -59,14 +59,28 @@ module Banzai
embed = Embed.new embed = Embed.new
url = node.attribute('data-dashboard-url').to_s url = node.attribute('data-dashboard-url').to_s
set_path_and_permission(embed, url, URL.metrics_regex, :read_environment) permissions_by_route.each do |route|
set_path_and_permission(embed, url, URL.grafana_regex, :read_project) unless embed.permission set_path_and_permission(embed, url, route.regex, route.permission) unless embed.permission
end
embeds[node] = embed if embed.permission embeds[node] = embed if embed.permission
end end
end end
end end
def permissions_by_route
[
Route.new(
::Gitlab::Metrics::Dashboard::Url.metrics_regex,
:read_environment
),
Route.new(
::Gitlab::Metrics::Dashboard::Url.grafana_regex,
:read_project
)
]
end
# Attempts to determine the path and permission attributes # Attempts to determine the path and permission attributes
# of a url based on expected dashboard url formats and # of a url based on expected dashboard url formats and
# sets the attributes on an Embed object # sets the attributes on an Embed object
......
...@@ -29,8 +29,7 @@ module Banzai ...@@ -29,8 +29,7 @@ module Banzai
Filter::AudioLinkFilter, Filter::AudioLinkFilter,
Filter::ImageLazyLoadFilter, Filter::ImageLazyLoadFilter,
Filter::ImageLinkFilter, Filter::ImageLinkFilter,
Filter::InlineMetricsFilter, *metrics_filters,
Filter::InlineGrafanaMetricsFilter,
Filter::TableOfContentsFilter, Filter::TableOfContentsFilter,
Filter::TableOfContentsTagFilter, Filter::TableOfContentsTagFilter,
Filter::AutolinkFilter, Filter::AutolinkFilter,
...@@ -48,6 +47,13 @@ module Banzai ...@@ -48,6 +47,13 @@ module Banzai
] ]
end end
def self.metrics_filters
[
Filter::InlineMetricsFilter,
Filter::InlineGrafanaMetricsFilter
]
end
def self.reference_filters def self.reference_filters
[ [
Filter::UserReferenceFilter, Filter::UserReferenceFilter,
......
...@@ -8,40 +8,26 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do ...@@ -8,40 +8,26 @@ describe Banzai::Filter::InlineGrafanaMetricsFilter do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:grafana_integration) { create(:grafana_integration, project: project) } let_it_be(:grafana_integration) { create(:grafana_integration, project: project) }
let(:input) { %(<a href="#{url}">example</a>) } let(:input) { %(<a href="#{trigger_url}">example</a>) }
let(:doc) { filter(input) } let(:doc) { filter(input) }
let(:url) { grafana_integration.grafana_url + dashboard_path }
let(:dashboard_path) do let(:dashboard_path) do
'/d/XDaNK6amz/gitlab-omnibus-redis' \ '/d/XDaNK6amz/gitlab-omnibus-redis' \
'?from=1570397739557&to=1570484139557' \ '?from=1570397739557&to=1570484139557' \
'&var-instance=All&panelId=14' '&var-instance=All&panelId=14'
end end
it 'appends a metrics charts placeholder with dashboard url after metrics links' do let(:trigger_url) { grafana_integration.grafana_url + dashboard_path }
node = doc.at_css('.js-render-metrics') let(:dashboard_url) do
expect(node).to be_present urls.project_grafana_api_metrics_dashboard_url(
dashboard_url = urls.project_grafana_api_metrics_dashboard_url(
project, project,
embedded: true, embedded: true,
grafana_url: url, grafana_url: trigger_url,
start: "2019-10-06T21:35:39Z", start: "2019-10-06T21:35:39Z",
end: "2019-10-07T21:35:39Z" end: "2019-10-07T21:35:39Z"
) )
expect(node.attribute('data-dashboard-url').to_s).to eq(dashboard_url)
end end
context 'when the dashboard link is part of a paragraph' do it_behaves_like 'a metrics embed filter'
let(:paragraph) { %(This is an <a href="#{url}">example</a> of metrics.) }
let(:input) { %(<p>#{paragraph}</p>) }
it 'appends the charts placeholder after the enclosing paragraph' do
expect(unescape(doc.at_css('p').to_s)).to include(paragraph)
expect(doc.at_css('.js-render-metrics')).to be_present
end
end
context 'when grafana is not configured' do context 'when grafana is not configured' do
before do before do
......
...@@ -5,66 +5,31 @@ require 'spec_helper' ...@@ -5,66 +5,31 @@ require 'spec_helper'
describe Banzai::Filter::InlineMetricsFilter do describe Banzai::Filter::InlineMetricsFilter do
include FilterSpecHelper include FilterSpecHelper
let(:input) { %(<a href="#{url}">example</a>) }
let(:doc) { filter(input) }
context 'when the document has an external link' do
let(:url) { 'https://foo.com' }
it 'leaves regular non-metrics links unchanged' do
expect(doc.to_s).to eq(input)
end
end
context 'when the document has a metrics dashboard link' do
let(:params) { ['foo', 'bar', 12] } let(:params) { ['foo', 'bar', 12] }
let(:url) { urls.metrics_namespace_project_environment_url(*params) } let(:query_params) { {} }
it 'leaves the original link unchanged' do
expect(doc.at_css('a').to_s).to eq(input)
end
it 'appends a metrics charts placeholder with dashboard url after metrics links' do
node = doc.at_css('.js-render-metrics')
expect(node).to be_present
dashboard_url = urls.metrics_dashboard_namespace_project_environment_url(*params, embedded: true) let(:trigger_url) { urls.metrics_namespace_project_environment_url(*params, query_params) }
expect(node.attribute('data-dashboard-url').to_s).to eq(dashboard_url) let(:dashboard_url) { urls.metrics_dashboard_namespace_project_environment_url(*params, **query_params, embedded: true) }
end
context 'when the metrics dashboard link is part of a paragraph' do
let(:paragraph) { %(This is an <a href="#{url}">example</a> of metrics.) }
let(:input) { %(<p>#{paragraph}</p>) }
it 'appends the charts placeholder after the enclosing paragraph' do it_behaves_like 'a metrics embed filter'
expect(doc.at_css('p').to_s).to include(paragraph)
expect(doc.at_css('.js-render-metrics')).to be_present
end
end
context 'with dashboard params specified' do context 'with query params specified' do
let(:params) do let(:query_params) do
[
'foo',
'bar',
12,
{ {
embedded: true,
dashboard: 'config/prometheus/common_metrics.yml', dashboard: 'config/prometheus/common_metrics.yml',
group: 'System metrics (Kubernetes)', group: 'System metrics (Kubernetes)',
title: 'Core Usage (Pod Average)', title: 'Core Usage (Pod Average)',
y_label: 'Cores per Pod' y_label: 'Cores per Pod'
} }
]
end end
it 'appends a metrics charts placeholder with dashboard url after metrics links' do it_behaves_like 'a metrics embed filter'
node = doc.at_css('.js-render-metrics')
expect(node).to be_present
dashboard_url = urls.metrics_dashboard_namespace_project_environment_url(*params)
expect(node.attribute('data-dashboard-url').to_s).to eq(dashboard_url)
end
end end
it 'leaves links to other dashboards unchanged' do
url = urls.namespace_project_grafana_api_metrics_dashboard_url('foo', 'bar')
input = %(<a href="#{url}">example</a>)
expect(filter(input).to_s).to eq(input)
end end
end end
...@@ -18,33 +18,6 @@ describe Banzai::Filter::InlineMetricsRedactorFilter do ...@@ -18,33 +18,6 @@ describe Banzai::Filter::InlineMetricsRedactorFilter do
end end
context 'with a metrics charts placeholder' do context 'with a metrics charts placeholder' do
shared_examples_for 'a supported metrics dashboard url' do
context 'no user is logged in' do
it 'redacts the placeholder' do
expect(doc.to_s).to be_empty
end
end
context 'the user does not have permission do see charts' do
let(:doc) { filter(input, current_user: build(:user)) }
it 'redacts the placeholder' do
expect(doc.to_s).to be_empty
end
end
context 'the user has requisite permissions' do
let(:user) { create(:user) }
let(:doc) { filter(input, current_user: user) }
it 'leaves the placeholder' do
project.add_maintainer(user)
expect(doc.to_s).to eq input
end
end
end
let(:input) { %(<div class="js-render-metrics" data-dashboard-url="#{url}"></div>) } let(:input) { %(<div class="js-render-metrics" data-dashboard-url="#{url}"></div>) }
it_behaves_like 'a supported metrics dashboard url' it_behaves_like 'a supported metrics dashboard url'
......
# frozen_string_literal: true
# Expects 2 attributes to be defined:
# trigger_url - Url expected to trigger the insertion of a placeholder.
# dashboard_url - Url expected to be present in the placeholder.
RSpec.shared_examples 'a metrics embed filter' do
let(:input) { %(<a href="#{url}">example</a>) }
let(:doc) { filter(input) }
context 'when the document has an external link' do
let(:url) { 'https://foo.com' }
it 'leaves regular non-metrics links unchanged' do
expect(doc.to_s).to eq(input)
end
end
context 'when the document contains an embeddable link' do
let(:url) { trigger_url }
it 'leaves the original link unchanged' do
expect(unescape(doc.at_css('a').to_s)).to eq(input)
end
it 'appends a metrics charts placeholder' do
node = doc.at_css('.js-render-metrics')
expect(node).to be_present
expect(node.attribute('data-dashboard-url').to_s).to eq(dashboard_url)
end
context 'in a paragraph' do
let(:paragraph) { %(This is an <a href="#{url}">example</a> of metrics.) }
let(:input) { %(<p>#{paragraph}</p>) }
it 'appends a metrics charts placeholder after the enclosing paragraph' do
expect(unescape(doc.at_css('p').to_s)).to include(paragraph)
expect(doc.at_css('.js-render-metrics')).to be_present
end
end
end
# Nokogiri escapes the URLs, but we don't care about that
# distinction for the purposes of these filters
def unescape(html)
CGI.unescapeHTML(html)
end
end
# frozen_string_literal: true
RSpec.shared_examples 'a supported metrics dashboard url' do
context 'no user is logged in' do
it 'redacts the placeholder' do
expect(doc.to_s).to be_empty
end
end
context 'the user does not have permission do see charts' do
let(:doc) { filter(input, current_user: build(:user)) }
it 'redacts the placeholder' do
expect(doc.to_s).to be_empty
end
end
context 'the user has requisite permissions' do
let(:user) { create(:user) }
let(:doc) { filter(input, current_user: user) }
it 'leaves the placeholder' do
project.add_maintainer(user)
expect(doc.to_s).to eq(input)
end
end
end
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