summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-12-04 09:06:33 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-12-04 09:06:33 +0000
commitc4038d4bdff93b260cbdcd69f9a6c0b07a849457 (patch)
tree7b74ce5a60a97324dec5c61ac477ca74e3db36e9
parentd07169c8ae0ebad0f23d03f01aeaf9acfa7e02d1 (diff)
downloadgitlab-ce-c4038d4bdff93b260cbdcd69f9a6c0b07a849457.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/error_tracking/components/error_details.vue80
-rw-r--r--app/assets/javascripts/error_tracking/details.js6
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue15
-rw-r--r--app/helpers/projects/error_tracking_helper.rb2
-rw-r--r--changelogs/unreleased/Replace-BoardService_in_list_spec-js.yml5
-rw-r--r--changelogs/unreleased/lm-create-issues-from-sentry-details-page.yml5
-rw-r--r--doc/integration/jenkins.md29
-rw-r--r--locale/gitlab.pot28
-rw-r--r--spec/frontend/error_tracking/components/error_details_spec.js70
-rw-r--r--spec/helpers/projects/error_tracking_helper_spec.rb6
10 files changed, 151 insertions, 95 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue
index 2000377530b..21f487f09f7 100644
--- a/app/assets/javascripts/error_tracking/components/error_details.vue
+++ b/app/assets/javascripts/error_tracking/components/error_details.vue
@@ -1,8 +1,9 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import dateFormat from 'dateformat';
-import { __, sprintf } from '~/locale';
-import { GlButton, GlLink, GlLoadingIcon } from '@gitlab/ui';
+import { GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui';
+import { __, sprintf, n__ } from '~/locale';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import Stacktrace from './stacktrace.vue';
@@ -12,7 +13,8 @@ import { trackClickErrorLinkToSentryOptions } from '../utils';
export default {
components: {
- GlButton,
+ LoadingButton,
+ GlFormInput,
GlLink,
GlLoadingIcon,
TooltipOnTruncate,
@@ -32,10 +34,19 @@ export default {
type: String,
required: true,
},
- issueProjectPath: {
+ projectIssuesPath: {
type: String,
required: true,
},
+ csrfToken: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ issueCreationInProgress: false,
+ };
},
computed: {
...mapState('details', ['error', 'loading', 'loadingStacktrace', 'stacktraceData']),
@@ -62,33 +73,26 @@ export default {
showStacktrace() {
return Boolean(!this.loadingStacktrace && this.stacktrace && this.stacktrace.length);
},
- errorTitle() {
- return `${this.error.title}`;
- },
- errorUrl() {
- return sprintf(__('Sentry event: %{external_url}'), {
- external_url: this.error.external_url,
- });
- },
- errorFirstSeen() {
- return sprintf(__('First seen: %{first_seen}'), { first_seen: this.error.first_seen });
- },
- errorLastSeen() {
- return sprintf(__('Last seen: %{last_seen}'), { last_seen: this.error.last_seen });
- },
- errorCount() {
- return sprintf(__('Events: %{count}'), { count: this.error.count });
- },
- errorUserCount() {
- return sprintf(__('Users: %{user_count}'), { user_count: this.error.user_count });
- },
- issueLink() {
- return `${this.issueProjectPath}?issue[title]=${encodeURIComponent(
- this.errorTitle,
- )}&issue[description]=${encodeURIComponent(this.issueDescription)}`;
+ issueTitle() {
+ return this.error.title;
},
issueDescription() {
- return `${this.errorUrl}${this.errorFirstSeen}${this.errorLastSeen}${this.errorCount}${this.errorUserCount}`;
+ return sprintf(
+ __(
+ '%{description}- Sentry event: %{errorUrl}- First seen: %{firstSeen}- Last seen: %{lastSeen} %{countLabel}: %{count}%{userCountLabel}: %{userCount}',
+ ),
+ {
+ description: '# Error Details:\n',
+ errorUrl: `${this.error.external_url}\n`,
+ firstSeen: `\n${this.error.first_seen}\n`,
+ lastSeen: `${this.error.last_seen}\n`,
+ countLabel: n__('- Event', '- Events', this.error.count),
+ count: `${this.error.count}\n`,
+ userCountLabel: n__('- User', '- Users', this.error.user_count),
+ userCount: `${this.error.user_count}\n`,
+ },
+ false,
+ );
},
},
mounted() {
@@ -98,6 +102,10 @@ export default {
methods: {
...mapActions('details', ['startPollingDetails', 'startPollingStacktrace']),
trackClickErrorLinkToSentryOptions,
+ createIssue() {
+ this.issueCreationInProgress = true;
+ this.$refs.sentryIssueForm.submit();
+ },
formatDate(date) {
return `${this.timeFormated(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
},
@@ -114,9 +122,17 @@ export default {
<div v-else-if="showDetails" class="error-details">
<div class="top-area align-items-center justify-content-between py-3">
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
- <gl-button variant="success" :href="issueLink">
- {{ __('Create issue') }}
- </gl-button>
+ <form ref="sentryIssueForm" :action="projectIssuesPath" method="POST">
+ <gl-form-input class="hidden" name="issue[title]" :value="issueTitle" />
+ <input name="issue[description]" :value="issueDescription" type="hidden" />
+ <gl-form-input :value="csrfToken" class="hidden" name="authenticity_token" />
+ <loading-button
+ class="btn-success"
+ :label="__('Create issue')"
+ :loading="issueCreationInProgress"
+ @click="createIssue"
+ />
+ </form>
</div>
<div>
<tooltip-on-truncate :title="error.title" truncate-target="child" placement="top">
diff --git a/app/assets/javascripts/error_tracking/details.js b/app/assets/javascripts/error_tracking/details.js
index 435315842bd..872cb8868a2 100644
--- a/app/assets/javascripts/error_tracking/details.js
+++ b/app/assets/javascripts/error_tracking/details.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import store from './store';
import ErrorDetails from './components/error_details.vue';
+import csrf from '~/lib/utils/csrf';
export default () => {
// eslint-disable-next-line no-new
@@ -12,13 +13,14 @@ export default () => {
store,
render(createElement) {
const domEl = document.querySelector(this.$options.el);
- const { issueDetailsPath, issueStackTracePath, issueProjectPath } = domEl.dataset;
+ const { issueDetailsPath, issueStackTracePath, projectIssuesPath } = domEl.dataset;
return createElement('error-details', {
props: {
issueDetailsPath,
issueStackTracePath,
- issueProjectPath,
+ projectIssuesPath,
+ csrfToken: csrf.token,
},
});
},
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index c08b471bd51..9e376a52702 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -252,23 +252,18 @@ export default {
'setEndpoints',
'setPanelGroupMetrics',
]),
- updateMetrics(key, panels) {
+ updatePanels(key, panels) {
this.setPanelGroupMetrics({
panels,
key,
});
},
- removeMetric(key, metrics, graphIndex) {
+ removePanel(key, panels, graphIndex) {
this.setPanelGroupMetrics({
- metrics: metrics.filter((v, i) => i !== graphIndex),
+ panels: panels.filter((v, i) => i !== graphIndex),
key,
});
},
- removeGraph(metrics, graphIndex) {
- // At present graphs will not be removed, they should removed using the vuex store
- // See https://gitlab.com/gitlab-org/gitlab/issues/27835
- metrics.splice(graphIndex, 1);
- },
showInvalidDateError() {
createFlash(s__('Metrics|Link contains an invalid time window.'));
},
@@ -463,7 +458,7 @@ export default {
group="metrics-dashboard"
:component-data="{ attrs: { class: 'row mx-0 w-100' } }"
:disabled="!isRearrangingPanels"
- @input="updateMetrics(groupData.key, $event)"
+ @input="updatePanels(groupData.key, $event)"
>
<div
v-for="(graphData, graphIndex) in groupData.panels"
@@ -475,7 +470,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.panels, graphIndex)"
+ @click="removePanel(groupData.key, groupData.panels, graphIndex)"
>
<a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')"
><icon name="close"
diff --git a/app/helpers/projects/error_tracking_helper.rb b/app/helpers/projects/error_tracking_helper.rb
index 2dd22402028..de21a78f5f0 100644
--- a/app/helpers/projects/error_tracking_helper.rb
+++ b/app/helpers/projects/error_tracking_helper.rb
@@ -18,7 +18,7 @@ module Projects::ErrorTrackingHelper
opts = [project, issue_id, { format: :json }]
{
- 'issue-project-path' => new_project_issue_path(project),
+ 'project-issues-path' => project_issues_path(project),
'issue-details-path' => details_project_error_tracking_index_path(*opts),
'issue-stack-trace-path' => stack_trace_project_error_tracking_index_path(*opts)
}
diff --git a/changelogs/unreleased/Replace-BoardService_in_list_spec-js.yml b/changelogs/unreleased/Replace-BoardService_in_list_spec-js.yml
new file mode 100644
index 00000000000..9f23dca62a0
--- /dev/null
+++ b/changelogs/unreleased/Replace-BoardService_in_list_spec-js.yml
@@ -0,0 +1,5 @@
+---
+title: removes references of BoardService
+merge_request: 20877
+author: nuwe1
+type: other
diff --git a/changelogs/unreleased/lm-create-issues-from-sentry-details-page.yml b/changelogs/unreleased/lm-create-issues-from-sentry-details-page.yml
new file mode 100644
index 00000000000..0a9774dfcd3
--- /dev/null
+++ b/changelogs/unreleased/lm-create-issues-from-sentry-details-page.yml
@@ -0,0 +1,5 @@
+---
+title: Adds ability to create issues from sentry details page
+merge_request: 20666
+author:
+type: added
diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md
index a54f6843c53..32816652ed5 100644
--- a/doc/integration/jenkins.md
+++ b/doc/integration/jenkins.md
@@ -135,3 +135,32 @@ configured or there was an error reporting the status via the API.
1. [Configure the Jenkins server](#configure-the-jenkins-server) for GitLab API access
1. [Configure a Jenkins project](#configure-a-jenkins-project), including the
'Publish build status to GitLab' post-build action.
+
+### Merge Request event does not trigger a Jenkins Pipeline
+
+Check the **/var/log/gitlab/gitlab-rails/production.log** file for messages like:
+
+```plaintext
+WebHook Error => Net::ReadTimeout
+```
+
+or
+
+```plaintext
+WebHook Error => execution expired
+```
+
+If those are present, the request is exceeding the
+[webhook timeout](../user/project/integrations/webhooks.md#receiving-duplicate-or-multiple-webhook-requests-triggered-by-one-event),
+which is set to 10 seconds by default.
+
+To fix this the `gitlab_rails['webhook_timeout']` value will need to be increased
+in the `gitlab.rb` config file, followed by the [`gitlab-ctl reconfigure` command](../administration/restart_gitlab.md).
+
+If you don't find the errors above, but do find *duplicate* entries like below (in **/var/log/gitlab/gitlab-rail**), this
+could also indicate that [webhook requests are timing out](../user/project/integrations/webhooks.md#receiving-duplicate-or-multiple-webhook-requests-triggered-by-one-event):
+
+```
+2019-10-25_04:22:41.25630 2019-10-25T04:22:41.256Z 1584 TID-ovowh4tek WebHookWorker JID-941fb7f40b69dff3d833c99b INFO: start
+2019-10-25_04:22:41.25630 2019-10-25T04:22:41.256Z 1584 TID-ovowh4tek WebHookWorker JID-941fb7f40b69dff3d833c99b INFO: start
+```
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 108097542c2..52736edf84a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -234,6 +234,9 @@ msgstr[1] ""
msgid "%{count} related %{pluralized_subject}: %{links}"
msgstr ""
+msgid "%{description}- Sentry event: %{errorUrl}- First seen: %{firstSeen}- Last seen: %{lastSeen} %{countLabel}: %{count}%{userCountLabel}: %{userCount}"
+msgstr ""
+
msgid "%{duration}ms"
msgstr ""
@@ -474,12 +477,22 @@ msgstr ""
msgid ", or "
msgstr ""
+msgid "- Event"
+msgid_plural "- Events"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "- Runner is active and can process any new jobs"
msgstr ""
msgid "- Runner is paused and will not receive any new jobs"
msgstr ""
+msgid "- User"
+msgid_plural "- Users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "- show less"
msgstr ""
@@ -6983,9 +6996,6 @@ msgstr ""
msgid "Events"
msgstr ""
-msgid "Events: %{count}"
-msgstr ""
-
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
msgstr ""
@@ -7642,9 +7652,6 @@ msgstr ""
msgid "First seen"
msgstr ""
-msgid "First seen: %{first_seen}"
-msgstr ""
-
msgid "Fixed date"
msgstr ""
@@ -10108,9 +10115,6 @@ msgstr ""
msgid "Last seen"
msgstr ""
-msgid "Last seen: %{last_seen}"
-msgstr ""
-
msgid "Last successful update"
msgstr ""
@@ -15637,9 +15641,6 @@ msgstr ""
msgid "Sentry event"
msgstr ""
-msgid "Sentry event: %{external_url}"
-msgstr ""
-
msgid "Sep"
msgstr ""
@@ -19362,9 +19363,6 @@ msgstr ""
msgid "Users with a Guest role or those who don't belong to any projects or groups don't count towards seats in use."
msgstr ""
-msgid "Users: %{user_count}"
-msgstr ""
-
msgid "UsersSelect|%{name} + %{length} more"
msgstr ""
diff --git a/spec/frontend/error_tracking/components/error_details_spec.js b/spec/frontend/error_tracking/components/error_details_spec.js
index cf91c840cc4..72f3577c530 100644
--- a/spec/frontend/error_tracking/components/error_details_spec.js
+++ b/spec/frontend/error_tracking/components/error_details_spec.js
@@ -1,6 +1,7 @@
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
-import { GlButton, GlLoadingIcon, GlLink } from '@gitlab/ui';
+import { GlLoadingIcon, GlLink } from '@gitlab/ui';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Stacktrace from '~/error_tracking/components/stacktrace.vue';
import ErrorDetails from '~/error_tracking/components/error_details.vue';
@@ -15,12 +16,14 @@ describe('ErrorDetails', () => {
function mountComponent() {
wrapper = shallowMount(ErrorDetails, {
+ stubs: { LoadingButton },
localVue,
store,
propsData: {
issueDetailsPath: '/123/details',
issueStackTracePath: '/stacktrace',
- issueProjectPath: '/test-project/issues/new',
+ projectIssuesPath: '/test-project/issues/',
+ csrfToken: 'fakeToken',
},
});
}
@@ -83,36 +86,6 @@ describe('ErrorDetails', () => {
expect(wrapper.find(Stacktrace).exists()).toBe(false);
});
- it('should allow an issue to be created with title and description', () => {
- store.state.details.loading = false;
- store.state.details.error = {
- id: 1,
- title: 'Issue title',
- external_url: 'http://sentry.gitlab.net/gitlab',
- first_seen: '2017-05-26T13:32:48Z',
- last_seen: '2018-05-26T13:32:48Z',
- count: 12,
- user_count: 2,
- };
- mountComponent();
- const button = wrapper.find(GlButton);
- const title = 'Issue title';
- const url = 'Sentry event: http://sentry.gitlab.net/gitlab';
- const firstSeen = 'First seen: 2017-05-26T13:32:48Z';
- const lastSeen = 'Last seen: 2018-05-26T13:32:48Z';
- const count = 'Events: 12';
- const userCount = 'Users: 2';
-
- const issueDescription = `${url}${firstSeen}${lastSeen}${count}${userCount}`;
-
- const issueLink = `/test-project/issues/new?issue[title]=${encodeURIComponent(
- title,
- )}&issue[description]=${encodeURIComponent(issueDescription)}`;
-
- expect(button.exists()).toBe(true);
- expect(button.attributes().href).toBe(issueLink);
- });
-
describe('Stacktrace', () => {
it('should show stacktrace', () => {
store.state.details.loading = false;
@@ -132,5 +105,38 @@ describe('ErrorDetails', () => {
expect(wrapper.find(Stacktrace).exists()).toBe(false);
});
});
+
+ describe('When a user clicks the create issue button', () => {
+ beforeEach(() => {
+ store.state.details.loading = false;
+ store.state.details.error = {
+ id: 1,
+ title: 'Issue title',
+ external_url: 'http://sentry.gitlab.net/gitlab',
+ first_seen: '2017-05-26T13:32:48Z',
+ last_seen: '2018-05-26T13:32:48Z',
+ count: 12,
+ user_count: 2,
+ };
+ mountComponent();
+ });
+
+ it('should set the form values with title and description', () => {
+ const csrfTokenInput = wrapper.find('glforminput-stub[name="authenticity_token"]');
+ const issueTitleInput = wrapper.find('glforminput-stub[name="issue[title]"]');
+ const issueDescriptionInput = wrapper.find('input[name="issue[description]"]');
+ expect(csrfTokenInput.attributes('value')).toBe('fakeToken');
+ expect(issueTitleInput.attributes('value')).toContain(wrapper.vm.issueTitle);
+ expect(issueDescriptionInput.attributes('value')).toContain(wrapper.vm.issueDescription);
+ });
+
+ it('should submit the form', () => {
+ window.HTMLFormElement.prototype.submit = () => {};
+ const submitSpy = jest.spyOn(wrapper.vm.$refs.sentryIssueForm, 'submit');
+ wrapper.find('button').trigger('click');
+ expect(submitSpy).toHaveBeenCalled();
+ submitSpy.mockRestore();
+ });
+ });
});
});
diff --git a/spec/helpers/projects/error_tracking_helper_spec.rb b/spec/helpers/projects/error_tracking_helper_spec.rb
index fdffdca22fb..753144eef89 100644
--- a/spec/helpers/projects/error_tracking_helper_spec.rb
+++ b/spec/helpers/projects/error_tracking_helper_spec.rb
@@ -81,7 +81,7 @@ describe Projects::ErrorTrackingHelper do
let(:route_params) { [project.owner, project, issue_id, { format: :json }] }
let(:details_path) { details_namespace_project_error_tracking_index_path(*route_params) }
let(:stack_trace_path) { stack_trace_namespace_project_error_tracking_index_path(*route_params) }
- let(:issue_project_path) { new_project_issue_path(project) }
+ let(:issues_path) { project_issues_path(project) }
let(:result) { helper.error_details_data(project, issue_id) }
@@ -93,8 +93,8 @@ describe Projects::ErrorTrackingHelper do
expect(result['issue-stack-trace-path']).to eq stack_trace_path
end
- it 'returns the correct path for creating a new issue' do
- expect(result['issue-project-path']).to eq issue_project_path
+ it 'creates an issue and redirects to issue show page' do
+ expect(result['project-issues-path']).to eq issues_path
end
end
end