Skip to content
Snippets Groups Projects
Commit 7cdd70dc authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 79348fac
No related branches found
No related tags found
No related merge requests found
Showing
with 245 additions and 196 deletions
Loading
Loading
@@ -13,7 +13,7 @@
- .default-before_script
- .assets-compile-cache
- .only:changes-code-backstage-qa
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-git-2.22-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.33-docker-18.06.1
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-git-2.22-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.33-docker-19.03.1
stage: test
dependencies: ["setup-test-env"]
needs: ["setup-test-env"]
Loading
Loading
Loading
Loading
@@ -312,8 +312,7 @@ gem 'gettext', '~> 3.2.2', require: false, group: :development
gem 'batch-loader', '~> 1.4.0'
 
# Perf bar
# https://gitlab.com/gitlab-org/gitlab/issues/13996
gem 'gitlab-peek', '~> 0.0.1', require: 'peek'
gem 'peek', '~> 1.1'
 
# Snowplow events tracking
gem 'snowplow-tracker', '~> 0.6.1'
Loading
Loading
@@ -381,7 +380,7 @@ group :development, :test do
 
gem 'knapsack', '~> 1.17'
 
gem 'stackprof', '~> 0.2.10', require: false
gem 'stackprof', '~> 0.2.13', require: false
 
gem 'simple_po_parser', '~> 1.1.2', require: false
 
Loading
Loading
Loading
Loading
@@ -373,8 +373,6 @@ GEM
gitlab-license (1.0.0)
gitlab-markup (1.7.0)
gitlab-net-dns (0.9.1)
gitlab-peek (0.0.1)
railties (>= 4.0.0)
gitlab-sidekiq-fetcher (0.5.2)
sidekiq (~> 5)
gitlab-styles (2.8.0)
Loading
Loading
@@ -724,6 +722,8 @@ GEM
parser (2.6.3.0)
ast (~> 2.4.0)
parslet (1.8.2)
peek (1.1.0)
railties (>= 4.0.0)
pg (1.1.4)
po_to_json (1.0.1)
json (>= 1.6.0)
Loading
Loading
@@ -1003,7 +1003,7 @@ GEM
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
sshkey (2.0.0)
stackprof (0.2.10)
stackprof (0.2.13)
state_machines (0.5.0)
state_machines-activemodel (0.7.1)
activemodel (>= 4.1)
Loading
Loading
@@ -1201,7 +1201,6 @@ DEPENDENCIES
gitlab-license (~> 1.0)
gitlab-markup (~> 1.7.0)
gitlab-net-dns (~> 0.9.1)
gitlab-peek (~> 0.0.1)
gitlab-sidekiq-fetcher (= 0.5.2)
gitlab-styles (~> 2.7)
gitlab_chronic_duration (~> 0.10.6.2)
Loading
Loading
@@ -1276,6 +1275,7 @@ DEPENDENCIES
omniauth_crowd (~> 2.2.0)
omniauth_openid_connect (~> 0.3.3)
org-ruby (~> 0.9.12)
peek (~> 1.1)
pg (~> 1.1)
premailer-rails (~> 1.10.3)
prometheus-client-mmap (~> 0.9.10)
Loading
Loading
@@ -1340,7 +1340,7 @@ DEPENDENCIES
spring-commands-rspec (~> 1.0.4)
sprockets (~> 3.7.0)
sshkey (~> 2.0)
stackprof (~> 0.2.10)
stackprof (~> 0.2.13)
state_machines-activerecord (~> 0.6.0)
sys-filesystem (~> 1.1.6)
test-prof (~> 0.10.0)
Loading
Loading
Loading
Loading
@@ -29,7 +29,7 @@ const AREA_COLOR_RGBA = `rgba(${hexToRgb(AREA_COLOR).join(',')},${AREA_OPACITY})
* time series chart, the boundary band shows the normal
* range of values the metric should take.
*
* This component accepts 3 queries, which contain the
* This component accepts 3 metrics, which contain the
* "metric", "upper" limit and "lower" limit.
*
* The upper and lower series are "stacked areas" visually
Loading
Loading
@@ -62,10 +62,10 @@ export default {
},
computed: {
series() {
return this.graphData.queries.map(query => {
const values = query.result[0] ? query.result[0].values : [];
return this.graphData.metrics.map(metric => {
const values = metric.result && metric.result[0] ? metric.result[0].values : [];
return {
label: query.label,
label: metric.label,
// NaN values may disrupt avg., max. & min. calculations in the legend, filter them out
data: values.filter(([, value]) => !Number.isNaN(value)),
};
Loading
Loading
@@ -83,7 +83,7 @@ export default {
return min < 0 ? -min : 0;
},
metricData() {
const originalMetricQuery = this.graphData.queries[0];
const originalMetricQuery = this.graphData.metrics[0];
 
const metricQuery = { ...originalMetricQuery };
metricQuery.result[0].values = metricQuery.result[0].values.map(([x, y]) => [
Loading
Loading
@@ -93,7 +93,7 @@ export default {
return {
...this.graphData,
type: 'line-chart',
queries: [metricQuery],
metrics: [metricQuery],
};
},
metricSeriesConfig() {
Loading
Loading
Loading
Loading
@@ -32,8 +32,8 @@ export default {
},
computed: {
chartData() {
const queryData = this.graphData.queries.reduce((acc, query) => {
const series = makeDataSeries(query.result, {
const queryData = this.graphData.metrics.reduce((acc, query) => {
const series = makeDataSeries(query.result || [], {
name: this.formatLegendLabel(query),
});
 
Loading
Loading
@@ -45,13 +45,13 @@ export default {
};
},
xAxisTitle() {
return this.graphData.queries[0].result[0].x_label !== undefined
? this.graphData.queries[0].result[0].x_label
return this.graphData.metrics[0].result[0].x_label !== undefined
? this.graphData.metrics[0].result[0].x_label
: '';
},
yAxisTitle() {
return this.graphData.queries[0].result[0].y_label !== undefined
? this.graphData.queries[0].result[0].y_label
return this.graphData.metrics[0].result[0].y_label !== undefined
? this.graphData.metrics[0].result[0].y_label
: '';
},
xAxisType() {
Loading
Loading
Loading
Loading
@@ -24,7 +24,7 @@ export default {
},
computed: {
chartData() {
return this.queries.result.reduce(
return this.metrics.result.reduce(
(acc, result, i) => [...acc, ...result.values.map((value, j) => [i, j, value[1]])],
[],
);
Loading
Loading
@@ -36,7 +36,7 @@ export default {
return this.graphData.y_label || '';
},
xAxisLabels() {
return this.queries.result.map(res => Object.values(res.metric)[0]);
return this.metrics.result.map(res => Object.values(res.metric)[0]);
},
yAxisLabels() {
return this.result.values.map(val => {
Loading
Loading
@@ -46,10 +46,10 @@ export default {
});
},
result() {
return this.queries.result[0];
return this.metrics.result[0];
},
queries() {
return this.graphData.queries[0];
metrics() {
return this.graphData.metrics[0];
},
},
};
Loading
Loading
Loading
Loading
@@ -17,7 +17,7 @@ export default {
},
computed: {
queryInfo() {
return this.graphData.queries[0];
return this.graphData.metrics[0];
},
engineeringNotation() {
return `${roundOffFloat(this.queryInfo.result[0].value[1], 1)}${this.queryInfo.unit}`;
Loading
Loading
Loading
Loading
@@ -105,7 +105,7 @@ export default {
// Transforms & supplements query data to render appropriate labels & styles
// Input: [{ queryAttributes1 }, { queryAttributes2 }]
// Output: [{ seriesAttributes1 }, { seriesAttributes2 }]
return this.graphData.queries.reduce((acc, query) => {
return this.graphData.metrics.reduce((acc, query) => {
const { appearance } = query;
const lineType =
appearance && appearance.line && appearance.line.type
Loading
Loading
@@ -121,7 +121,7 @@ export default {
? appearance.area.opacity
: undefined,
};
const series = makeDataSeries(query.result, {
const series = makeDataSeries(query.result || [], {
name: this.formatLegendLabel(query),
lineStyle: {
type: lineType,
Loading
Loading
Loading
Loading
@@ -250,14 +250,9 @@ export default {
'setEndpoints',
'setPanelGroupMetrics',
]),
chartsWithData(charts) {
return charts.filter(chart =>
chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
);
},
updateMetrics(key, metrics) {
updateMetrics(key, panels) {
this.setPanelGroupMetrics({
metrics,
panels,
key,
});
},
Loading
Loading
@@ -292,8 +287,13 @@ export default {
submitCustomMetricsForm() {
this.$refs.customMetricsForm.submit();
},
chartsWithData(panels) {
return panels.filter(panel =>
panel.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
);
},
groupHasData(group) {
return this.chartsWithData(group.metrics).length > 0;
return this.chartsWithData(group.panels).length > 0;
},
onDateTimePickerApply(timeWindowUrlParams) {
return redirectTo(mergeUrlParams(timeWindowUrlParams, window.location.href));
Loading
Loading
@@ -453,14 +453,14 @@ export default {
:collapse-group="groupHasData(groupData)"
>
<vue-draggable
:value="groupData.metrics"
:value="groupData.panels"
group="metrics-dashboard"
:component-data="{ attrs: { class: 'row mx-0 w-100' } }"
:disabled="!isRearrangingPanels"
@input="updateMetrics(groupData.key, $event)"
>
<div
v-for="(graphData, graphIndex) in groupData.metrics"
v-for="(graphData, graphIndex) in groupData.panels"
:key="`panel-type-${graphIndex}`"
class="col-12 col-lg-6 px-2 mb-2 draggable"
:class="{ 'draggable-enabled': isRearrangingPanels }"
Loading
Loading
@@ -469,7 +469,7 @@ export default {
<div
v-if="isRearrangingPanels"
class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end"
@click="removeGraph(groupData.metrics, graphIndex)"
@click="removeGraph(groupData.panels, graphIndex)"
>
<a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')"
><icon name="close"
Loading
Loading
Loading
Loading
@@ -37,11 +37,14 @@ export default {
computed: {
...mapState('monitoringDashboard', ['dashboard', 'metricsWithData']),
charts() {
if (!this.dashboard || !this.dashboard.panel_groups) {
return [];
}
const groupWithMetrics = this.dashboard.panel_groups.find(group =>
group.metrics.find(chart => this.chartHasData(chart)),
) || { metrics: [] };
group.panels.find(chart => this.chartHasData(chart)),
) || { panels: [] };
 
return groupWithMetrics.metrics.filter(chart => this.chartHasData(chart));
return groupWithMetrics.panels.filter(chart => this.chartHasData(chart));
},
isSingleChart() {
return this.charts.length === 1;
Loading
Loading
Loading
Loading
@@ -54,10 +54,14 @@ export default {
return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData;
},
graphDataHasMetrics() {
return this.graphData.queries[0].result.length > 0;
return (
this.graphData.metrics &&
this.graphData.metrics[0].result &&
this.graphData.metrics[0].result.length > 0
);
},
csvText() {
const chartData = this.graphData.queries[0].result[0].values;
const chartData = this.graphData.metrics[0].result[0].values;
const yLabel = this.graphData.y_label;
const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings
return chartData.reduce((csv, data) => {
Loading
Loading
@@ -112,7 +116,7 @@ export default {
:graph-data="graphData"
:deployment-data="deploymentData"
:project-path="projectPath"
:thresholds="getGraphAlertValues(graphData.queries)"
:thresholds="getGraphAlertValues(graphData.metrics)"
group-id="panel-type-chart"
>
<div class="d-flex align-items-center">
Loading
Loading
@@ -120,8 +124,8 @@ export default {
v-if="alertWidgetAvailable && graphData"
:modal-id="`alert-modal-${index}`"
:alerts-endpoint="alertsEndpoint"
:relevant-queries="graphData.queries"
:alerts-to-manage="getGraphAlerts(graphData.queries)"
:relevant-queries="graphData.metrics"
:alerts-to-manage="getGraphAlerts(graphData.metrics)"
@setAlerts="setAlerts"
/>
<gl-dropdown
Loading
Loading
import Vue from 'vue';
import { slugify } from '~/lib/utils/text_utility';
import * as types from './mutation_types';
import { normalizeMetrics, normalizeMetric, normalizeQueryResult } from './utils';
import { normalizeMetric, normalizeQueryResult } from './utils';
 
const normalizePanel = panel => panel.metrics.map(normalizeMetric);
const normalizePanelMetrics = (metrics, defaultLabel) =>
metrics.map(metric => ({
...normalizeMetric(metric),
label: metric.label || defaultLabel,
}));
 
export default {
[types.REQUEST_METRICS_DATA](state) {
Loading
Loading
@@ -13,28 +17,18 @@ export default {
[types.RECEIVE_METRICS_DATA_SUCCESS](state, groupData) {
state.dashboard.panel_groups = groupData.map((group, i) => {
const key = `${slugify(group.group || 'default')}-${i}`;
let { metrics = [], panels = [] } = group;
let { panels = [] } = group;
 
// each panel has metric information that needs to be normalized
panels = panels.map(panel => ({
...panel,
metrics: normalizePanel(panel),
}));
// for backwards compatibility, and to limit Vue template changes:
// for each group alias panels to metrics
// for each panel alias metrics to queries
metrics = panels.map(panel => ({
...panel,
queries: panel.metrics,
metrics: normalizePanelMetrics(panel.metrics, panel.y_label),
}));
 
return {
...group,
panels,
key,
metrics: normalizeMetrics(metrics),
};
});
 
Loading
Loading
@@ -58,6 +52,7 @@ export default {
[types.RECEIVE_ENVIRONMENTS_DATA_FAILURE](state) {
state.environments = [];
},
[types.SET_QUERY_RESULT](state, { metricId, result }) {
if (!metricId || !result || result.length === 0) {
return;
Loading
Loading
@@ -65,14 +60,17 @@ export default {
 
state.showEmptyState = false;
 
/**
* Search the dashboard state for a matching id
*/
state.dashboard.panel_groups.forEach(group => {
group.metrics.forEach(metric => {
metric.queries.forEach(query => {
if (query.metric_id === metricId) {
group.panels.forEach(panel => {
panel.metrics.forEach(metric => {
if (metric.metric_id === metricId) {
state.metricsWithData.push(metricId);
// ensure dates/numbers are correctly formatted for charts
const normalizedResults = result.map(normalizeQueryResult);
Vue.set(query, 'result', Object.freeze(normalizedResults));
Vue.set(metric, 'result', Object.freeze(normalizedResults));
}
});
});
Loading
Loading
@@ -101,6 +99,6 @@ export default {
},
[types.SET_PANEL_GROUP_METRICS](state, payload) {
const panelGroup = state.dashboard.panel_groups.find(pg => payload.key === pg.key);
panelGroup.metrics = payload.metrics;
panelGroup.panels = payload.panels;
},
};
import _ from 'underscore';
 
function checkQueryEmptyData(query) {
return {
...query,
result: query.result.filter(timeSeries => {
const newTimeSeries = timeSeries;
const hasValue = series =>
!Number.isNaN(series[1]) && (series[1] !== null || series[1] !== undefined);
const hasNonNullValue = timeSeries.values.find(hasValue);
newTimeSeries.values = hasNonNullValue ? newTimeSeries.values : [];
return newTimeSeries.values.length > 0;
}),
};
}
function removeTimeSeriesNoData(queries) {
return queries.reduce((series, query) => series.concat(checkQueryEmptyData(query)), []);
}
// Metrics and queries are currently stored 1:1, so `queries` is an array of length one.
// We want to group queries onto a single chart by title & y-axis label.
// This function will no longer be required when metrics:queries are 1:many,
// though there is no consequence if the function stays in use.
// @param metrics [Array<Object>]
// Ex) [
// { id: 1, title: 'title', y_label: 'MB', queries: [{ ...query1Attrs }] },
// { id: 2, title: 'title', y_label: 'MB', queries: [{ ...query2Attrs }] },
// { id: 3, title: 'new title', y_label: 'MB', queries: [{ ...query3Attrs }] }
// ]
// @return [Array<Object>]
// Ex) [
// { title: 'title', y_label: 'MB', queries: [{ metricId: 1, ...query1Attrs },
// { metricId: 2, ...query2Attrs }] },
// { title: 'new title', y_label: 'MB', queries: [{ metricId: 3, ...query3Attrs }]}
// ]
export function groupQueriesByChartInfo(metrics) {
const metricsByChart = metrics.reduce((accumulator, metric) => {
const { queries, ...chart } = metric;
const chartKey = `${chart.title}|${chart.y_label}`;
accumulator[chartKey] = accumulator[chartKey] || { ...chart, queries: [] };
queries.forEach(queryAttrs => {
let metricId;
if (chart.id) {
metricId = chart.id.toString();
} else if (queryAttrs.metric_id) {
metricId = queryAttrs.metric_id.toString();
} else {
metricId = null;
}
accumulator[chartKey].queries.push({ metricId, ...queryAttrs });
});
return accumulator;
}, {});
return Object.values(metricsByChart);
}
export const uniqMetricsId = metric => `${metric.metric_id}_${metric.id}`;
 
/**
* Not to confuse with normalizeMetrics (plural)
* Metrics loaded from project-defined dashboards do not have a metric_id.
* This method creates a unique ID combining metric_id and id, if either is present.
* This is hopefully a temporary solution until BE processes metrics before passing to fE
* @param {Object} metric - metric
* @returns {Object} - normalized metric with a uniqueID
*/
export const normalizeMetric = (metric = {}) =>
_.omit(
{
...metric,
metric_id: uniqMetricsId(metric),
metricId: uniqMetricsId(metric),
},
'id',
);
Loading
Loading
@@ -93,6 +31,11 @@ export const normalizeQueryResult = timeSeries => {
Number(value),
]),
};
// Check result for empty data
normalizedResult.values = normalizedResult.values.filter(series => {
const hasValue = d => !Number.isNaN(d[1]) && (d[1] !== null || d[1] !== undefined);
return series.find(hasValue);
});
} else if (timeSeries.value) {
normalizedResult = {
...timeSeries,
Loading
Loading
@@ -102,21 +45,3 @@ export const normalizeQueryResult = timeSeries => {
 
return normalizedResult;
};
export const normalizeMetrics = metrics => {
const groupedMetrics = groupQueriesByChartInfo(metrics);
return groupedMetrics.map(metric => {
const queries = metric.queries.map(query => ({
...query,
// custom metrics do not require a label, so we should ensure this attribute is defined
label: query.label || metric.y_label,
result: (query.result || []).map(normalizeQueryResult),
}));
return {
...metric,
queries: removeTimeSeriesNoData(queries),
};
});
};
Loading
Loading
@@ -72,10 +72,9 @@ export const ISODateToString = date => dateformat(date, dateFormats.dateTimePick
*/
export const graphDataValidatorForValues = (isValues, graphData) => {
const responseValueKeyName = isValues ? 'value' : 'values';
return (
Array.isArray(graphData.queries) &&
graphData.queries.filter(query => {
Array.isArray(graphData.metrics) &&
graphData.metrics.filter(query => {
if (Array.isArray(query.result)) {
return (
query.result.filter(res => Array.isArray(res[responseValueKeyName])).length ===
Loading
Loading
@@ -83,7 +82,7 @@ export const graphDataValidatorForValues = (isValues, graphData) => {
);
}
return false;
}).length === graphData.queries.length
}).length === graphData.metrics.filter(query => query.result).length
);
};
 
Loading
Loading
@@ -131,7 +130,7 @@ export const downloadCSVOptions = title => {
};
 
/**
* This function validates the graph data contains exactly 3 queries plus
* This function validates the graph data contains exactly 3 metrics plus
* value validations from graphDataValidatorForValues.
* @param {Object} isValues
* @param {Object} graphData the graph data response from a prometheus request
Loading
Loading
@@ -140,8 +139,8 @@ export const downloadCSVOptions = title => {
export const graphDataValidatorForAnomalyValues = graphData => {
const anomalySeriesCount = 3; // metric, upper, lower
return (
graphData.queries &&
graphData.queries.length === anomalySeriesCount &&
graphData.metrics &&
graphData.metrics.length === anomalySeriesCount &&
graphDataValidatorForValues(false, graphData)
);
};
Loading
Loading
Loading
Loading
@@ -40,15 +40,14 @@
# Any other value will be ignored.
class SnippetsFinder < UnionFinder
include FinderMethods
include Gitlab::Utils::StrongMemoize
 
attr_accessor :current_user, :project, :author, :scope, :explore
attr_accessor :current_user, :params
delegate :explore, :only_personal, :only_project, :scope, to: :params
 
def initialize(current_user = nil, params = {})
@current_user = current_user
@project = params[:project]
@author = params[:author]
@scope = params[:scope].to_s
@explore = params[:explore]
@params = OpenStruct.new(params)
 
if project && author
raise(
Loading
Loading
@@ -60,8 +59,15 @@ class SnippetsFinder < UnionFinder
end
 
def execute
base = init_collection
base.with_optional_visibility(visibility_from_scope).fresh
# The snippet query can be expensive, therefore if the
# author or project params have been passed and they don't
# exist, it's better to return
return Snippet.none if author.nil? && params[:author].present?
return Snippet.none if project.nil? && params[:project].present?
items = init_collection
items = by_ids(items)
items.with_optional_visibility(visibility_from_scope).fresh
end
 
private
Loading
Loading
@@ -69,10 +75,12 @@ class SnippetsFinder < UnionFinder
def init_collection
if explore
snippets_for_explore
elsif only_personal
personal_snippets
elsif project
snippets_for_a_single_project
else
snippets_for_multiple_projects
snippets_for_personal_and_multiple_projects
end
end
 
Loading
Loading
@@ -96,8 +104,9 @@ class SnippetsFinder < UnionFinder
#
# Each collection is constructed in isolation, allowing for greater control
# over the resulting SQL query.
def snippets_for_multiple_projects
queries = [personal_snippets]
def snippets_for_personal_and_multiple_projects
queries = []
queries << personal_snippets unless only_project
 
if Ability.allowed?(current_user, :read_cross_project)
queries << snippets_of_visible_projects
Loading
Loading
@@ -158,7 +167,7 @@ class SnippetsFinder < UnionFinder
end
 
def visibility_from_scope
case scope
case scope.to_s
when 'are_private'
Snippet::PRIVATE
when 'are_internal'
Loading
Loading
@@ -169,6 +178,28 @@ class SnippetsFinder < UnionFinder
nil
end
end
def by_ids(items)
return items unless params[:ids].present?
items.id_in(params[:ids])
end
def author
strong_memoize(:author) do
next unless params[:author].present?
params[:author].is_a?(User) ? params[:author] : User.find_by_id(params[:author])
end
end
def project
strong_memoize(:project) do
next unless params[:project].present?
params[:project].is_a?(Project) ? params[:project] : Project.find_by_id(params[:project])
end
end
end
 
SnippetsFinder.prepend_if_ee('EE::SnippetsFinder')
Loading
Loading
@@ -141,7 +141,7 @@ module BlobHelper
if @build && @entry
raw_project_job_artifacts_url(@project, @build, path: @entry.path, **kwargs)
elsif @snippet
reliable_raw_snippet_url(@snippet)
raw_snippet_url(@snippet)
elsif @blob
project_raw_url(@project, @id, **kwargs)
end
Loading
Loading
Loading
Loading
@@ -193,6 +193,101 @@ module GitlabRoutingHelper
project = schedule.project
take_ownership_project_pipeline_schedule_path(project, schedule, *args)
end
def snippet_path(snippet, *args)
if snippet.is_a?(ProjectSnippet)
application_url_helpers.project_snippet_path(snippet.project, snippet, *args)
else
new_args = snippet_query_params(snippet, *args)
application_url_helpers.snippet_path(snippet, *new_args)
end
end
def snippet_url(snippet, *args)
if snippet.is_a?(ProjectSnippet)
application_url_helpers.project_snippet_url(snippet.project, snippet, *args)
else
new_args = snippet_query_params(snippet, *args)
application_url_helpers.snippet_url(snippet, *new_args)
end
end
def raw_snippet_path(snippet, *args)
if snippet.is_a?(ProjectSnippet)
application_url_helpers.raw_project_snippet_path(snippet.project, snippet, *args)
else
new_args = snippet_query_params(snippet, *args)
application_url_helpers.raw_snippet_path(snippet, *new_args)
end
end
def raw_snippet_url(snippet, *args)
if snippet.is_a?(ProjectSnippet)
application_url_helpers.raw_project_snippet_url(snippet.project, snippet, *args)
else
new_args = snippet_query_params(snippet, *args)
application_url_helpers.raw_snippet_url(snippet, *new_args)
end
end
def snippet_notes_path(snippet, *args)
new_args = snippet_query_params(snippet, *args)
application_url_helpers.snippet_notes_path(snippet, *new_args)
end
def snippet_notes_url(snippet, *args)
new_args = snippet_query_params(snippet, *args)
application_url_helpers.snippet_notes_url(snippet, *new_args)
end
def snippet_note_path(snippet, note, *args)
new_args = snippet_query_params(snippet, *args)
application_url_helpers.snippet_note_path(snippet, note, *new_args)
end
def snippet_note_url(snippet, note, *args)
new_args = snippet_query_params(snippet, *args)
application_url_helpers.snippet_note_url(snippet, note, *new_args)
end
def toggle_award_emoji_snippet_note_path(snippet, note, *args)
new_args = snippet_query_params(snippet, *args)
application_url_helpers.toggle_award_emoji_snippet_note_path(snippet, note, *new_args)
end
def toggle_award_emoji_snippet_note_url(snippet, note, *args)
new_args = snippet_query_params(snippet, *args)
application_url_helpers.toggle_award_emoji_snippet_note_url(snippet, note, *new_args)
end
def toggle_award_emoji_snippet_path(snippet, *args)
new_args = snippet_query_params(snippet, *args)
application_url_helpers.toggle_award_emoji_snippet_path(snippet, *new_args)
end
def toggle_award_emoji_snippet_url(snippet, *args)
new_args = snippet_query_params(snippet, *args)
application_url_helpers.toggle_award_emoji_snippet_url(snippet, *new_args)
end
private
def application_url_helpers
Gitlab::Routing.url_helpers
end
def snippet_query_params(snippet, *args)
opts = case args.last
when Hash
args.pop
when ActionController::Parameters
args.pop.to_h
else
{}
end
args << opts
end
end
 
GitlabRoutingHelper.include_if_ee('EE::GitlabRoutingHelper')
Loading
Loading
@@ -11,33 +11,9 @@ module SnippetsHelper
end
end
 
def reliable_snippet_path(snippet, opts = {})
reliable_snippet_url(snippet, opts.merge(only_path: true))
end
def reliable_raw_snippet_path(snippet, opts = {})
reliable_raw_snippet_url(snippet, opts.merge(only_path: true))
end
def reliable_snippet_url(snippet, opts = {})
if snippet.project_id?
project_snippet_url(snippet.project, snippet, nil, opts)
else
snippet_url(snippet, nil, opts)
end
end
def reliable_raw_snippet_url(snippet, opts = {})
if snippet.project_id?
raw_project_snippet_url(snippet.project, snippet, nil, opts)
else
raw_snippet_url(snippet, nil, opts)
end
end
def download_raw_snippet_button(snippet)
link_to(icon('download'),
reliable_raw_snippet_path(snippet, inline: false),
raw_snippet_path(snippet, inline: false),
target: '_blank',
rel: 'noopener noreferrer',
class: "btn btn-sm has-tooltip",
Loading
Loading
@@ -133,7 +109,7 @@ module SnippetsHelper
end
 
def snippet_embed_tag(snippet)
content_tag(:script, nil, src: reliable_snippet_url(snippet, format: :js, only_path: false))
content_tag(:script, nil, src: snippet_url(snippet, format: :js))
end
 
def snippet_badge(snippet)
Loading
Loading
@@ -158,7 +134,7 @@ module SnippetsHelper
return if blob.empty? || blob.binary? || blob.stored_externally?
 
link_to(external_snippet_icon('doc-code'),
reliable_raw_snippet_url(@snippet),
raw_snippet_url(@snippet),
class: 'btn',
target: '_blank',
rel: 'noopener noreferrer',
Loading
Loading
@@ -167,7 +143,7 @@ module SnippetsHelper
 
def embedded_snippet_download_button
link_to(external_snippet_icon('download'),
reliable_raw_snippet_url(@snippet, inline: false),
raw_snippet_url(@snippet, inline: false),
class: 'btn',
target: '_blank',
title: 'Download',
Loading
Loading
Loading
Loading
@@ -464,8 +464,20 @@ class Commit
"commit:#{sha}"
end
 
def expire_note_etag_cache
super
expire_note_etag_cache_for_related_mrs
end
private
 
def expire_note_etag_cache_for_related_mrs
MergeRequest.includes(target_project: :namespace).by_commit_sha(id).find_each do |mr|
mr.expire_note_etag_cache
end
end
def commit_reference(from, referable_commit_id, full: false)
reference = project.to_reference(from, full: full)
 
Loading
Loading
# frozen_string_literal: true
class ImportFailure < ApplicationRecord
belongs_to :project
validates :project, presence: true
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment