Commit 3fd97b4b authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent c5223939
......@@ -8,6 +8,7 @@
# Technical writing team are the default reviewers for all markdown docs
*.md @gl-docsteam
/doc/ @gl-docsteam
# Frontend maintainers should see everything in `app/assets/`
*.scss @annabeldunstone @gitlab-org/maintainers/frontend
......
......@@ -495,6 +495,10 @@ export default class LabelsSelect {
].join(''),
);
const rightLabelTextColor = ({ label, escapeStr }) => {
return escapeStr(label.text_color === '#FFFFFF' ? label.color : label.text_color);
};
const infoIconTemplate = _.template(
[
'<a href="<%= scopedLabelsDocumentationLink %>" class="gl-link gl-label-icon" target="_blank" rel="noopener">',
......@@ -510,7 +514,7 @@ export default class LabelsSelect {
spanOpenTag,
'<%- label.title.slice(0, label.title.lastIndexOf("::")) %>',
'</span>',
'<span class="gl-label-text" style="color: <%= escapeStr(label.color) %>;">',
'<span class="gl-label-text" style="color: <%= rightLabelTextColor({ label, escapeStr }) %>;">',
'<%- label.title.slice(label.title.lastIndexOf("::") + 2) %>',
'</span>',
'</a>',
......@@ -536,7 +540,7 @@ export default class LabelsSelect {
'<% _.each(labels, function(label){ %>',
'<% if (isScopedLabel(label) && enableScopedLabels) { %>',
'<span class="d-inline-block position-relative scoped-label-wrapper">',
'<%= scopedLabelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, infoIconTemplate, scopedLabelsDocumentationLink, tooltipTitleTemplate, escapeStr, linkAttrs: \'data-html="true"\' }) %>',
'<%= scopedLabelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, rightLabelTextColor, infoIconTemplate, scopedLabelsDocumentationLink, tooltipTitleTemplate, escapeStr, linkAttrs: \'data-html="true"\' }) %>',
'</span>',
'<% } else { %>',
'<%= labelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, tooltipTitleTemplate, escapeStr, linkAttrs: "" }) %>',
......@@ -548,6 +552,7 @@ export default class LabelsSelect {
return tpl({
...tplData,
labelTemplate,
rightLabelTextColor,
infoIconTemplate,
scopedLabelTemplate,
tooltipTitleTemplate,
......
......@@ -18,14 +18,23 @@ export default {
unavailableFeatureText: s__(
'ContainerRegistry|Currently, the Container Registry tag expiration feature is not available for projects created before GitLab version 12.8. For updates and more information, visit Issue %{linkStart}#196124%{linkEnd}',
),
fetchSettingsErrorText: FETCH_SETTINGS_ERROR_MESSAGE,
},
data() {
return {
fetchSettingsError: false,
};
},
computed: {
...mapState(['isDisabled']),
showSettingForm() {
return !this.isDisabled && !this.fetchSettingsError;
},
},
mounted() {
this.fetchSettings().catch(() =>
this.$toast.show(FETCH_SETTINGS_ERROR_MESSAGE, { type: 'error' }),
);
this.fetchSettings().catch(() => {
this.fetchSettingsError = true;
});
},
methods: {
...mapActions(['fetchSettings']),
......@@ -48,17 +57,22 @@ export default {
}}
</li>
</ul>
<settings-form v-if="!isDisabled" />
<gl-alert v-else :dismissible="false">
<p>
<gl-sprintf :message="$options.i18n.unavailableFeatureText">
<template #link="{content}">
<gl-link href="https://gitlab.com/gitlab-org/gitlab/issues/196124" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</p>
</gl-alert>
<settings-form v-if="showSettingForm" />
<template v-else>
<gl-alert v-if="isDisabled" :dismissible="false">
<p>
<gl-sprintf :message="$options.i18n.unavailableFeatureText">
<template #link="{content}">
<gl-link href="https://gitlab.com/gitlab-org/gitlab/issues/196124" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</p>
</gl-alert>
<gl-alert v-else-if="fetchSettingsError" variant="warning" :dismissible="false">
<gl-sprintf :message="$options.i18n.fetchSettingsErrorText" />
</gl-alert>
</template>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import { GlLink, GlLoadingIcon } from '@gitlab/ui';
import { sprintf, n__, s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
......@@ -10,6 +10,7 @@ export default {
name: 'RelatedMergeRequests',
components: {
Icon,
GlLink,
GlLoadingIcon,
RelatedIssuableItem,
},
......@@ -64,10 +65,19 @@ export default {
</script>
<template>
<div v-if="isFetchingMergeRequests || (!isFetchingMergeRequests && totalCount)">
<div
v-if="isFetchingMergeRequests || (!isFetchingMergeRequests && totalCount)"
id="related-merge-requests"
>
<div id="merge-requests" class="card card-slim mt-3">
<div class="card-header">
<div class="card-title mt-0 mb-0 h5 merge-requests-title">
<div class="card-title mt-0 mb-0 h5 merge-requests-title position-relative">
<gl-link
id="user-content-related-merge-requests"
class="anchor position-absolute text-decoration-none"
href="#related-merge-requests"
aria-hidden="true"
/>
<span class="mr-1">
{{ __('Related merge requests') }}
</span>
......
......@@ -158,10 +158,6 @@
a:not(.btn) {
color: inherit;
.gl-label-text:hover {
color: inherit;
}
&:hover {
color: $blue-800;
......
......@@ -284,3 +284,22 @@ ul.related-merge-requests > li {
text-align: right;
}
}
.issue-details {
.card-title {
a.anchor {
left: -16px;
top: 4px;
outline: none;
&::after {
content: image-url('icon_anchor.svg');
@include invisible(hidden);
}
}
&:hover > a.anchor::after {
@include invisible(visible);
}
}
}
......@@ -23,4 +23,10 @@ class ZoomMeeting < ApplicationRecord
def self.canonical_meeting_url(issue)
canonical_meeting(issue)&.url
end
def self.distinct_count_by(column = nil, fallback = -1)
distinct.count(column)
rescue ActiveRecord::StatementInvalid
fallback
end
end
---
title: Container expiration policy settings hide form on API error
merge_request: 26303
author:
type: fixed
---
title: Add anchor tags to related issues and related merge requests.
merge_request: 26756
author: Gilang Gumilar
type: added
This diff is collapsed.
......@@ -84,7 +84,7 @@ module Gitlab
issues: count(Issue),
issues_created_from_gitlab_error_tracking_ui: count(SentryIssue),
issues_with_associated_zoom_link: count(ZoomMeeting.added_to_issue),
issues_using_zoom_quick_actions: count(ZoomMeeting.select(:issue_id).distinct, batch: false),
issues_using_zoom_quick_actions: distinct_count(ZoomMeeting, :issue_id),
issues_with_embedded_grafana_charts_approx: ::Gitlab::GrafanaEmbedUsageData.issue_count,
incident_issues: count(::Issue.authored(::User.alert_bot)),
keys: count(Key),
......
......@@ -20484,31 +20484,40 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
msgid "ThreatMonitoring|A Web Application Firewall (WAF) provides monitoring and rules to protect production applications. GitLab adds the modsecurity WAF plug-in when you install the Ingress app in your Kubernetes cluster."
msgstr ""
msgid "ThreatMonitoring|Anomalous Requests"
msgstr ""
msgid "ThreatMonitoring|At this time, threat monitoring only supports WAF data."
msgstr ""
msgid "ThreatMonitoring|Container Network Policy"
msgstr ""
msgid "ThreatMonitoring|Dropped Packets"
msgstr ""
msgid "ThreatMonitoring|Environment"
msgstr ""
msgid "ThreatMonitoring|No traffic to display"
msgstr ""
msgid "ThreatMonitoring|Operations Per Second"
msgstr ""
msgid "ThreatMonitoring|Packet Activity"
msgstr ""
msgid "ThreatMonitoring|Requests"
msgstr ""
msgid "ThreatMonitoring|Show last"
msgstr ""
msgid "ThreatMonitoring|Something went wrong, unable to fetch WAF statistics"
msgid "ThreatMonitoring|Something went wrong, unable to fetch environments"
msgstr ""
msgid "ThreatMonitoring|Something went wrong, unable to fetch environments"
msgid "ThreatMonitoring|Something went wrong, unable to fetch statistics"
msgstr ""
msgid "ThreatMonitoring|The graph below is an overview of traffic coming to your application as tracked by the Web Application Firewall (WAF). View the docs for instructions on how to access the WAF logs to see what type of malicious traffic is trying to access your app. The docs link is also accessible by clicking the \"?\" icon next to the title below."
......@@ -20520,13 +20529,25 @@ msgstr ""
msgid "ThreatMonitoring|Threat Monitoring help page link"
msgstr ""
msgid "ThreatMonitoring|Threat monitoring is not enabled"
msgstr ""
msgid "ThreatMonitoring|Threat monitoring provides security monitoring and rules to protect production applications."
msgstr ""
msgid "ThreatMonitoring|Time"
msgstr ""
msgid "ThreatMonitoring|Total Packets"
msgstr ""
msgid "ThreatMonitoring|Total Requests"
msgstr ""
msgid "ThreatMonitoring|Web Application Firewall not enabled"
msgid "ThreatMonitoring|Web Application Firewall"
msgstr ""
msgid "ThreatMonitoring|While it's rare to have no traffic coming to your application, it can happen. In any event, we ask that you double check your settings to make sure you've set up the Network Policies correctly."
msgstr ""
msgid "ThreatMonitoring|While it's rare to have no traffic coming to your application, it can happen. In any event, we ask that you double check your settings to make sure you've set up the WAF correctly."
......
......@@ -23,6 +23,16 @@ const mockScopedLabels = [
},
];
const mockScopedLabels2 = [
{
id: 28,
title: 'Foo::Bar2',
description: 'Foobar2',
color: '#FFFFFF',
text_color: '#000000',
},
];
describe('LabelsSelect', () => {
describe('getLabelTemplate', () => {
describe('when normal label is present', () => {
......@@ -108,11 +118,45 @@ describe('LabelsSelect', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
`background-color: ${label.color}; color: ${label.text_color};`,
);
expect(
$labelEl
.find('span.gl-label-text')
.last()
.attr('style'),
).toBe(`color: ${label.color};`);
});
it('generated label item has a badge class', () => {
expect($labelEl.find('span').hasClass('gl-label-text')).toEqual(true);
});
});
describe('when scoped label is present, with text color not white', () => {
const label = mockScopedLabels2[0];
let $labelEl;
beforeEach(() => {
$labelEl = $(
LabelsSelect.getLabelTemplate({
labels: mockScopedLabels2,
issueUpdateURL: mockUrl,
enableScopedLabels: true,
scopedLabelsDocumentationLink: 'docs-link',
}),
);
});
it('generated label item template has correct label styles', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
`background-color: ${label.color}; color: ${label.text_color};`,
);
expect(
$labelEl
.find('span.gl-label-text')
.last()
.attr('style'),
).toBe(`color: ${label.text_color};`);
});
});
});
});
......@@ -44,15 +44,6 @@ describe('Registry Settings App', () => {
expect(store.dispatch).toHaveBeenCalledWith('fetchSettings');
});
it('show a toast if fetchSettings fails', () => {
mountComponent({ dispatchMock: 'mockRejectedValue' });
return wrapper.vm.$nextTick().then(() =>
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(FETCH_SETTINGS_ERROR_MESSAGE, {
type: 'error',
}),
);
});
it('renders the setting form', () => {
mountComponent();
expect(findSettingsComponent().exists()).toBe(true);
......@@ -68,7 +59,23 @@ describe('Registry Settings App', () => {
});
it('shows an alert', () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().html()).toContain(
'Currently, the Container Registry tag expiration feature is not available',
);
});
});
describe('fetchSettingsError', () => {
beforeEach(() => {
mountComponent({ dispatchMock: 'mockRejectedValue' });
});
it('the form is hidden', () => {
expect(findSettingsComponent().exists()).toBe(false);
});
it('shows an alert', () => {
expect(findAlert().html()).toContain(FETCH_SETTINGS_ERROR_MESSAGE);
});
});
});
......@@ -151,4 +151,51 @@ describe ZoomMeeting do
it_behaves_like 'can remove meetings'
end
end
describe '.distinct_count_by' do
let(:issue_1) { create(:issue) }
let(:issue_2) { create(:issue) }
context 'two meetings for the same issue' do
before do
create(:zoom_meeting, issue: issue_1)
create(:zoom_meeting, :removed_from_issue, issue: issue_1)
end
it 'returns a count of 1' do
expect(described_class.distinct_count_by(:issue_id)).to eq(1)
end
context 'when given no colum to count' do
it 'counts by :id and returns a count of 2' do
expect(described_class.distinct_count_by).to eq(2)
end
end
end
context 'one meeting for each issue' do
it 'returns a count of 2' do
create(:zoom_meeting, issue: issue_1)
create(:zoom_meeting, issue: issue_2)
expect(described_class.distinct_count_by(:issue_id)).to eq(2)
end
end
context 'the count query times out' do
before do
allow_next_instance_of(ActiveRecord::Relation) do |instance|
allow(instance).to receive(:count).and_raise(ActiveRecord::StatementInvalid.new(''))
end
end
it 'does not raise an error' do
expect { described_class.distinct_count_by(:issue_id) }.not_to raise_error
end
it 'returns -1' do
expect(described_class.distinct_count_by(:issue_id)).to eq(-1)
end
end
end
end
......@@ -796,15 +796,15 @@
dependencies:
vue-eslint-parser "^7.0.0"
"@gitlab/svgs@^1.110.0":
version "1.110.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.110.0.tgz#3c4f5f0e78fcf616ec63a265754158b84ed80af8"
integrity sha512-bLVUW9Hj6j7zTdeoQELO3Bls5xDKr6AoSEU8gZbEZKLK9PV81hxRl/lJPJUo1qt4E7eJGapCTlH73tTIL4OZ3A==
"@gitlab/ui@^9.23.1":
version "9.23.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.23.1.tgz#791d0c8a6762b1dd73ed686326c1dfb3f0c7b987"
integrity sha512-7bGcV2W6qh/KK423W/vasv+S6myWJMD1tyMr5MBz1WQRg/B3eUlpr4HbjQXmtALRWiWkag8GMI/HSy0rby4WrA==
"@gitlab/svgs@^1.111.0":
version "1.111.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.111.0.tgz#10c0ba2fef9351a4d5e1f939994e39ebe5cba909"
integrity sha512-r+PMe4o7F9HAof+J9eF4aR/J068ZywAQRHBA+xU8TdyqX9gtDdBsvHBiMT65s8gr6MgLhduc3N4YlPwZua2QNQ==
"@gitlab/ui@^9.28.0":
version "9.28.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.28.0.tgz#fe307f7fdd9dc203078efb6f4d0590e0b1cb9053"
integrity sha512-de2zApLY5dD7YhguLAb/6mDROFMoA0zQ1euiGhBoVbaUwzNwSP1gremE186uPd6uGs9ZKfSp3on/qQYhXrNy9w==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
......
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