Merge "Add BEFORE_PUBLISH_EDIT plugin event"
diff --git a/polygerrit-ui/app/api/plugin.ts b/polygerrit-ui/app/api/plugin.ts
index 7695b60..6f6dfb6 100644
--- a/polygerrit-ui/app/api/plugin.ts
+++ b/polygerrit-ui/app/api/plugin.ts
@@ -43,6 +43,7 @@
SHOW_DIFF = 'showdiff',
BEFORE_REPLY_SENT = 'before-reply-sent',
REPLY_SENT = 'replysent',
+ BEFORE_PUBLISH_EDIT = 'before-publish-edit',
PUBLISH_EDIT = 'publish-edit',
}
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
index 46da136..12e2715 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
@@ -1510,7 +1510,7 @@
this.handleMoveTap();
break;
case ChangeActions.PUBLISH_EDIT:
- this.handlePublishEditTap();
+ await this.handlePublishEditTap();
break;
case ChangeActions.REBASE_EDIT:
this.handleRebaseEditTap();
@@ -1770,7 +1770,7 @@
);
}
- private handlePublishEditConfirm() {
+ private async handlePublishEditConfirm() {
this.hideAllDialogs();
if (!this.actions.publishEdit) return;
@@ -1779,6 +1779,15 @@
// edit are deleted.
this.getStorage().eraseEditableContentItemsForChangeEdit(this.changeNum);
+ if (
+ !(await this.getPluginLoader().jsApiService.handleBeforePublishEdit(
+ this.change as ChangeInfo
+ ))
+ ) {
+ // Exit early and abort publish if a plugin hook requests it.
+ return;
+ }
+
this.fireAction(
'/edit:publish',
assertUIActionInfo(this.actions.publishEdit),
@@ -2142,7 +2151,7 @@
this.fireAction('/wip', assertUIActionInfo(this.actions.wip), false);
}
- private handlePublishEditTap() {
+ private async handlePublishEditTap() {
if (this.numberOfThreadsWithUnappliedSuggestions() > 0) {
assertIsDefined(
this.confirmPublishEditDialog,
@@ -2151,7 +2160,7 @@
this.showActionDialog(this.confirmPublishEditDialog);
} else {
// Skip confirmation dialog and publish immediately.
- this.handlePublishEditConfirm();
+ await this.handlePublishEditConfirm();
}
}
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 b612f73..7184e00 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
@@ -517,16 +517,27 @@
});
};
- private handlePublishTap = () => {
+ // private but used in test
+ handlePublishTap = async () => {
const changeNum = this.viewState?.changeNum;
assertIsDefined(changeNum, 'change number');
- this.saveEdit().then(() => {
+ await this.saveEdit().then(async () => {
const handleError: ErrorCallback = response => {
this.showAlert(PUBLISH_FAILED_MSG);
this.reporting.error('/edit:publish', new Error(response?.statusText));
};
+ if (
+ !(await this.getPluginLoader().jsApiService.handleBeforePublishEdit(
+ this.change as ChangeInfo
+ ))
+ ) {
+ // The event handler should notify with a more specific
+ // message if it blocks publishing.
+ return;
+ }
+
this.showAlert(PUBLISHING_EDIT_MSG);
// restApiService return undefined if server response with non-200 error
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
index c8b476b..690180c 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view_test.ts
@@ -309,6 +309,7 @@
test('file modification and publish', async () => {
const saveSpy = sinon.spy(element, 'saveEdit');
const alertStub = sinon.stub(element, 'showAlert');
+ const tapSpy = sinon.spy(element, 'handlePublishTap');
const changeActionsStub = stubRestApi('executeChangeAction').resolves();
saveFileStub.returns(Promise.resolve({ok: true}));
element.newContent = newText;
@@ -328,11 +329,12 @@
query<GrButton>(element, '#save')!.hasAttribute('disabled')
);
- return saveSpy.lastCall.returnValue.then(() => {
+ return saveSpy.lastCall.returnValue.then(async () => {
assert.isTrue(saveFileStub.called);
assert.isFalse(element.saving);
assert.equal(alertStub.getCall(1).args[0], 'All changes saved');
+ await tapSpy.lastCall.returnValue;
assert.equal(alertStub.getCall(2).args[0], 'Publishing edit...');
assert.isTrue(
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
index 8417a94..2dca571 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
@@ -81,6 +81,19 @@
return okay;
}
+ async handleBeforePublishEdit(change: ChangeInfo): Promise<boolean> {
+ await this.waitForPluginsToLoad();
+ let okay = true;
+ for (const cb of this._getEventCallbacks(EventType.BEFORE_PUBLISH_EDIT)) {
+ try {
+ okay = (await cb(change)) && okay;
+ } catch (err: unknown) {
+ this.reportError(err, EventType.BEFORE_PUBLISH_EDIT);
+ }
+ }
+ return okay;
+ }
+
handlePublishEdit(change: ChangeInfo, revision?: RevisionInfo | null) {
for (const cb of this._getEventCallbacks(EventType.PUBLISH_EDIT)) {
try {
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-types.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-types.ts
index 9f40f7c..7e7ffba 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-types.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-types.ts
@@ -57,6 +57,13 @@
key: string,
change?: ParsedChangeInfo
): Promise<boolean>;
+ /**
+ * This method is called before publishing a change edit.
+ * It allows plugins to conditionally block edits.
+ * @param change The relevant change.
+ * @return A promise that resolves to true if the action should proceed.
+ */
+ handleBeforePublishEdit(change: ChangeInfo): Promise<boolean>;
handlePublishEdit(change: ChangeInfo, revision?: RevisionInfo | null): void;
handleShowChange(detail: ShowChangeDetail): Promise<void>;
handleShowRevisionActions(detail: ShowRevisionActionsDetail): void;