diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html index b86213a0ee..c9f881f0f7 100644 --- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html +++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html @@ -51,6 +51,7 @@ limitations under the License. + @@ -635,6 +636,11 @@ limitations under the License. on-thread-list-modified="_handleReloadDiffComments"> + + + + + + + + + + + + + + + [[_robotId]] - [[getFixDescription(_currentFix)]] + + + + [[item.filepath]] + + + + + + + + + + + + diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js new file mode 100644 index 0000000000..e4a0b968ca --- /dev/null +++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.js @@ -0,0 +1,153 @@ +/** + * @license + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +(function() { + 'use strict'; + Polymer({ + is: 'gr-apply-fix-dialog', + + properties: { + // Diff rendering preference API response. + prefs: Array, + // ChangeInfo API response object. + change: Object, + changeNum: String, + _patchNum: Number, + // robot ID associated with a robot comment. + _robotId: String, + // Selected FixSuggestionInfo entity from robot comment API response. + _currentFix: Object, + // Flattened /preview API response DiffInfo map object. + _currentPreviews: {type: Array, value: () => []}, + // FixSuggestionInfo entities from robot comment API response. + _fixSuggestions: Array, + _isApplyFixLoading: { + type: Boolean, + value: false, + }, + }, + + behaviors: [ + Gerrit.FireBehavior, + ], + + /** + * Given robot comment CustomEvent objevt, fetch diffs associated + * with first robot comment suggested fix and open dialog. + * + * @param {*} e CustomEvent to be passed from gr-comment with + * robot comment detail. + * @return {Promise} Promise that resolves either when all + * preview diffs are fetched or no fix suggestions in custom event detail. + */ + open(e) { + this._patchNum = e.detail.patchNum; + this._fixSuggestions = e.detail.comment.fix_suggestions; + this._robotId = e.detail.comment.robot_id; + if (this._fixSuggestions == null || this._fixSuggestions.length == 0) { + return Promise.resolve(); + } + const promises = []; + promises.push( + this._showSelectedFixSuggestion(this._fixSuggestions[0]), + this.$.applyFixOverlay.open() + ); + return Promise.all(promises) + .then(() => { + // ensures gr-overlay repositions overlay in center + this.$.applyFixOverlay.fire('iron-resize'); + }); + }, + + _showSelectedFixSuggestion(fixSuggestion) { + this._currentFix = fixSuggestion; + return this._fetchFixPreview(fixSuggestion.fix_id); + }, + + _fetchFixPreview(fixId) { + return this.$.restAPI + .getRobotCommentFixPreview(this.changeNum, this._patchNum, fixId) + .then(res => { + if (res != null) { + const previews = Object.keys(res).map(key => + ({filepath: key, preview: res[key]})); + this._currentPreviews = previews; + } + }).catch(err => { + this._close(); + this.dispatchEvent(new CustomEvent('show-error', { + bubbles: true, + composed: true, + detail: {message: `Error generating fix preview: ${err}`}, + })); + }); + }, + + overridePartialPrefs(prefs) { + // generate a smaller gr-diff than fullscreen for dialog + return Object.assign({}, prefs, {line_length: 50}); + }, + + onCancel(e) { + if (e) { + e.stopPropagation(); + } + this._close(); + }, + + _close() { + this._currentFix = {}; + this._currentPreviews = []; + this._isApplyFixLoading = false; + + this.dispatchEvent(new CustomEvent('close-fix-preview', { + bubbles: true, + composed: true, + })); + this.$.applyFixOverlay.close(); + }, + + _getApplyFixButtonLabel(isLoading) { + return isLoading ? 'Saving...' : 'Apply Fix'; + }, + + _handleApplyFix(e) { + if (e) { + e.stopPropagation(); + } + if (this._currentFix == null || this._currentFix.fix_id == null) { + return; + } + this._isApplyFixLoading = true; + return this.$.restAPI.applyFixSuggestion(this.changeNum, this._patchNum, + this._currentFix.fix_id).then(res => { + Gerrit.Nav.navigateToChange(this.change, 'edit', this._patchNum); + this._close(); + }).catch(err => { + this.dispatchEvent(new CustomEvent('show-error', { + bubbles: true, + composed: true, + detail: {message: `Error applying fix suggestion: ${err}`}, + })); + }); + }, + + getFixDescription(currentFix) { + return currentFix != null && currentFix.description ? + currentFix.description : ''; + }, + }); +})(); diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.html b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.html new file mode 100644 index 0000000000..1b9df9e1c4 --- /dev/null +++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.html @@ -0,0 +1,143 @@ + + + +gr-apply-fix-dialog + + + + + + + + + + + + + + + + + + diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js index 92921219a2..62b35e4a14 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js +++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js @@ -357,7 +357,9 @@ } for (const layer of this.layers) { - layer.annotate(contentText, lineNumberEl, line); + if (typeof layer.annotate == 'function') { + layer.annotate(contentText, lineNumberEl, line); + } } td.appendChild(contentText); diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html index cd5351096e..7275ae5218 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html +++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html @@ -37,6 +37,7 @@ limitations under the License. + @@ -343,6 +344,12 @@ limitations under the License. on-comment-anchor-tap="_onLineSelected" on-line-selected="_onLineSelected"> + + + + Show Fix + diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js index 62d15e5b72..0c45c85035 100644 --- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js +++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js @@ -48,6 +48,12 @@ * @event create-fix-comment */ + /** + * Fired when the show fix preview action is triggered. + * + * @event open-fix-preview + */ + /** * Fired when this comment is discarded. * @@ -505,6 +511,18 @@ })); } + _handleShowFix() { + this.dispatchEvent(new CustomEvent('open-fix-preview', { + bubbles: true, + composed: true, + detail: this._getEventPayload(), + })); + } + + _hasNoFix(comment) { + return !comment || !comment.fix_suggestions; + } + _handleDiscard(e) { e.preventDefault(); this.$.reporting.recordDraftInteraction(); diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html index 81dfaeb4d2..ae8f3f6f9d 100644 --- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html +++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment_test.html @@ -1007,5 +1007,17 @@ limitations under the License. flushAsynchronousOperations(); assert.isNotNull(element.$$('.robotActions gr-button')); }); + + test('_handleShowFix fires open-fix-preview event', done => { + element.addEventListener('open-fix-preview', e => { + assert.deepEqual(e.detail, element._getEventPayload()); + done(); + }); + element.comment = {fix_suggestions: [{}]}; + element.isRobotComment = true; + flushAsynchronousOperations(); + + MockInteractions.tap(element.$$('.show-fix')); + }); }); diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js index a8c5bb365e..fdb108f83f 100644 --- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js +++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js @@ -1860,6 +1860,25 @@ }); } + getRobotCommentFixPreview(changeNum, patchNum, fixId) { + return this._getChangeURLAndFetch({ + changeNum, + patchNum, + endpoint: `/fixes/${encodeURIComponent(fixId)}/preview`, + reportEndpointAsId: true, + }); + } + + applyFixSuggestion(changeNum, patchNum, fixId) { + return this._getChangeURLAndSend({ + method: 'POST', + changeNum, + patchNum, + endpoint: `/fixes/${encodeURIComponent(fixId)}/apply`, + reportEndpointAsId: true, + }); + } + // Deprecated, prefer to use putChangeCommitMessage instead. saveChangeCommitMessageEdit(changeNum, message) { return this._getChangeURLAndSend({ diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html index cca7d045ec..26078cd420 100644 --- a/polygerrit-ui/app/test/index.html +++ b/polygerrit-ui/app/test/index.html @@ -117,6 +117,7 @@ limitations under the License. 'diff/gr-diff-view/gr-diff-view_test.html', 'diff/gr-diff/gr-diff-group_test.html', 'diff/gr-diff/gr-diff_test.html', + 'diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.html', 'diff/gr-patch-range-select/gr-patch-range-select_test.html', 'diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html', 'diff/gr-selection-action-box/gr-selection-action-box_test.html',