Convert files to typescript

The change converts the following files to typescript:

* elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts

Change-Id: I274ba656285897c89c42f299724e402758b8cfe3
This commit is contained in:
Dmitrii Filippov
2020-09-22 16:51:18 +02:00
parent 51fa679373
commit 3f3c20561d
11 changed files with 285 additions and 158 deletions

View File

@@ -38,6 +38,7 @@ import {
RepoName, RepoName,
ReviewInputTag, ReviewInputTag,
VotingRangeInfo, VotingRangeInfo,
ChangeNum,
} from '../../../types/common'; } from '../../../types/common';
import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api'; import {RestApiService} from '../../../services/services/gr-rest-api/gr-rest-api';
import {CommentThread} from '../../diff/gr-comment-api/gr-comment-api'; import {CommentThread} from '../../diff/gr-comment-api/gr-comment-api';
@@ -102,7 +103,7 @@ export class GrMessage extends GestureEventListeners(
change?: ChangeInfo; change?: ChangeInfo;
@property({type: Number}) @property({type: Number})
changeNum?: number; changeNum?: ChangeNum;
@property({type: Object}) @property({type: Object})
message: ChangeMessage | undefined; message: ChangeMessage | undefined;

View File

@@ -596,7 +596,7 @@ export const GerritNav = {
* @param basePatchNum The string 'PARENT' can be used for none. * @param basePatchNum The string 'PARENT' can be used for none.
*/ */
getUrlForChange( getUrlForChange(
change: ChangeInfo, change: Pick<ChangeInfo, '_number' | 'project' | 'internalHost'>,
patchNum?: PatchSetNum, patchNum?: PatchSetNum,
basePatchNum?: PatchSetNum, basePatchNum?: PatchSetNum,
isEdit?: boolean, isEdit?: boolean,
@@ -640,7 +640,7 @@ export const GerritNav = {
* *
*/ */
navigateToChange( navigateToChange(
change: ChangeInfo, change: Pick<ChangeInfo, '_number' | 'project' | 'internalHost'>,
patchNum?: PatchSetNum, patchNum?: PatchSetNum,
basePatchNum?: PatchSetNum, basePatchNum?: PatchSetNum,
isEdit?: boolean, isEdit?: boolean,

View File

@@ -14,87 +14,131 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import '@polymer/iron-icon/iron-icon.js'; import '@polymer/iron-icon/iron-icon';
import '../../../styles/shared-styles.js'; import '../../../styles/shared-styles';
import '../../shared/gr-dialog/gr-dialog.js'; import '../../shared/gr-dialog/gr-dialog';
import '../../shared/gr-overlay/gr-overlay.js'; import '../../shared/gr-overlay/gr-overlay';
import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js'; import '../../shared/gr-rest-api-interface/gr-rest-api-interface';
import '../gr-diff/gr-diff.js'; import '../gr-diff/gr-diff';
import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js'; import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js'; import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
import {PolymerElement} from '@polymer/polymer/polymer-element.js'; import {PolymerElement} from '@polymer/polymer/polymer-element';
import {htmlTemplate} from './gr-apply-fix-dialog_html.js'; import {htmlTemplate} from './gr-apply-fix-dialog_html';
import {GerritNav} from '../../core/gr-navigation/gr-navigation.js'; 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';
/** export interface GrApplyFixDialog {
* @extends PolymerElement $: {
*/ restAPI: RestApiService & Element;
class GrApplyFixDialog extends GestureEventListeners( applyFixOverlay: GrOverlay;
LegacyElementMixin(PolymerElement)) { };
static get template() { return htmlTemplate; } }
static get is() { return 'gr-apply-fix-dialog'; } interface FilePreview {
filepath: string;
preview: DiffInfo;
}
static get properties() { @customElement('gr-apply-fix-dialog')
return { export class GrApplyFixDialog extends GestureEventListeners(
// Diff rendering preference API response. LegacyElementMixin(PolymerElement)
prefs: Array, ) {
// ChangeInfo API response object. static get template() {
change: Object, return htmlTemplate;
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)',
},
};
} }
@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. * with first robot comment suggested fix and open dialog.
* *
* @param {*} e CustomEvent to be passed from gr-comment with * @param e to be passed from gr-comment with robot comment detail.
* robot comment detail. * @return Promise that resolves either when all
* @return {Promise<undefined>} Promise that resolves either when all
* preview diffs are fetched or no fix suggestions in custom event detail. * preview diffs are fetched or no fix suggestions in custom event detail.
*/ */
open(e) { open(e: CustomEvent<CommentEventDetail>) {
this._patchNum = e.detail.patchNum; const detail = e.detail;
this._fixSuggestions = e.detail.comment.fix_suggestions; const comment = detail.comment;
this._robotId = e.detail.comment.robot_id; if (!detail.patchNum || !comment || !isRobotComment(comment)) {
if (this._fixSuggestions == null || this._fixSuggestions.length == 0) { 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(); return Promise.resolve();
} }
this._selectedFixIdx = 0; this._selectedFixIdx = 0;
const promises = []; const promises = [];
promises.push( promises.push(
this._showSelectedFixSuggestion(this._fixSuggestions[0]), this._showSelectedFixSuggestion(this._fixSuggestions[0]),
this.$.applyFixOverlay.open() this.$.applyFixOverlay.open()
); );
return Promise.all(promises) return Promise.all(promises).then(() => {
.then(() => { // ensures gr-overlay repositions overlay in center
// ensures gr-overlay repositions overlay in center this.$.applyFixOverlay.dispatchEvent(
this.$.applyFixOverlay.dispatchEvent( new CustomEvent('iron-resize', {
new CustomEvent('iron-resize', { composed: true,
composed: true, bubbles: true, bubbles: true,
})); })
}); );
});
} }
attached() { attached() {
@@ -102,114 +146,132 @@ class GrApplyFixDialog extends GestureEventListeners(
this.refitOverlay = () => { this.refitOverlay = () => {
// re-center the dialog as content changed // re-center the dialog as content changed
this.$.applyFixOverlay.dispatchEvent( this.$.applyFixOverlay.dispatchEvent(
new CustomEvent('iron-resize', { new CustomEvent('iron-resize', {
composed: true, bubbles: true, composed: true,
})); bubbles: true,
})
);
}; };
this.addEventListener('diff-context-expanded', this.refitOverlay); this.addEventListener('diff-context-expanded', this.refitOverlay);
} }
detached() { detached() {
super.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; this._currentFix = fixSuggestion;
return this._fetchFixPreview(fixSuggestion.fix_id); 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 return this.$.restAPI
.getRobotCommentFixPreview(this.changeNum, this._patchNum, fixId) .getRobotCommentFixPreview(this.changeNum, this._patchNum, fixId)
.then(res => { .then(res => {
if (res != null) { if (res) {
this._currentPreviews = Object.keys(res).map(key => { this._currentPreviews = Object.keys(res).map(key => {
return {filepath: key, preview: res[key]}; return {filepath: key, preview: res[key]};
}); });
} }
}) })
.catch(err => { .catch(err => {
this._close(); this._close();
throw err; throw err;
}); });
} }
hasSingleFix(_fixSuggestions) { hasSingleFix(_fixSuggestions?: FixSuggestionInfo[]) {
return (_fixSuggestions || {}).length === 1; return (_fixSuggestions || []).length === 1;
} }
overridePartialPrefs(prefs) { overridePartialPrefs(prefs: DiffPreferencesInfo): DiffPreferencesInfo {
// generate a smaller gr-diff than fullscreen for dialog // generate a smaller gr-diff than fullscreen for dialog
return {...prefs, line_length: 50}; return {...prefs, line_length: 50};
} }
onCancel(e) { onCancel(e: CustomEvent) {
if (e) { if (e) {
e.stopPropagation(); e.stopPropagation();
} }
this._close(); this._close();
} }
addOneTo(_selectedFixIdx) { addOneTo(_selectedFixIdx: number) {
return _selectedFixIdx + 1; return _selectedFixIdx + 1;
} }
_onPrevFixClick(e) { _onPrevFixClick(e: CustomEvent) {
if (e) e.stopPropagation(); if (e) e.stopPropagation();
if (this._selectedFixIdx >= 1 && this._fixSuggestions != null) { if (this._selectedFixIdx >= 1 && this._fixSuggestions) {
this._selectedFixIdx -= 1; this._selectedFixIdx -= 1;
return this._showSelectedFixSuggestion( this._showSelectedFixSuggestion(
this._fixSuggestions[this._selectedFixIdx]); this._fixSuggestions[this._selectedFixIdx]
);
} }
} }
_onNextFixClick(e) { _onNextFixClick(e: CustomEvent) {
if (e) e.stopPropagation(); if (e) e.stopPropagation();
if (this._fixSuggestions && if (
this._selectedFixIdx < this._fixSuggestions.length) { this._fixSuggestions &&
this._selectedFixIdx < this._fixSuggestions.length
) {
this._selectedFixIdx += 1; this._selectedFixIdx += 1;
return this._showSelectedFixSuggestion( this._showSelectedFixSuggestion(
this._fixSuggestions[this._selectedFixIdx]); this._fixSuggestions[this._selectedFixIdx]
);
} }
} }
_noPrevFix(_selectedFixIdx) { _noPrevFix(_selectedFixIdx: number) {
return _selectedFixIdx === 0; return _selectedFixIdx === 0;
} }
_noNextFix(_selectedFixIdx, fixSuggestions) { _noNextFix(_selectedFixIdx: number, fixSuggestions?: FixSuggestionInfo[]) {
if (fixSuggestions == null) return true; if (!fixSuggestions) return true;
return _selectedFixIdx === fixSuggestions.length - 1; return _selectedFixIdx === fixSuggestions.length - 1;
} }
_close() { _close() {
this._currentFix = {}; this._currentFix = undefined;
this._currentPreviews = []; this._currentPreviews = [];
this._isApplyFixLoading = false; this._isApplyFixLoading = false;
this.dispatchEvent(new CustomEvent('close-fix-preview', { this.dispatchEvent(
bubbles: true, new CustomEvent('close-fix-preview', {
composed: true, bubbles: true,
})); composed: true,
})
);
this.$.applyFixOverlay.close(); this.$.applyFixOverlay.close();
} }
_getApplyFixButtonLabel(isLoading) { _getApplyFixButtonLabel(isLoading: boolean) {
return isLoading ? 'Saving...' : 'Apply Fix'; return isLoading ? 'Saving...' : 'Apply Fix';
} }
_computeTooltip(change, patchNum) { _computeTooltip(change?: ParsedChangeInfo, patchNum?: PatchSetNum) {
if (!change || patchNum == undefined) return ''; if (!change || !patchNum) return '';
// If change is defined, change.revisions and change.current_revisions
// must be defined
const latestPatchNum = change.revisions[change.current_revision]._number; const latestPatchNum = change.revisions[change.current_revision]._number;
return latestPatchNum !== patchNum ? return latestPatchNum !== patchNum
'Fix can only be applied to the latest patchset' : ''; ? 'Fix can only be applied to the latest patchset'
: '';
} }
_computeDisableApplyFixButton(isApplyFixLoading, change, patchNum) { _computeDisableApplyFixButton(
if (!change || isApplyFixLoading == undefined || patchNum == undefined) { isApplyFixLoading?: boolean,
change?: ParsedChangeInfo,
patchNum?: PatchSetNum
) {
if (!change || isApplyFixLoading === undefined || patchNum === undefined) {
return true; return true;
} }
const currentPatchNum = change.revisions[change.current_revision]._number; const currentPatchNum = change.revisions[change.current_revision]._number;
@@ -219,31 +281,36 @@ class GrApplyFixDialog extends GestureEventListeners(
return isApplyFixLoading; return isApplyFixLoading;
} }
_handleApplyFix(e) { _handleApplyFix(e: CustomEvent) {
if (e) { if (e) {
e.stopPropagation(); 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; this._isApplyFixLoading = true;
return this.$.restAPI return this.$.restAPI
.applyFixSuggestion( .applyFixSuggestion(changeNum, patchNum, this._currentFix.fix_id)
this.changeNum, this._patchNum, this._currentFix.fix_id .then(res => {
) if (res && res.ok) {
.then(res => { GerritNav.navigateToChange(change, EditPatchSetNum, patchNum);
if (res && res.ok) { this._close();
GerritNav.navigateToChange(this.change, 'edit', this._patchNum); }
this._close(); this._isApplyFixLoading = false;
} });
this._isApplyFixLoading = false;
});
} }
getFixDescription(currentFix) { getFixDescription(currentFix?: FixSuggestionInfo) {
return currentFix != null && currentFix.description ? return currentFix && currentFix.description ? currentFix.description : '';
currentFix.description : '';
} }
} }
customElements.define(GrApplyFixDialog.is, GrApplyFixDialog); declare global {
interface HTMLElementTagNameMap {
'gr-apply-fix-dialog': GrApplyFixDialog;
}
}

View File

@@ -179,19 +179,19 @@ suite('gr-apply-fix-dialog tests', () => {
comment: ROBOT_COMMENT_WITH_TWO_FIXES}}); comment: ROBOT_COMMENT_WITH_TWO_FIXES}});
flush(() => { flush(() => {
assert.isTrue(errorStub.called); assert.isTrue(errorStub.called);
assert.deepEqual(element._currentFix, {}); assert.equal(element._currentFix, undefined);
done(); done();
}); });
}); });
test('apply fix button should call apply ' + test('apply fix button should call apply ' +
'and navigate to change view', done => { 'and navigate to change view', () => {
sinon.stub(element.$.restAPI, 'applyFixSuggestion') sinon.stub(element.$.restAPI, 'applyFixSuggestion')
.returns(Promise.resolve({ok: true})); .returns(Promise.resolve({ok: true}));
sinon.stub(GerritNav, 'navigateToChange'); sinon.stub(GerritNav, 'navigateToChange');
element._currentFix = {fix_id: '123'}; element._currentFix = {fix_id: '123'};
element._handleApplyFix().then(() => { return element._handleApplyFix().then(() => {
assert.isTrue(element.$.restAPI.applyFixSuggestion assert.isTrue(element.$.restAPI.applyFixSuggestion
.calledWithExactly('1', 2, '123')); .calledWithExactly('1', 2, '123'));
assert.isTrue(GerritNav.navigateToChange.calledWithExactly({ assert.isTrue(GerritNav.navigateToChange.calledWithExactly({
@@ -205,9 +205,8 @@ suite('gr-apply-fix-dialog tests', () => {
}, 'edit', 2)); }, 'edit', 2));
// reset gr-apply-fix-dialog and close // reset gr-apply-fix-dialog and close
assert.deepEqual(element._currentFix, {}); assert.equal(element._currentFix, undefined);
assert.equal(element._currentPreviews.length, 0); assert.equal(element._currentPreviews.length, 0);
done();
}); });
}); });

View File

@@ -40,6 +40,7 @@ import {
PatchSetNum, PatchSetNum,
EditPreferencesInfo, EditPreferencesInfo,
Base64FileContent, Base64FileContent,
ChangeNum,
} from '../../../types/common'; } from '../../../types/common';
import {GrStorage} from '../../shared/gr-storage/gr-storage'; import {GrStorage} from '../../shared/gr-storage/gr-storage';
@@ -83,7 +84,7 @@ export class GrEditorView extends KeyboardShortcutMixin(
_change?: ChangeInfo | null; _change?: ChangeInfo | null;
@property({type: Number}) @property({type: Number})
_changeNum?: number; _changeNum?: ChangeNum;
@property({type: String}) @property({type: String})
_patchNum?: PatchSetNum; _patchNum?: PatchSetNum;
@@ -189,7 +190,7 @@ export class GrEditorView extends KeyboardShortcutMixin(
return Promise.all(promises); return Promise.all(promises);
} }
_getChangeDetail(changeNum: number) { _getChangeDetail(changeNum: ChangeNum) {
return this.$.restAPI.getDiffChangeDetail(changeNum).then(change => { return this.$.restAPI.getDiffChangeDetail(changeNum).then(change => {
this._change = 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) { if (patchNum === undefined) {
return Promise.reject(new Error('patchNum undefined')); return Promise.reject(new Error('patchNum undefined'));
} }

View File

@@ -49,6 +49,7 @@ import {
CommentInfo, CommentInfo,
ConfigInfo, ConfigInfo,
AccountDetailInfo, AccountDetailInfo,
ChangeNum,
} from '../../../types/common'; } from '../../../types/common';
import {GrButton} from '../gr-button/gr-button'; import {GrButton} from '../gr-button/gr-button';
import {GrConfirmDeleteCommentDialog} from '../gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog'; 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 Comment = Draft & CommentInfo;
export type RobotComment = Draft & RobotCommentInfo; export type RobotComment = Draft & RobotCommentInfo;
export function isRobotComment(c: Comment | RobotComment): c is RobotComment {
return (c as RobotComment).robot_id !== undefined;
}
interface CommentOverlays { interface CommentOverlays {
confirmDelete?: GrOverlay | null; confirmDelete?: GrOverlay | null;
confirmDiscard?: GrOverlay | null; confirmDiscard?: GrOverlay | null;
@@ -109,6 +114,12 @@ export interface GrComment {
resolvedCheckbox: HTMLInputElement; resolvedCheckbox: HTMLInputElement;
}; };
} }
export interface CommentEventDetail {
patchNum?: PatchSetNum;
comment?: Comment | RobotComment;
}
@customElement('gr-comment') @customElement('gr-comment')
export class GrComment extends KeyboardShortcutMixin( export class GrComment extends KeyboardShortcutMixin(
GestureEventListeners(LegacyElementMixin(PolymerElement)) GestureEventListeners(LegacyElementMixin(PolymerElement))
@@ -160,7 +171,7 @@ export class GrComment extends KeyboardShortcutMixin(
*/ */
@property({type: Number}) @property({type: Number})
changeNum?: number; changeNum?: ChangeNum;
@property({type: Object, notify: true, observer: '_commentChanged'}) @property({type: Object, notify: true, observer: '_commentChanged'})
comment?: Comment | RobotComment; comment?: Comment | RobotComment;
@@ -506,8 +517,8 @@ export class GrComment extends KeyboardShortcutMixin(
); );
} }
_getEventPayload(opt_mixin?: Record<string, any>) { _getEventPayload(): CommentEventDetail {
return {...opt_mixin, comment: this.comment, patchNum: this.patchNum}; return {comment: this.comment, patchNum: this.patchNum};
} }
_fireSave() { _fireSave() {

View File

@@ -130,6 +130,9 @@ import {
SubmittedTogetherInfo, SubmittedTogetherInfo,
ChangeNum, ChangeNum,
EmailAddress, EmailAddress,
FixId,
FilePathToDiffInfoMap,
ChangeViewChangeInfo,
} from '../../../types/common'; } from '../../../types/common';
import { import {
CancelConditionCallback, CancelConditionCallback,
@@ -1388,7 +1391,11 @@ export class GrRestApiInterface
optionsHex, optionsHex,
errFn, errFn,
cancelCondition 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( getRobotCommentFixPreview(
changeNum: ChangeNum, changeNum: ChangeNum,
patchNum: PatchSetNum, patchNum: PatchSetNum,
fixId: string fixId: FixId
) { ): Promise<FilePathToDiffInfoMap | undefined> {
return this._getChangeURLAndFetch({ return this._getChangeURLAndFetch({
changeNum, changeNum,
patchNum, patchNum,
endpoint: `/fixes/${encodeURIComponent(fixId)}/preview`, endpoint: `/fixes/${encodeURIComponent(fixId)}/preview`,
reportEndpointAsId: true, reportEndpointAsId: true,
}); }) as Promise<FilePathToDiffInfoMap | undefined>;
} }
applyFixSuggestion( applyFixSuggestion(
changeNum: ChangeNum, changeNum: ChangeNum,
patchNum: PatchSetNum, patchNum: PatchSetNum,
fixId: string fixId: string
) { ): Promise<Response> {
return this._getChangeURLAndSend({ return this._getChangeURLAndSend({
method: HttpMethod.POST, method: HttpMethod.POST,
changeNum, changeNum,

View File

@@ -21,6 +21,7 @@ import {
AccountInfo, AccountInfo,
ChangeInfo, ChangeInfo,
ChangeMessageInfo, ChangeMessageInfo,
ChangeViewChangeInfo,
ReviewerUpdateInfo, ReviewerUpdateInfo,
Timestamp, Timestamp,
} from '../../../types/common'; } from '../../../types/common';
@@ -30,7 +31,7 @@ import {accountKey} from '../../../utils/account-util';
const MESSAGE_REVIEWERS_THRESHOLD_MILLIS = 500; const MESSAGE_REVIEWERS_THRESHOLD_MILLIS = 500;
const REVIEWER_UPDATE_THRESHOLD_MILLIS = 6000; const REVIEWER_UPDATE_THRESHOLD_MILLIS = 6000;
interface ChangeInfoParserInput extends ChangeInfo { interface ChangeInfoParserInput extends ChangeViewChangeInfo {
messages: ChangeMessageInfo[]; messages: ChangeMessageInfo[];
reviewer_updates: ReviewerUpdateInfo[]; // Always has at least 1 item reviewer_updates: ReviewerUpdateInfo[]; // Always has at least 1 item
} }
@@ -77,7 +78,8 @@ interface UpdateItem {
prev_state?: ReviewerState; prev_state?: ReviewerState;
} }
export interface ParsedChangeInfo extends Omit<ChangeInfo, 'reviewer_updates'> { export interface ParsedChangeInfo
extends Omit<ChangeViewChangeInfo, 'reviewer_updates'> {
reviewer_updates?: ReviewerUpdateInfo[] | FormattedReviewerUpdateInfo[]; reviewer_updates?: ReviewerUpdateInfo[] | FormattedReviewerUpdateInfo[];
} }
@@ -291,7 +293,7 @@ export class GrReviewerUpdatesParser {
} }
static parse( static parse(
change: ChangeInfo | undefined | null change: ChangeViewChangeInfo | undefined | null
): ParsedChangeInfo | undefined | null { ): ParsedChangeInfo | undefined | null {
// TODO(TS): The !change condition should be removed when all files are converted to TS // TODO(TS): The !change condition should be removed when all files are converted to TS
if (!change || !isChangeInfoParserInput(change)) { if (!change || !isChangeInfoParserInput(change)) {

View File

@@ -85,6 +85,8 @@ import {
RelatedChangesInfo, RelatedChangesInfo,
SubmittedTogetherInfo, SubmittedTogetherInfo,
EmailAddress, EmailAddress,
FixId,
FilePathToDiffInfoMap,
} from '../../../types/common'; } from '../../../types/common';
import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser'; import {ParsedChangeInfo} from '../../../elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser';
import {HttpMethod} from '../../../constants/constants'; import {HttpMethod} from '../../../constants/constants';
@@ -718,4 +720,16 @@ export interface RestApiService {
hasPendingDiffDrafts(): number; hasPendingDiffDrafts(): number;
awaitPendingDiffDrafts(): Promise<void>; awaitPendingDiffDrafts(): Promise<void>;
getRobotCommentFixPreview(
changeNum: ChangeNum,
patchNum: PatchSetNum,
fixId: FixId
): Promise<FilePathToDiffInfoMap | undefined>;
applyFixSuggestion(
changeNum: ChangeNum,
patchNum: PatchSetNum,
fixId: string
): Promise<Response>;
} }

View File

@@ -47,6 +47,12 @@ import {
export type BrandType<T, BrandName extends string> = T & export type BrandType<T, BrandName extends string> = T &
{[__brand in BrandName]: never}; {[__brand in BrandName]: never};
/*
* In T, make a set of properties whose keys are in the union K required
*/
export type RequireProperties<T, K extends keyof T> = Omit<T, K> &
Required<Pick<T, K>>;
/** /**
* Type alias for parsed json object to make code cleaner * Type alias for parsed json object to make code cleaner
*/ */
@@ -62,7 +68,7 @@ export const ParentPatchSetNum = 'PARENT' as PatchSetNum;
export type ChangeId = BrandType<string, '_changeId'>; export type ChangeId = BrandType<string, '_changeId'>;
export type ChangeMessageId = BrandType<string, '_changeMessageId'>; export type ChangeMessageId = BrandType<string, '_changeMessageId'>;
export type NumericChangeId = BrandType<number, '_numericChangeId'>; export type NumericChangeId = BrandType<number, '_numericChangeId'>;
export type ChangeNum = number; // !!!TODO: define correct types export type ChangeNum = NumericChangeId; // This type is removed in the following change
export type RepoName = BrandType<string, '_repoName'>; export type RepoName = BrandType<string, '_repoName'>;
export type UrlEncodedRepoName = BrandType<string, '_urlEncodedRepoName'>; export type UrlEncodedRepoName = BrandType<string, '_urlEncodedRepoName'>;
export type TopicName = BrandType<string, '_topicName'>; export type TopicName = BrandType<string, '_topicName'>;
@@ -74,6 +80,8 @@ export type TrackingId = BrandType<string, '_trackingId'>;
export type ReviewInputTag = BrandType<string, '_reviewInputTag'>; export type ReviewInputTag = BrandType<string, '_reviewInputTag'>;
export type RobotId = BrandType<string, '_robotId'>; export type RobotId = BrandType<string, '_robotId'>;
export type RobotRunId = BrandType<string, '_robotRunId'>; export type RobotRunId = BrandType<string, '_robotRunId'>;
// The UUID of the suggested fix.
export type FixId = BrandType<string, '_fixId'>; export type FixId = BrandType<string, '_fixId'>;
export type EmailAddress = BrandType<string, '_emailAddress'>; export type EmailAddress = BrandType<string, '_emailAddress'>;
@@ -237,6 +245,14 @@ export interface ChangeInfo {
internalHost?: string; // TODO(TS): provide an explanation what is its 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. * The AccountInfo entity contains information about an account.
* https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html#account-info * https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html#account-info
@@ -1178,6 +1194,8 @@ export interface DiffInfo {
binary: boolean; binary: boolean;
} }
export type FilePathToDiffInfoMap = {[path: string]: DiffInfo};
/** /**
* The DiffWebLinkInfo entity describes a link on a diff screen to an external site. * 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 * 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 * The FixSuggestionInfo entity represents a suggested fix
* https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#fix-suggestion-info * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#fix-suggestion-info
*/ */
export interface FixSuggestionInfo { export interface FixSuggestionInfoInput {
fix_id?: FixId; description: string;
replacements: FixReplacementInfo[];
}
export interface FixSuggestionInfo extends FixSuggestionInfoInput {
fix_id: FixId;
description: string; description: string;
replacements: FixReplacementInfo[]; replacements: FixReplacementInfo[];
} }

View File

@@ -169,7 +169,9 @@ export function sortRevisions<T extends RevisionInfo>(revisions: T[]): T[] {
* @return Sorted list of patch set objects, as described * @return Sorted list of patch set objects, as described
* above * above
*/ */
export function computeAllPatchSets(change: ParsedChangeInfo): PatchSet[] { export function computeAllPatchSets(
change: ChangeInfo | ParsedChangeInfo
): PatchSet[] {
if (!change) { if (!change) {
return []; return [];
} }
@@ -205,7 +207,7 @@ export function computeAllPatchSets(change: ParsedChangeInfo): PatchSet[] {
* wip property set on each of them * wip property set on each of them
*/ */
function _computeWipForPatchSets( function _computeWipForPatchSets(
change: ParsedChangeInfo, change: ChangeInfo | ParsedChangeInfo,
patchNums: PatchSet[] patchNums: PatchSet[]
) { ) {
if (!change.messages || !change.messages.length) { if (!change.messages || !change.messages.length) {