Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-11-10 15:10:12 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-11-10 15:10:12 +0300
commit419966e5d34a98d89354ed658c33478ce02d8017 (patch)
tree6a83543e53f483b59953febc8d61a23b19dd5ccb /app
parentbfbd788e0910597b93b31020300b15952828c386 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/admin/deploy_keys/components/table.vue54
-rw-r--r--app/assets/javascripts/admin/deploy_keys/index.js23
-rw-r--r--app/assets/javascripts/chronic_duration.js386
-rw-r--r--app/assets/javascripts/pages/admin/deploy_keys/index/index.js3
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue31
-rw-r--r--app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql5
-rw-r--r--app/assets/stylesheets/utilities.scss7
-rw-r--r--app/controllers/projects/alerting/notifications_controller.rb6
-rw-r--r--app/controllers/projects/prometheus/alerts_controller.rb6
-rw-r--r--app/helpers/admin/deploy_key_helper.rb14
-rw-r--r--app/helpers/routing/pseudonymization_helper.rb15
-rw-r--r--app/serializers/alert_management/alert_entity.rb8
-rw-r--r--app/serializers/alert_management/alert_serializer.rb7
-rw-r--r--app/services/alert_management/process_prometheus_alert_service.rb7
-rw-r--r--app/services/concerns/alert_management/responses.rb26
-rw-r--r--app/services/projects/alerting/notify_service.rb15
-rw-r--r--app/services/projects/prometheus/alerts/notify_service.rb19
-rw-r--r--app/views/admin/deploy_keys/index.html.haml66
18 files changed, 611 insertions, 87 deletions
diff --git a/app/assets/javascripts/admin/deploy_keys/components/table.vue b/app/assets/javascripts/admin/deploy_keys/components/table.vue
new file mode 100644
index 00000000000..97a5a2f2f32
--- /dev/null
+++ b/app/assets/javascripts/admin/deploy_keys/components/table.vue
@@ -0,0 +1,54 @@
+<script>
+import { GlTable, GlButton } from '@gitlab/ui';
+
+import { __ } from '~/locale';
+
+export default {
+ name: 'DeployKeysTable',
+ i18n: {
+ pageTitle: __('Public deploy keys'),
+ newDeployKeyButtonText: __('New deploy key'),
+ },
+ fields: [
+ {
+ key: 'title',
+ label: __('Title'),
+ },
+ {
+ key: 'fingerprint',
+ label: __('Fingerprint'),
+ },
+ {
+ key: 'projects',
+ label: __('Projects with write access'),
+ },
+ {
+ key: 'created',
+ label: __('Created'),
+ },
+ {
+ key: 'actions',
+ label: __('Actions'),
+ },
+ ],
+ components: {
+ GlTable,
+ GlButton,
+ },
+ inject: ['editPath', 'deletePath', 'createPath', 'emptyStateSvgPath'],
+};
+</script>
+
+<template>
+ <div>
+ <div class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-py-5">
+ <h4 class="gl-m-0">
+ {{ $options.i18n.pageTitle }}
+ </h4>
+ <gl-button variant="confirm" :href="createPath">{{
+ $options.i18n.newDeployKeyButtonText
+ }}</gl-button>
+ </div>
+ <gl-table :fields="$options.fields" data-testid="deploy-keys-list" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/deploy_keys/index.js b/app/assets/javascripts/admin/deploy_keys/index.js
new file mode 100644
index 00000000000..d86de4229de
--- /dev/null
+++ b/app/assets/javascripts/admin/deploy_keys/index.js
@@ -0,0 +1,23 @@
+import Vue from 'vue';
+import DeployKeysTable from './components/table.vue';
+
+export const initAdminDeployKeysTable = () => {
+ const el = document.getElementById('js-admin-deploy-keys-table');
+
+ if (!el) return false;
+
+ const { editPath, deletePath, createPath, emptyStateSvgPath } = el.dataset;
+
+ return new Vue({
+ el,
+ provide: {
+ editPath,
+ deletePath,
+ createPath,
+ emptyStateSvgPath,
+ },
+ render(createElement) {
+ return createElement(DeployKeysTable);
+ },
+ });
+};
diff --git a/app/assets/javascripts/chronic_duration.js b/app/assets/javascripts/chronic_duration.js
new file mode 100644
index 00000000000..b595324f6e9
--- /dev/null
+++ b/app/assets/javascripts/chronic_duration.js
@@ -0,0 +1,386 @@
+/*
+ * NOTE:
+ * Changes to this file should be kept in sync with
+ * https://gitlab.com/gitlab-org/gitlab-chronic-duration/-/blob/master/lib/gitlab_chronic_duration.rb.
+ */
+
+export class DurationParseError extends Error {}
+
+// On average, there's a little over 4 weeks in month.
+const FULL_WEEKS_PER_MONTH = 4;
+
+const HOURS_PER_DAY = 24;
+const DAYS_PER_MONTH = 30;
+
+const FLOAT_MATCHER = /[0-9]*\.?[0-9]+/g;
+const DURATION_UNITS_LIST = ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'];
+
+const MAPPINGS = {
+ seconds: 'seconds',
+ second: 'seconds',
+ secs: 'seconds',
+ sec: 'seconds',
+ s: 'seconds',
+ minutes: 'minutes',
+ minute: 'minutes',
+ mins: 'minutes',
+ min: 'minutes',
+ m: 'minutes',
+ hours: 'hours',
+ hour: 'hours',
+ hrs: 'hours',
+ hr: 'hours',
+ h: 'hours',
+ days: 'days',
+ day: 'days',
+ dy: 'days',
+ d: 'days',
+ weeks: 'weeks',
+ week: 'weeks',
+ wks: 'weeks',
+ wk: 'weeks',
+ w: 'weeks',
+ months: 'months',
+ mo: 'months',
+ mos: 'months',
+ month: 'months',
+ years: 'years',
+ year: 'years',
+ yrs: 'years',
+ yr: 'years',
+ y: 'years',
+};
+
+const JOIN_WORDS = ['and', 'with', 'plus'];
+
+function convertToNumber(string) {
+ const f = parseFloat(string);
+ return f % 1 > 0 ? f : parseInt(string, 10);
+}
+
+function durationUnitsSecondsMultiplier(unit, opts) {
+ if (!DURATION_UNITS_LIST.includes(unit)) {
+ return 0;
+ }
+
+ const hoursPerDay = opts.hoursPerDay || HOURS_PER_DAY;
+ const daysPerMonth = opts.daysPerMonth || DAYS_PER_MONTH;
+ const daysPerWeek = Math.trunc(daysPerMonth / FULL_WEEKS_PER_MONTH);
+
+ switch (unit) {
+ case 'years':
+ return 31557600;
+ case 'months':
+ return 3600 * hoursPerDay * daysPerMonth;
+ case 'weeks':
+ return 3600 * hoursPerDay * daysPerWeek;
+ case 'days':
+ return 3600 * hoursPerDay;
+ case 'hours':
+ return 3600;
+ case 'minutes':
+ return 60;
+ case 'seconds':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+function calculateFromWords(string, opts) {
+ let val = 0;
+ const words = string.split(' ');
+ words.forEach((v, k) => {
+ if (v === '') {
+ return;
+ }
+ if (v.search(FLOAT_MATCHER) >= 0) {
+ val +=
+ convertToNumber(v) *
+ durationUnitsSecondsMultiplier(
+ words[parseInt(k, 10) + 1] || opts.defaultUnit || 'seconds',
+ opts,
+ );
+ }
+ });
+ return val;
+}
+
+// Parse 3:41:59 and return 3 hours 41 minutes 59 seconds
+function filterByType(string) {
+ const chronoUnitsList = DURATION_UNITS_LIST.filter((v) => v !== 'weeks');
+ if (
+ string
+ .replace(/ +/g, '')
+ .search(RegExp(`${FLOAT_MATCHER.source}(:${FLOAT_MATCHER.source})+`, 'g')) >= 0
+ ) {
+ const res = [];
+ string
+ .replace(/ +/g, '')
+ .split(':')
+ .reverse()
+ .forEach((v, k) => {
+ if (!chronoUnitsList[k]) {
+ return;
+ }
+ res.push(`${v} ${chronoUnitsList[k]}`);
+ });
+ return res.reverse().join(' ');
+ }
+ return string;
+}
+
+// Get rid of unknown words and map found
+// words to defined time units
+function filterThroughWhiteList(string, opts) {
+ const res = [];
+ string.split(' ').forEach((word) => {
+ if (word === '') {
+ return;
+ }
+ if (word.search(FLOAT_MATCHER) >= 0) {
+ res.push(word.trim());
+ return;
+ }
+ const strippedWord = word.trim().replace(/^,/g, '').replace(/,$/g, '');
+ if (MAPPINGS[strippedWord] !== undefined) {
+ res.push(MAPPINGS[strippedWord]);
+ } else if (!JOIN_WORDS.includes(strippedWord) && opts.raiseExceptions) {
+ throw new DurationParseError(
+ `An invalid word ${JSON.stringify(word)} was used in the string to be parsed.`,
+ );
+ }
+ });
+ // add '1' at front if string starts with something recognizable but not with a number, like 'day' or 'minute 30sec'
+ if (res.length > 0 && MAPPINGS[res[0]]) {
+ res.splice(0, 0, 1);
+ }
+ return res.join(' ');
+}
+
+function cleanup(string, opts) {
+ let res = string.toLowerCase();
+ /*
+ * TODO The Ruby implementation of this algorithm uses the Numerizer module,
+ * which converts strings like "forty two" to "42", but there is no
+ * JavaScript equivalent of Numerizer. Skip it for now until Numerizer is
+ * ported to JavaScript.
+ */
+ res = filterByType(res);
+ res = res
+ .replace(FLOAT_MATCHER, (n) => ` ${n} `)
+ .replace(/ +/g, ' ')
+ .trim();
+ return filterThroughWhiteList(res, opts);
+}
+
+function humanizeTimeUnit(number, unit, pluralize, keepZero) {
+ if (number === '0' && !keepZero) {
+ return null;
+ }
+ let res = number + unit;
+ // A poor man's pluralizer
+ if (number !== '1' && pluralize) {
+ res += 's';
+ }
+ return res;
+}
+
+// Given a string representation of elapsed time,
+// return an integer (or float, if fractions of a
+// second are input)
+export function parseChronicDuration(string, opts = {}) {
+ const result = calculateFromWords(cleanup(string, opts), opts);
+ return !opts.keepZero && result === 0 ? null : result;
+}
+
+// Given an integer and an optional format,
+// returns a formatted string representing elapsed time
+export function outputChronicDuration(seconds, opts = {}) {
+ const units = {
+ years: 0,
+ months: 0,
+ weeks: 0,
+ days: 0,
+ hours: 0,
+ minutes: 0,
+ seconds,
+ };
+
+ const hoursPerDay = opts.hoursPerDay || HOURS_PER_DAY;
+ const daysPerMonth = opts.daysPerMonth || DAYS_PER_MONTH;
+ const daysPerWeek = Math.trunc(daysPerMonth / FULL_WEEKS_PER_MONTH);
+
+ const decimalPlaces =
+ seconds % 1 !== 0 ? seconds.toString().split('.').reverse()[0].length : null;
+
+ const minute = 60;
+ const hour = 60 * minute;
+ const day = hoursPerDay * hour;
+ const month = daysPerMonth * day;
+ const year = 31557600;
+
+ if (units.seconds >= 31557600 && units.seconds % year < units.seconds % month) {
+ units.years = Math.trunc(units.seconds / year);
+ units.months = Math.trunc((units.seconds % year) / month);
+ units.days = Math.trunc(((units.seconds % year) % month) / day);
+ units.hours = Math.trunc((((units.seconds % year) % month) % day) / hour);
+ units.minutes = Math.trunc(((((units.seconds % year) % month) % day) % hour) / minute);
+ units.seconds = Math.trunc(((((units.seconds % year) % month) % day) % hour) % minute);
+ } else if (seconds >= 60) {
+ units.minutes = Math.trunc(seconds / 60);
+ units.seconds %= 60;
+ if (units.minutes >= 60) {
+ units.hours = Math.trunc(units.minutes / 60);
+ units.minutes = Math.trunc(units.minutes % 60);
+ if (!opts.limitToHours) {
+ if (units.hours >= hoursPerDay) {
+ units.days = Math.trunc(units.hours / hoursPerDay);
+ units.hours = Math.trunc(units.hours % hoursPerDay);
+ if (opts.weeks) {
+ if (units.days >= daysPerWeek) {
+ units.weeks = Math.trunc(units.days / daysPerWeek);
+ units.days = Math.trunc(units.days % daysPerWeek);
+ if (units.weeks >= FULL_WEEKS_PER_MONTH) {
+ units.months = Math.trunc(units.weeks / FULL_WEEKS_PER_MONTH);
+ units.weeks = Math.trunc(units.weeks % FULL_WEEKS_PER_MONTH);
+ }
+ }
+ } else if (units.days >= daysPerMonth) {
+ units.months = Math.trunc(units.days / daysPerMonth);
+ units.days = Math.trunc(units.days % daysPerMonth);
+ }
+ }
+ }
+ }
+ }
+
+ let joiner = opts.joiner || ' ';
+ let process = null;
+
+ let dividers;
+ switch (opts.format) {
+ case 'micro':
+ dividers = {
+ years: 'y',
+ months: 'mo',
+ weeks: 'w',
+ days: 'd',
+ hours: 'h',
+ minutes: 'm',
+ seconds: 's',
+ };
+ joiner = '';
+ break;
+ case 'short':
+ dividers = {
+ years: 'y',
+ months: 'mo',
+ weeks: 'w',
+ days: 'd',
+ hours: 'h',
+ minutes: 'm',
+ seconds: 's',
+ };
+ break;
+ case 'long':
+ dividers = {
+ /* eslint-disable @gitlab/require-i18n-strings */
+ years: ' year',
+ months: ' month',
+ weeks: ' week',
+ days: ' day',
+ hours: ' hour',
+ minutes: ' minute',
+ seconds: ' second',
+ /* eslint-enable @gitlab/require-i18n-strings */
+ pluralize: true,
+ };
+ break;
+ case 'chrono':
+ dividers = {
+ years: ':',
+ months: ':',
+ weeks: ':',
+ days: ':',
+ hours: ':',
+ minutes: ':',
+ seconds: ':',
+ keepZero: true,
+ };
+ process = (str) => {
+ // Pad zeros
+ // Get rid of lead off times if they are zero
+ // Get rid of lead off zero
+ // Get rid of trailing:
+ const divider = ':';
+ const processed = [];
+ str.split(divider).forEach((n) => {
+ if (n === '') {
+ return;
+ }
+ // add zeros only if n is an integer
+ if (n.search('\\.') >= 0) {
+ processed.push(
+ parseFloat(n)
+ .toFixed(decimalPlaces)
+ .padStart(3 + decimalPlaces, '0'),
+ );
+ } else {
+ processed.push(n.padStart(2, '0'));
+ }
+ });
+ return processed
+ .join(divider)
+ .replace(/^(00:)+/g, '')
+ .replace(/^0/g, '')
+ .replace(/:$/g, '');
+ };
+ joiner = '';
+ break;
+ default:
+ dividers = {
+ /* eslint-disable @gitlab/require-i18n-strings */
+ years: ' yr',
+ months: ' mo',
+ weeks: ' wk',
+ days: ' day',
+ hours: ' hr',
+ minutes: ' min',
+ seconds: ' sec',
+ /* eslint-enable @gitlab/require-i18n-strings */
+ pluralize: true,
+ };
+ break;
+ }
+
+ let result = [];
+ ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'].forEach((t) => {
+ if (t === 'weeks' && !opts.weeks) {
+ return;
+ }
+ let num = units[t];
+ if (t === 'seconds' && num % 0 !== 0) {
+ num = num.toFixed(decimalPlaces);
+ } else {
+ num = num.toString();
+ }
+ const keepZero = !dividers.keepZero && t === 'seconds' ? opts.keepZero : dividers.keepZero;
+ const humanized = humanizeTimeUnit(num, dividers[t], dividers.pluralize, keepZero);
+ if (humanized !== null) {
+ result.push(humanized);
+ }
+ });
+
+ if (opts.units) {
+ result = result.slice(0, opts.units);
+ }
+
+ result = result.join(joiner);
+
+ if (process) {
+ result = process(result);
+ }
+
+ return result.length === 0 ? null : result;
+}
diff --git a/app/assets/javascripts/pages/admin/deploy_keys/index/index.js b/app/assets/javascripts/pages/admin/deploy_keys/index/index.js
new file mode 100644
index 00000000000..1e52aa3efd8
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/deploy_keys/index/index.js
@@ -0,0 +1,3 @@
+import { initAdminDeployKeysTable } from '~/admin/deploy_keys';
+
+initAdminDeployKeysTable();
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
index 0163b8c572b..6fe1459c80c 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlIcon, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
+import { GlButton, GlIcon, GlLink, GlLoadingIcon, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { truncateSha } from '~/lib/utils/text_utility';
import { s__ } from '~/locale';
@@ -20,6 +20,7 @@ export const i18n = {
`Pipeline|Pipeline %{idStart}#%{idEnd} %{statusStart}%{statusEnd} for %{commitStart}%{commitEnd}`,
),
viewBtn: s__('Pipeline|View pipeline'),
+ viewCommit: s__('Pipeline|View commit'),
pipelineNotTriggeredMsg: s__(
'Pipeline|No pipeline was triggered for the latest changes due to the current CI/CD configuration.',
),
@@ -36,6 +37,9 @@ export default {
GlSprintf,
PipelineEditorMiniGraph,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
inject: ['projectFullPath'],
props: {
commitSha: {
@@ -60,13 +64,13 @@ export default {
};
},
update(data) {
- const { id, iid, commitPath = '', detailedStatus = {}, stages, status } =
+ const { id, iid, commit = {}, detailedStatus = {}, stages, status } =
data.project?.pipeline || {};
return {
id,
iid,
- commitPath,
+ commit,
detailedStatus,
stages,
status,
@@ -95,6 +99,16 @@ export default {
};
},
computed: {
+ commitText() {
+ const shortSha = truncateSha(this.commitSha);
+ const commitTitle = this.pipeline.commit.title || '';
+
+ if (commitTitle.length > 0) {
+ return `${shortSha}: ${commitTitle}`;
+ }
+
+ return shortSha;
+ },
hasPipelineData() {
return Boolean(this.pipeline?.id);
},
@@ -146,7 +160,7 @@ export default {
</div>
</template>
<template v-else>
- <div>
+ <div class="gl-text-truncate gl-md-max-w-50p gl-mr-1">
<a :href="status.detailsPath" class="gl-mr-auto">
<ci-icon :status="status" :size="16" data-testid="pipeline-status-icon" />
</a>
@@ -158,12 +172,12 @@ export default {
<template #status>{{ status.text }}</template>
<template #commit>
<gl-link
- :href="pipeline.commitPath"
- class="commit-sha gl-font-weight-normal"
- target="_blank"
+ v-gl-tooltip.hover
+ :href="pipeline.commit.webPath"
+ :title="$options.i18n.viewCommit"
data-testid="pipeline-commit"
>
- {{ shortSha }}
+ {{ commitText }}
</gl-link>
</template>
</gl-sprintf>
@@ -173,7 +187,6 @@ export default {
<pipeline-editor-mini-graph :pipeline="pipeline" v-on="$listeners" />
<gl-button
class="gl-mt-2 gl-md-mt-0"
- target="_blank"
category="secondary"
variant="confirm"
:href="status.detailsPath"
diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql
index 0c3653a2880..34e98ae3eb3 100644
--- a/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql
+++ b/app/assets/javascripts/pipeline_editor/graphql/queries/client/pipeline.graphql
@@ -1,10 +1,13 @@
query getPipeline($fullPath: ID!, $sha: String!) {
project(fullPath: $fullPath) {
pipeline(sha: $sha) {
- commitPath
id
iid
status
+ commit {
+ title
+ webPath
+ }
detailedStatus {
detailsPath
icon
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index fbcc97d6750..7e46f16e1d0 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -290,3 +290,10 @@ $gl-line-height-42: px-to-rem(42px);
.gl-focus-ring-border-1-gray-900\! {
@include gl-focus($gl-border-size-1, $gray-900, true);
}
+
+// Will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/2476
+.gl-md-max-w-50p {
+ @include gl-media-breakpoint-up(md) {
+ max-width: 50%;
+ }
+}
diff --git a/app/controllers/projects/alerting/notifications_controller.rb b/app/controllers/projects/alerting/notifications_controller.rb
index 95b403faf55..ae8498ce65f 100644
--- a/app/controllers/projects/alerting/notifications_controller.rb
+++ b/app/controllers/projects/alerting/notifications_controller.rb
@@ -18,7 +18,11 @@ module Projects
token = extract_alert_manager_token(request)
result = notify_service.execute(token, integration)
- head result.http_status
+ if result.success?
+ render json: AlertManagement::AlertSerializer.new.represent(result.payload[:alerts]), code: result.http_status
+ else
+ head result.http_status
+ end
end
private
diff --git a/app/controllers/projects/prometheus/alerts_controller.rb b/app/controllers/projects/prometheus/alerts_controller.rb
index 19c908026cf..312919831d4 100644
--- a/app/controllers/projects/prometheus/alerts_controller.rb
+++ b/app/controllers/projects/prometheus/alerts_controller.rb
@@ -30,7 +30,11 @@ module Projects
token = extract_alert_manager_token(request)
result = notify_service.execute(token)
- head result.http_status
+ if result.success?
+ render json: AlertManagement::AlertSerializer.new.represent(result.payload[:alerts]), code: result.http_status
+ else
+ head result.http_status
+ end
end
def create
diff --git a/app/helpers/admin/deploy_key_helper.rb b/app/helpers/admin/deploy_key_helper.rb
new file mode 100644
index 00000000000..caf3757a68e
--- /dev/null
+++ b/app/helpers/admin/deploy_key_helper.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Admin
+ module DeployKeyHelper
+ def admin_deploy_keys_data
+ {
+ edit_path: edit_admin_deploy_key_path(':id'),
+ delete_path: admin_deploy_key_path(':id'),
+ create_path: new_admin_deploy_key_path,
+ empty_state_svg_path: image_path('illustrations/empty-state/empty-deploy-keys-lg.svg')
+ }
+ end
+ end
+end
diff --git a/app/helpers/routing/pseudonymization_helper.rb b/app/helpers/routing/pseudonymization_helper.rb
index dee202d3785..58cbf8dbdea 100644
--- a/app/helpers/routing/pseudonymization_helper.rb
+++ b/app/helpers/routing/pseudonymization_helper.rb
@@ -31,24 +31,11 @@ module Routing
end
end
- generate_url(masked_params.merge(params: masked_query_params))
+ Gitlab::Routing.url_helpers.url_for(masked_params.merge(params: masked_query_params))
end
private
- def generate_url(masked_params)
- # The below check is added since `project/insights` route does not
- # work with Rails router `url_for` method.
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/343551
- if @request.path_parameters[:controller] == 'projects/insights'
- default_root_url + "#{Gitlab::Routing.url_helpers.namespace_project_insights_path(masked_params)}"
- elsif @request.path_parameters[:controller] == 'groups/insights'
- default_root_url + "#{Gitlab::Routing.url_helpers.group_insights_path(masked_params)}"
- else
- Gitlab::Routing.url_helpers.url_for(masked_params)
- end
- end
-
def mask_id(value)
if @request.path_parameters[:controller] == 'projects/blob'
':repository_path'
diff --git a/app/serializers/alert_management/alert_entity.rb b/app/serializers/alert_management/alert_entity.rb
new file mode 100644
index 00000000000..6871da44887
--- /dev/null
+++ b/app/serializers/alert_management/alert_entity.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module AlertManagement
+ class AlertEntity < Grape::Entity
+ expose :iid
+ expose :title
+ end
+end
diff --git a/app/serializers/alert_management/alert_serializer.rb b/app/serializers/alert_management/alert_serializer.rb
new file mode 100644
index 00000000000..89815bf6510
--- /dev/null
+++ b/app/serializers/alert_management/alert_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module AlertManagement
+ class AlertSerializer < BaseSerializer
+ entity AlertManagement::AlertEntity
+ end
+end
diff --git a/app/services/alert_management/process_prometheus_alert_service.rb b/app/services/alert_management/process_prometheus_alert_service.rb
index 605ab7a1869..1b377a3d367 100644
--- a/app/services/alert_management/process_prometheus_alert_service.rb
+++ b/app/services/alert_management/process_prometheus_alert_service.rb
@@ -4,6 +4,7 @@ module AlertManagement
class ProcessPrometheusAlertService
extend ::Gitlab::Utils::Override
include ::AlertManagement::AlertProcessing
+ include ::AlertManagement::Responses
def initialize(project, payload)
@project = project
@@ -18,7 +19,7 @@ module AlertManagement
complete_post_processing_tasks
- ServiceResponse.success
+ success(alert)
end
private
@@ -40,9 +41,5 @@ module AlertManagement
def resolving_alert?
incoming_payload.resolved?
end
-
- def bad_request
- ServiceResponse.error(message: 'Bad Request', http_status: :bad_request)
- end
end
end
diff --git a/app/services/concerns/alert_management/responses.rb b/app/services/concerns/alert_management/responses.rb
new file mode 100644
index 00000000000..183a831a00a
--- /dev/null
+++ b/app/services/concerns/alert_management/responses.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module AlertManagement
+ # Module to hold common response logic for AlertManagement services.
+ module Responses
+ def success(alerts)
+ ServiceResponse.success(payload: { alerts: Array(alerts) })
+ end
+
+ def bad_request
+ ServiceResponse.error(message: 'Bad Request', http_status: :bad_request)
+ end
+
+ def unauthorized
+ ServiceResponse.error(message: 'Unauthorized', http_status: :unauthorized)
+ end
+
+ def unprocessable_entity
+ ServiceResponse.error(message: 'Unprocessable Entity', http_status: :unprocessable_entity)
+ end
+
+ def forbidden
+ ServiceResponse.error(message: 'Forbidden', http_status: :forbidden)
+ end
+ end
+end
diff --git a/app/services/projects/alerting/notify_service.rb b/app/services/projects/alerting/notify_service.rb
index a5ee7173bdf..e5d40b60747 100644
--- a/app/services/projects/alerting/notify_service.rb
+++ b/app/services/projects/alerting/notify_service.rb
@@ -5,6 +5,7 @@ module Projects
class NotifyService
extend ::Gitlab::Utils::Override
include ::AlertManagement::AlertProcessing
+ include ::AlertManagement::Responses
def initialize(project, payload)
@project = project
@@ -23,7 +24,7 @@ module Projects
complete_post_processing_tasks
- ServiceResponse.success
+ success(alert)
end
private
@@ -46,18 +47,6 @@ module Projects
def valid_token?(token)
token == integration.token
end
-
- def bad_request
- ServiceResponse.error(message: 'Bad Request', http_status: :bad_request)
- end
-
- def unauthorized
- ServiceResponse.error(message: 'Unauthorized', http_status: :unauthorized)
- end
-
- def forbidden
- ServiceResponse.error(message: 'Forbidden', http_status: :forbidden)
- end
end
end
end
diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb
index c1bf2e68436..56f65718d24 100644
--- a/app/services/projects/prometheus/alerts/notify_service.rb
+++ b/app/services/projects/prometheus/alerts/notify_service.rb
@@ -6,6 +6,7 @@ module Projects
class NotifyService
include Gitlab::Utils::StrongMemoize
include ::IncidentManagement::Settings
+ include ::AlertManagement::Responses
# This set of keys identifies a payload as a valid Prometheus
# payload and thus processable by this service. See also
@@ -27,9 +28,9 @@ module Projects
return unprocessable_entity unless self.class.processable?(payload)
return unauthorized unless valid_alert_manager_token?(token, integration)
- process_prometheus_alerts
+ alert_responses = process_prometheus_alerts
- ServiceResponse.success
+ alert_response(alert_responses)
end
def self.processable?(payload)
@@ -128,23 +129,17 @@ module Projects
end
def process_prometheus_alerts
- alerts.each do |alert|
+ alerts.map do |alert|
AlertManagement::ProcessPrometheusAlertService
.new(project, alert.to_h)
.execute
end
end
- def bad_request
- ServiceResponse.error(message: 'Bad Request', http_status: :bad_request)
- end
-
- def unauthorized
- ServiceResponse.error(message: 'Unauthorized', http_status: :unauthorized)
- end
+ def alert_response(alert_responses)
+ alerts = alert_responses.map { |resp| resp.payload[:alert] }.compact
- def unprocessable_entity
- ServiceResponse.error(message: 'Unprocessable Entity', http_status: :unprocessable_entity)
+ success(alerts)
end
end
end
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index eec8f816f04..1cbb5296f8d 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -1,33 +1,37 @@
- page_title _('Deploy Keys')
-- if @deploy_keys.any?
- %h3.page-title.deploy-keys-title
- = _('Public deploy keys (%{deploy_keys_count})') % { deploy_keys_count: @deploy_keys.load.size }
- = link_to _('New deploy key'), new_admin_deploy_key_path, class: 'float-right btn gl-button btn-confirm btn-md gl-button'
- .table-holder.deploy-keys-list
- %table.table
- %thead
- %tr
- %th.col-sm-2= _('Title')
- %th.col-sm-4= _('Fingerprint')
- %th.col-sm-2= _('Projects with write access')
- %th.col-sm-2= _('Added at')
- %th.col-sm-2
- %tbody
- - @deploy_keys.each do |deploy_key|
- %tr
- %td
- %strong= deploy_key.title
- %td
- %code.key-fingerprint= deploy_key.fingerprint
- %td
- - deploy_key.projects_with_write_access.each do |project|
- = link_to project.full_name, admin_project_path(project), class: 'label deploy-project-label'
- %td
- %span.cgray
- = _('added %{created_at_timeago}').html_safe % { created_at_timeago: time_ago_with_tooltip(deploy_key.created_at) }
- %td
- .float-right
- = link_to _('Edit'), edit_admin_deploy_key_path(deploy_key), class: 'btn gl-button btn-sm'
- = link_to _('Remove'), admin_deploy_key_path(deploy_key), data: { confirm: _('Are you sure?') }, method: :delete, class: 'gl-button btn btn-sm btn-danger delete-key'
+
+- if Feature.enabled?(:admin_deploy_keys_vue, default_enabled: :yaml)
+ #js-admin-deploy-keys-table{ data: admin_deploy_keys_data }
- else
- = render 'shared/empty_states/deploy_keys'
+ - if @deploy_keys.any?
+ %h3.page-title.deploy-keys-title
+ = _('Public deploy keys (%{deploy_keys_count})') % { deploy_keys_count: @deploy_keys.load.size }
+ = link_to _('New deploy key'), new_admin_deploy_key_path, class: 'float-right btn gl-button btn-confirm btn-md gl-button'
+ .table-holder.deploy-keys-list
+ %table.table
+ %thead
+ %tr
+ %th.col-sm-2= _('Title')
+ %th.col-sm-4= _('Fingerprint')
+ %th.col-sm-2= _('Projects with write access')
+ %th.col-sm-2= _('Added at')
+ %th.col-sm-2
+ %tbody
+ - @deploy_keys.each do |deploy_key|
+ %tr
+ %td
+ %strong= deploy_key.title
+ %td
+ %code.key-fingerprint= deploy_key.fingerprint
+ %td
+ - deploy_key.projects_with_write_access.each do |project|
+ = link_to project.full_name, admin_project_path(project), class: 'label deploy-project-label'
+ %td
+ %span.cgray
+ = _('added %{created_at_timeago}').html_safe % { created_at_timeago: time_ago_with_tooltip(deploy_key.created_at) }
+ %td
+ .float-right
+ = link_to _('Edit'), edit_admin_deploy_key_path(deploy_key), class: 'btn gl-button btn-sm'
+ = link_to _('Remove'), admin_deploy_key_path(deploy_key), data: { confirm: _('Are you sure?') }, method: :delete, class: 'gl-button btn btn-sm btn-danger delete-key'
+ - else
+ = render 'shared/empty_states/deploy_keys'