diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.ts b/polygerrit-ui/app/elements/change/gr-message/gr-message.ts index 2b5d94d21b..05316dbf65 100644 --- a/polygerrit-ui/app/elements/change/gr-message/gr-message.ts +++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.ts @@ -38,6 +38,7 @@ import { RepoName, ReviewInputTag, VotingRangeInfo, + ChangeNum, } from '../../../types/common'; import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api'; import {CommentThread} from '../../diff/gr-comment-api/gr-comment-api'; @@ -102,7 +103,7 @@ export class GrMessage extends GestureEventListeners( change?: ChangeInfo; @property({type: Number}) - changeNum?: number; + changeNum?: ChangeNum; @property({type: Object}) message: ChangeMessage | undefined; diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts index 11e48884b9..db0bd64e30 100644 --- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts +++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.ts @@ -596,7 +596,7 @@ export const GerritNav = { * @param basePatchNum The string 'PARENT' can be used for none. */ getUrlForChange( - change: ChangeInfo, + change: Pick, patchNum?: PatchSetNum, basePatchNum?: PatchSetNum, isEdit?: boolean, @@ -640,7 +640,7 @@ export const GerritNav = { * */ navigateToChange( - change: ChangeInfo, + change: Pick, patchNum?: PatchSetNum, basePatchNum?: PatchSetNum, isEdit?: boolean, diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts index d051d30025..4ac7e8f6bc 100644 --- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts +++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts @@ -14,87 +14,131 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import '@polymer/iron-icon/iron-icon.js'; -import '../../../styles/shared-styles.js'; -import '../../shared/gr-dialog/gr-dialog.js'; -import '../../shared/gr-overlay/gr-overlay.js'; -import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js'; -import '../gr-diff/gr-diff.js'; -import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js'; -import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; -import {PolymerElement} from '@polymer/polymer/polymer-element.js'; -import {htmlTemplate} from './gr-apply-fix-dialog_html.js'; -import {GerritNav} from '../../core/gr-navigation/gr-navigation.js'; +import '@polymer/iron-icon/iron-icon'; +import '../../../styles/shared-styles'; +import '../../shared/gr-dialog/gr-dialog'; +import '../../shared/gr-overlay/gr-overlay'; +import '../../shared/gr-rest-api-interface/gr-rest-api-interface'; +import '../gr-diff/gr-diff'; +import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners'; +import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin'; +import {PolymerElement} from '@polymer/polymer/polymer-element'; +import {htmlTemplate} from './gr-apply-fix-dialog_html'; +import {GerritNav} from '../../core/gr-navigation/gr-navigation'; +import {customElement, property} from '@polymer/decorators'; +import {ParsedChangeInfo} from '../../shared/gr-rest-api-interface/gr-reviewer-updates-parser'; +import { + ChangeNum, + DiffInfo, + DiffPreferencesInfo, + EditPatchSetNum, + FixId, + FixSuggestionInfo, + PatchSetNum, + RobotId, +} from '../../../types/common'; +import {GrOverlay} from '../../shared/gr-overlay/gr-overlay'; +import { + CommentEventDetail, + isRobotComment, +} from '../../shared/gr-comment/gr-comment'; +import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api'; -/** - * @extends PolymerElement - */ -class GrApplyFixDialog extends GestureEventListeners( - LegacyElementMixin(PolymerElement)) { - static get template() { return htmlTemplate; } +export interface GrApplyFixDialog { + $: { + restAPI: RestApiService & Element; + applyFixOverlay: GrOverlay; + }; +} - static get is() { return 'gr-apply-fix-dialog'; } +interface FilePreview { + filepath: string; + preview: DiffInfo; +} - static get properties() { - return { - // 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, - }, - // Index of currently showing suggested fix. - _selectedFixIdx: Number, - _disableApplyFixButton: { - type: Boolean, - computed: '_computeDisableApplyFixButton(_isApplyFixLoading, change, ' - + '_patchNum)', - }, - }; +@customElement('gr-apply-fix-dialog') +export class GrApplyFixDialog extends GestureEventListeners( + LegacyElementMixin(PolymerElement) +) { + static get template() { + return htmlTemplate; } + @property({type: Object}) + prefs?: DiffPreferencesInfo; + + @property({type: Object}) + change?: ParsedChangeInfo; + + @property({type: String}) + changeNum?: ChangeNum; + + @property({type: Number}) + _patchNum?: PatchSetNum; + + @property({type: String}) + _robotId?: RobotId; + + @property({type: Object}) + _currentFix?: FixSuggestionInfo; + + @property({type: Array}) + _currentPreviews: FilePreview[] = []; + + @property({type: Array}) + _fixSuggestions?: FixSuggestionInfo[]; + + @property({type: Boolean}) + _isApplyFixLoading = false; + + @property({type: Number}) + _selectedFixIdx = 0; + + @property({ + type: Boolean, + computed: + '_computeDisableApplyFixButton(_isApplyFixLoading, change, ' + + '_patchNum)', + }) + _disableApplyFixButton?: boolean; + + private refitOverlay?: () => void; + /** - * Given robot comment CustomEvent objevt, fetch diffs associated + * Given robot comment CustomEvent object, 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 + * @param e to be passed from gr-comment with robot comment detail. + * @return 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) { + open(e: CustomEvent) { + const detail = e.detail; + const comment = detail.comment; + if (!detail.patchNum || !comment || !isRobotComment(comment)) { + return Promise.resolve(); + } + this._patchNum = detail.patchNum; + this._fixSuggestions = comment.fix_suggestions; + this._robotId = comment.robot_id; + if (!this._fixSuggestions || !this._fixSuggestions.length) { return Promise.resolve(); } this._selectedFixIdx = 0; const promises = []; promises.push( - this._showSelectedFixSuggestion(this._fixSuggestions[0]), - this.$.applyFixOverlay.open() + this._showSelectedFixSuggestion(this._fixSuggestions[0]), + this.$.applyFixOverlay.open() ); - return Promise.all(promises) - .then(() => { - // ensures gr-overlay repositions overlay in center - this.$.applyFixOverlay.dispatchEvent( - new CustomEvent('iron-resize', { - composed: true, bubbles: true, - })); - }); + return Promise.all(promises).then(() => { + // ensures gr-overlay repositions overlay in center + this.$.applyFixOverlay.dispatchEvent( + new CustomEvent('iron-resize', { + composed: true, + bubbles: true, + }) + ); + }); } attached() { @@ -102,114 +146,132 @@ class GrApplyFixDialog extends GestureEventListeners( this.refitOverlay = () => { // re-center the dialog as content changed this.$.applyFixOverlay.dispatchEvent( - new CustomEvent('iron-resize', { - composed: true, bubbles: true, - })); + new CustomEvent('iron-resize', { + composed: true, + bubbles: true, + }) + ); }; this.addEventListener('diff-context-expanded', this.refitOverlay); } detached() { super.detached(); - this.removeEventListener('diff-context-expanded', this.refitOverlay); + if (this.refitOverlay) { + this.removeEventListener('diff-context-expanded', this.refitOverlay); + } } - _showSelectedFixSuggestion(fixSuggestion) { + _showSelectedFixSuggestion(fixSuggestion: FixSuggestionInfo) { this._currentFix = fixSuggestion; return this._fetchFixPreview(fixSuggestion.fix_id); } - _fetchFixPreview(fixId) { + _fetchFixPreview(fixId: FixId) { + if (!this.changeNum || !this._patchNum) { + return Promise.reject( + new Error('Both _patchNum and changeNum must be set') + ); + } return this.$.restAPI - .getRobotCommentFixPreview(this.changeNum, this._patchNum, fixId) - .then(res => { - if (res != null) { - this._currentPreviews = Object.keys(res).map(key => { - return {filepath: key, preview: res[key]}; - }); - } - }) - .catch(err => { - this._close(); - throw err; - }); + .getRobotCommentFixPreview(this.changeNum, this._patchNum, fixId) + .then(res => { + if (res) { + this._currentPreviews = Object.keys(res).map(key => { + return {filepath: key, preview: res[key]}; + }); + } + }) + .catch(err => { + this._close(); + throw err; + }); } - hasSingleFix(_fixSuggestions) { - return (_fixSuggestions || {}).length === 1; + hasSingleFix(_fixSuggestions?: FixSuggestionInfo[]) { + return (_fixSuggestions || []).length === 1; } - overridePartialPrefs(prefs) { + overridePartialPrefs(prefs: DiffPreferencesInfo): DiffPreferencesInfo { // generate a smaller gr-diff than fullscreen for dialog return {...prefs, line_length: 50}; } - onCancel(e) { + onCancel(e: CustomEvent) { if (e) { e.stopPropagation(); } this._close(); } - addOneTo(_selectedFixIdx) { + addOneTo(_selectedFixIdx: number) { return _selectedFixIdx + 1; } - _onPrevFixClick(e) { + _onPrevFixClick(e: CustomEvent) { if (e) e.stopPropagation(); - if (this._selectedFixIdx >= 1 && this._fixSuggestions != null) { + if (this._selectedFixIdx >= 1 && this._fixSuggestions) { this._selectedFixIdx -= 1; - return this._showSelectedFixSuggestion( - this._fixSuggestions[this._selectedFixIdx]); + this._showSelectedFixSuggestion( + this._fixSuggestions[this._selectedFixIdx] + ); } } - _onNextFixClick(e) { + _onNextFixClick(e: CustomEvent) { if (e) e.stopPropagation(); - if (this._fixSuggestions && - this._selectedFixIdx < this._fixSuggestions.length) { + if ( + this._fixSuggestions && + this._selectedFixIdx < this._fixSuggestions.length + ) { this._selectedFixIdx += 1; - return this._showSelectedFixSuggestion( - this._fixSuggestions[this._selectedFixIdx]); + this._showSelectedFixSuggestion( + this._fixSuggestions[this._selectedFixIdx] + ); } } - _noPrevFix(_selectedFixIdx) { + _noPrevFix(_selectedFixIdx: number) { return _selectedFixIdx === 0; } - _noNextFix(_selectedFixIdx, fixSuggestions) { - if (fixSuggestions == null) return true; + _noNextFix(_selectedFixIdx: number, fixSuggestions?: FixSuggestionInfo[]) { + if (!fixSuggestions) return true; return _selectedFixIdx === fixSuggestions.length - 1; } _close() { - this._currentFix = {}; + this._currentFix = undefined; this._currentPreviews = []; this._isApplyFixLoading = false; - this.dispatchEvent(new CustomEvent('close-fix-preview', { - bubbles: true, - composed: true, - })); + this.dispatchEvent( + new CustomEvent('close-fix-preview', { + bubbles: true, + composed: true, + }) + ); this.$.applyFixOverlay.close(); } - _getApplyFixButtonLabel(isLoading) { + _getApplyFixButtonLabel(isLoading: boolean) { return isLoading ? 'Saving...' : 'Apply Fix'; } - _computeTooltip(change, patchNum) { - if (!change || patchNum == undefined) return ''; - // If change is defined, change.revisions and change.current_revisions - // must be defined + _computeTooltip(change?: ParsedChangeInfo, patchNum?: PatchSetNum) { + if (!change || !patchNum) return ''; const latestPatchNum = change.revisions[change.current_revision]._number; - return latestPatchNum !== patchNum ? - 'Fix can only be applied to the latest patchset' : ''; + return latestPatchNum !== patchNum + ? 'Fix can only be applied to the latest patchset' + : ''; } - _computeDisableApplyFixButton(isApplyFixLoading, change, patchNum) { - if (!change || isApplyFixLoading == undefined || patchNum == undefined) { + _computeDisableApplyFixButton( + isApplyFixLoading?: boolean, + change?: ParsedChangeInfo, + patchNum?: PatchSetNum + ) { + if (!change || isApplyFixLoading === undefined || patchNum === undefined) { return true; } const currentPatchNum = change.revisions[change.current_revision]._number; @@ -219,31 +281,36 @@ class GrApplyFixDialog extends GestureEventListeners( return isApplyFixLoading; } - _handleApplyFix(e) { + _handleApplyFix(e: CustomEvent) { if (e) { e.stopPropagation(); } - if (this._currentFix == null || this._currentFix.fix_id == null) { - return; + + const changeNum = this.changeNum; + const patchNum = this._patchNum; + const change = this.change; + if (!changeNum || !patchNum || !change || !this._currentFix) { + return Promise.reject(new Error('Not all required properties are set.')); } this._isApplyFixLoading = true; return this.$.restAPI - .applyFixSuggestion( - this.changeNum, this._patchNum, this._currentFix.fix_id - ) - .then(res => { - if (res && res.ok) { - GerritNav.navigateToChange(this.change, 'edit', this._patchNum); - this._close(); - } - this._isApplyFixLoading = false; - }); + .applyFixSuggestion(changeNum, patchNum, this._currentFix.fix_id) + .then(res => { + if (res && res.ok) { + GerritNav.navigateToChange(change, EditPatchSetNum, patchNum); + this._close(); + } + this._isApplyFixLoading = false; + }); } - getFixDescription(currentFix) { - return currentFix != null && currentFix.description ? - currentFix.description : ''; + getFixDescription(currentFix?: FixSuggestionInfo) { + return currentFix && currentFix.description ? currentFix.description : ''; } } -customElements.define(GrApplyFixDialog.is, GrApplyFixDialog); +declare global { + interface HTMLElementTagNameMap { + 'gr-apply-fix-dialog': GrApplyFixDialog; + } +} diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js index b3ba637e1a..cb73885691 100644 --- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js +++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.js @@ -179,19 +179,19 @@ suite('gr-apply-fix-dialog tests', () => { comment: ROBOT_COMMENT_WITH_TWO_FIXES}}); flush(() => { assert.isTrue(errorStub.called); - assert.deepEqual(element._currentFix, {}); + assert.equal(element._currentFix, undefined); done(); }); }); test('apply fix button should call apply ' + - 'and navigate to change view', done => { + 'and navigate to change view', () => { sinon.stub(element.$.restAPI, 'applyFixSuggestion') .returns(Promise.resolve({ok: true})); sinon.stub(GerritNav, 'navigateToChange'); element._currentFix = {fix_id: '123'}; - element._handleApplyFix().then(() => { + return element._handleApplyFix().then(() => { assert.isTrue(element.$.restAPI.applyFixSuggestion .calledWithExactly('1', 2, '123')); assert.isTrue(GerritNav.navigateToChange.calledWithExactly({ @@ -205,9 +205,8 @@ suite('gr-apply-fix-dialog tests', () => { }, 'edit', 2)); // reset gr-apply-fix-dialog and close - assert.deepEqual(element._currentFix, {}); + assert.equal(element._currentFix, undefined); assert.equal(element._currentPreviews.length, 0); - done(); }); }); diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts index b75c26d7e5..88b0f71229 100644 --- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts +++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts @@ -40,6 +40,7 @@ import { PatchSetNum, EditPreferencesInfo, Base64FileContent, + ChangeNum, } from '../../../types/common'; import {GrStorage} from '../../shared/gr-storage/gr-storage'; @@ -83,7 +84,7 @@ export class GrEditorView extends KeyboardShortcutMixin( _change?: ChangeInfo | null; @property({type: Number}) - _changeNum?: number; + _changeNum?: ChangeNum; @property({type: String}) _patchNum?: PatchSetNum; @@ -189,7 +190,7 @@ export class GrEditorView extends KeyboardShortcutMixin( return Promise.all(promises); } - _getChangeDetail(changeNum: number) { + _getChangeDetail(changeNum: ChangeNum) { return this.$.restAPI.getDiffChangeDetail(changeNum).then(change => { this._change = change; }); @@ -229,7 +230,7 @@ export class GrEditorView extends KeyboardShortcutMixin( ); } - _getFileData(changeNum: number, path: string, patchNum?: PatchSetNum) { + _getFileData(changeNum: ChangeNum, path: string, patchNum?: PatchSetNum) { if (patchNum === undefined) { return Promise.reject(new Error('patchNum undefined')); } diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts index bc7e7399f8..f1af68d4ea 100644 --- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts +++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts @@ -49,6 +49,7 @@ import { CommentInfo, ConfigInfo, AccountDetailInfo, + ChangeNum, } from '../../../types/common'; import {GrButton} from '../gr-button/gr-button'; import {GrConfirmDeleteCommentDialog} from '../gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog'; @@ -96,6 +97,10 @@ export interface Draft { export type Comment = Draft & CommentInfo; export type RobotComment = Draft & RobotCommentInfo; +export function isRobotComment(c: Comment | RobotComment): c is RobotComment { + return (c as RobotComment).robot_id !== undefined; +} + interface CommentOverlays { confirmDelete?: GrOverlay | null; confirmDiscard?: GrOverlay | null; @@ -109,6 +114,12 @@ export interface GrComment { resolvedCheckbox: HTMLInputElement; }; } + +export interface CommentEventDetail { + patchNum?: PatchSetNum; + comment?: Comment | RobotComment; +} + @customElement('gr-comment') export class GrComment extends KeyboardShortcutMixin( GestureEventListeners(LegacyElementMixin(PolymerElement)) @@ -160,7 +171,7 @@ export class GrComment extends KeyboardShortcutMixin( */ @property({type: Number}) - changeNum?: number; + changeNum?: ChangeNum; @property({type: Object, notify: true, observer: '_commentChanged'}) comment?: Comment | RobotComment; @@ -506,8 +517,8 @@ export class GrComment extends KeyboardShortcutMixin( ); } - _getEventPayload(opt_mixin?: Record) { - return {...opt_mixin, comment: this.comment, patchNum: this.patchNum}; + _getEventPayload(): CommentEventDetail { + return {comment: this.comment, patchNum: this.patchNum}; } _fireSave() { diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts index 944c0a7469..b3dba32182 100644 --- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts +++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts @@ -130,6 +130,9 @@ import { SubmittedTogetherInfo, ChangeNum, EmailAddress, + FixId, + FilePathToDiffInfoMap, + ChangeViewChangeInfo, } from '../../../types/common'; import { CancelConditionCallback, @@ -1388,7 +1391,11 @@ export class GrRestApiInterface optionsHex, errFn, cancelCondition - ).then(GrReviewerUpdatesParser.parse); + ).then(detail => + // detail has ChangeViewChangeInfo type because the optionsHex always + // includes ALL_REVISIONS flag. + GrReviewerUpdatesParser.parse(detail as ChangeViewChangeInfo) + ); }); } @@ -2329,21 +2336,21 @@ export class GrRestApiInterface getRobotCommentFixPreview( changeNum: ChangeNum, patchNum: PatchSetNum, - fixId: string - ) { + fixId: FixId + ): Promise { return this._getChangeURLAndFetch({ changeNum, patchNum, endpoint: `/fixes/${encodeURIComponent(fixId)}/preview`, reportEndpointAsId: true, - }); + }) as Promise; } applyFixSuggestion( changeNum: ChangeNum, patchNum: PatchSetNum, fixId: string - ) { + ): Promise { return this._getChangeURLAndSend({ method: HttpMethod.POST, changeNum, diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.ts index 2c3bf44be4..b5e56961ec 100644 --- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.ts +++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser.ts @@ -21,6 +21,7 @@ import { AccountInfo, ChangeInfo, ChangeMessageInfo, + ChangeViewChangeInfo, ReviewerUpdateInfo, Timestamp, } from '../../../types/common'; @@ -30,7 +31,7 @@ import {accountKey} from '../../../utils/account-util'; const MESSAGE_REVIEWERS_THRESHOLD_MILLIS = 500; const REVIEWER_UPDATE_THRESHOLD_MILLIS = 6000; -interface ChangeInfoParserInput extends ChangeInfo { +interface ChangeInfoParserInput extends ChangeViewChangeInfo { messages: ChangeMessageInfo[]; reviewer_updates: ReviewerUpdateInfo[]; // Always has at least 1 item } @@ -77,7 +78,8 @@ interface UpdateItem { prev_state?: ReviewerState; } -export interface ParsedChangeInfo extends Omit { +export interface ParsedChangeInfo + extends Omit { reviewer_updates?: ReviewerUpdateInfo[] | FormattedReviewerUpdateInfo[]; } @@ -291,7 +293,7 @@ export class GrReviewerUpdatesParser { } static parse( - change: ChangeInfo | undefined | null + change: ChangeViewChangeInfo | undefined | null ): ParsedChangeInfo | undefined | null { // TODO(TS): The !change condition should be removed when all files are converted to TS if (!change || !isChangeInfoParserInput(change)) { diff --git a/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts b/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts index 3876f0eb1c..8a7180ecc9 100644 --- a/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts +++ b/polygerrit-ui/app/services/services/gr-rest-api/gr-rest-api.ts @@ -85,6 +85,8 @@ import { RelatedChangesInfo, SubmittedTogetherInfo, EmailAddress, + FixId, + FilePathToDiffInfoMap, } from '../../../types/common'; import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser'; import {HttpMethod} from '../../../constants/constants'; @@ -718,4 +720,16 @@ export interface RestApiService { hasPendingDiffDrafts(): number; awaitPendingDiffDrafts(): Promise; + + getRobotCommentFixPreview( + changeNum: ChangeNum, + patchNum: PatchSetNum, + fixId: FixId + ): Promise; + + applyFixSuggestion( + changeNum: ChangeNum, + patchNum: PatchSetNum, + fixId: string + ): Promise; } diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts index f943ff54db..ce2b9cb15a 100644 --- a/polygerrit-ui/app/types/common.ts +++ b/polygerrit-ui/app/types/common.ts @@ -47,6 +47,12 @@ import { export type BrandType = T & {[__brand in BrandName]: never}; +/* + * In T, make a set of properties whose keys are in the union K required + */ +export type RequireProperties = Omit & + Required>; + /** * Type alias for parsed json object to make code cleaner */ @@ -62,7 +68,7 @@ export const ParentPatchSetNum = 'PARENT' as PatchSetNum; export type ChangeId = BrandType; export type ChangeMessageId = BrandType; export type NumericChangeId = BrandType; -export type ChangeNum = number; // !!!TODO: define correct types +export type ChangeNum = NumericChangeId; // This type is removed in the following change export type RepoName = BrandType; export type UrlEncodedRepoName = BrandType; export type TopicName = BrandType; @@ -74,6 +80,8 @@ export type TrackingId = BrandType; export type ReviewInputTag = BrandType; export type RobotId = BrandType; export type RobotRunId = BrandType; + +// The UUID of the suggested fix. export type FixId = BrandType; export type EmailAddress = BrandType; @@ -237,6 +245,14 @@ export interface ChangeInfo { internalHost?: string; // TODO(TS): provide an explanation what is its } +/** + * ChangeView request change detail with ALL_REVISIONS option set. + * The response always contains current_revision and revisions. + */ +export type ChangeViewChangeInfo = RequireProperties< + ChangeInfo, + 'current_revision' | 'revisions' +>; /** * The AccountInfo entity contains information about an account. * https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html#account-info @@ -1178,6 +1194,8 @@ export interface DiffInfo { binary: boolean; } +export type FilePathToDiffInfoMap = {[path: string]: DiffInfo}; + /** * The DiffWebLinkInfo entity describes a link on a diff screen to an external site. * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#diff-web-link-info @@ -1876,8 +1894,13 @@ export type PathToRobotCommentsInfoMap = {[path: string]: RobotCommentInfo[]}; * The FixSuggestionInfo entity represents a suggested fix * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#fix-suggestion-info */ -export interface FixSuggestionInfo { - fix_id?: FixId; +export interface FixSuggestionInfoInput { + description: string; + replacements: FixReplacementInfo[]; +} + +export interface FixSuggestionInfo extends FixSuggestionInfoInput { + fix_id: FixId; description: string; replacements: FixReplacementInfo[]; } diff --git a/polygerrit-ui/app/utils/patch-set-util.ts b/polygerrit-ui/app/utils/patch-set-util.ts index ff55e1f572..4d688bdb01 100644 --- a/polygerrit-ui/app/utils/patch-set-util.ts +++ b/polygerrit-ui/app/utils/patch-set-util.ts @@ -169,7 +169,9 @@ export function sortRevisions(revisions: T[]): T[] { * @return Sorted list of patch set objects, as described * above */ -export function computeAllPatchSets(change: ParsedChangeInfo): PatchSet[] { +export function computeAllPatchSets( + change: ChangeInfo | ParsedChangeInfo +): PatchSet[] { if (!change) { return []; } @@ -205,7 +207,7 @@ export function computeAllPatchSets(change: ParsedChangeInfo): PatchSet[] { * wip property set on each of them */ function _computeWipForPatchSets( - change: ParsedChangeInfo, + change: ChangeInfo | ParsedChangeInfo, patchNums: PatchSet[] ) { if (!change.messages || !change.messages.length) {