diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-04 09:06:33 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-04 09:06:33 +0000 |
commit | c4038d4bdff93b260cbdcd69f9a6c0b07a849457 (patch) | |
tree | 7b74ce5a60a97324dec5c61ac477ca74e3db36e9 | |
parent | d07169c8ae0ebad0f23d03f01aeaf9acfa7e02d1 (diff) | |
download | gitlab-ce-c4038d4bdff93b260cbdcd69f9a6c0b07a849457.tar.gz |
Add latest changes from gitlab-org/gitlab@master
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 |