Merge "Only show Create Flow elements if user is the uploader."
diff --git a/polygerrit-ui/app/elements/change/gr-flows/gr-flows.ts b/polygerrit-ui/app/elements/change/gr-flows/gr-flows.ts
index 2692136..2d9361a 100644
--- a/polygerrit-ui/app/elements/change/gr-flows/gr-flows.ts
+++ b/polygerrit-ui/app/elements/change/gr-flows/gr-flows.ts
@@ -10,7 +10,12 @@
import {resolve} from '../../../models/dependency';
import {changeModelToken} from '../../../models/change/change-model';
import {subscribe} from '../../lit/subscription-controller';
-import {FlowInfo, FlowStageState} from '../../../api/rest-api';
+import {
+ AccountDetailInfo,
+ AccountId,
+ FlowInfo,
+ FlowStageState,
+} from '../../../api/rest-api';
import {flowsModelToken} from '../../../models/flows/flows-model';
import {NumericChangeId} from '../../../types/common';
import './gr-create-flow';
@@ -19,6 +24,7 @@
import '@material/web/select/filled-select';
import '@material/web/select/select-option';
import {computeFlowStringFromFlowStageInfo} from '../../../utils/flows-util';
+import {userModelToken} from '../../../models/user/user-model';
const iconForFlowStageState = (status: FlowStageState) => {
switch (status) {
@@ -44,6 +50,10 @@
@state() private changeNum?: NumericChangeId;
+ @state() private changeUploader?: AccountId;
+
+ @state() private account?: AccountDetailInfo;
+
@state() private loading = true;
@state() private flowIdToDelete?: string;
@@ -52,6 +62,8 @@
private readonly getChangeModel = resolve(this, changeModelToken);
+ private readonly getUserModel = resolve(this, userModelToken);
+
private readonly getFlowsModel = resolve(this, flowsModelToken);
static override get styles() {
@@ -62,6 +74,9 @@
.container {
padding: var(--spacing-l);
}
+ b {
+ font-weight: bolder;
+ }
hr {
margin-top: var(--spacing-l);
margin-bottom: var(--spacing-l);
@@ -139,6 +154,21 @@
);
subscribe(
this,
+ () => this.getChangeModel().change$,
+ change => {
+ this.changeUploader =
+ change?.revisions[change?.current_revision].uploader?._account_id;
+ }
+ );
+ subscribe(
+ this,
+ () => this.getUserModel().account$,
+ account => {
+ this.account = account;
+ }
+ );
+ subscribe(
+ this,
() => this.getFlowsModel().flows$,
flows => {
this.flows = flows;
@@ -172,8 +202,14 @@
override render() {
return html`
<div class="container">
- <h2 class="main-heading">Create new flow</h2>
- <gr-create-flow .changeNum=${this.changeNum}></gr-create-flow>
+ ${when(
+ this.showCreateFlow(),
+ () =>
+ html`<h2 class="main-heading">Create new flow</h2>
+ <gr-create-flow .changeNum=${this.changeNum}></gr-create-flow>`,
+ () =>
+ html`<b>Note:</b> New flows can only be added by change uploader.`
+ )}
<hr />
${this.renderFlowsList()}
</div>
@@ -196,6 +232,13 @@
</dialog>`;
}
+ private showCreateFlow() {
+ return (
+ this.account?._account_id !== undefined &&
+ this.account._account_id === this.changeUploader
+ );
+ }
+
private renderStatus(stage: FlowInfo['stages'][0]): TemplateResult {
const icon = iconForFlowStageState(stage.state);
return html`<gr-icon
diff --git a/polygerrit-ui/app/elements/change/gr-flows/gr-flows_test.ts b/polygerrit-ui/app/elements/change/gr-flows/gr-flows_test.ts
index 4a15840..6cba4d9 100644
--- a/polygerrit-ui/app/elements/change/gr-flows/gr-flows_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-flows/gr-flows_test.ts
@@ -7,30 +7,74 @@
import './gr-flows';
import {assert, fixture, html} from '@open-wc/testing';
import {GrFlows} from './gr-flows';
-import {FlowInfo, FlowStageState, Timestamp} from '../../../api/rest-api';
+import {
+ AccountId,
+ CommitId,
+ FlowInfo,
+ FlowStageState,
+ Timestamp,
+} from '../../../api/rest-api';
import {queryAndAssert} from '../../../test/test-utils';
import {NumericChangeId} from '../../../types/common';
import sinon from 'sinon';
import {GrButton} from '../../shared/gr-button/gr-button';
import {GrDialog} from '../../shared/gr-dialog/gr-dialog';
import {FlowsModel, flowsModelToken} from '../../../models/flows/flows-model';
+import {
+ ChangeModel,
+ changeModelToken,
+} from '../../../models/change/change-model';
+import {UserModel, userModelToken} from '../../../models/user/user-model';
import {testResolver} from '../../../test/common-test-setup';
+import {
+ createAccountDetailWithId,
+ createParsedChange,
+ createRevision,
+} from '../../../test/test-data-generators';
+
+function setChangeWithUploader(
+ changeModel: ChangeModel,
+ uploaderId: AccountId
+) {
+ changeModel.updateState({
+ change: {
+ ...createParsedChange(),
+ _number: 123 as NumericChangeId,
+ revisions: {
+ rev1: {
+ ...createRevision(1),
+ uploader: createAccountDetailWithId(uploaderId),
+ },
+ },
+ current_revision: 'rev1' as CommitId,
+ },
+ });
+}
suite('gr-flows tests', () => {
let element: GrFlows;
let clock: sinon.SinonFakeTimers;
let flowsModel: FlowsModel;
+ let changeModel: ChangeModel;
+ let userModel: UserModel;
setup(async () => {
clock = sinon.useFakeTimers();
+ changeModel = testResolver(changeModelToken);
+ userModel = testResolver(userModelToken);
flowsModel = testResolver(flowsModelToken);
// The model is created by the DI system. The test setup replaces the real
// model with a mock. To prevent real API calls, we stub the reload method.
sinon.stub(flowsModel, 'reload');
element = await fixture<GrFlows>(html`<gr-flows></gr-flows>`);
- element['changeNum'] = 123 as NumericChangeId;
+ await element.updateComplete;
+ setChangeWithUploader(changeModel, 123 as AccountId);
+ userModel.setState({
+ account: createAccountDetailWithId(123 as AccountId),
+ accountLoaded: true,
+ });
await element.updateComplete;
});
@@ -442,4 +486,39 @@
assert.equal(flowElements.length, 4);
});
});
+
+ suite('create flow visibility', () => {
+ setup(async () => {
+ flowsModel.setState({flows: [], loading: false, isEnabled: true});
+ await element.updateComplete;
+ });
+
+ test('shows gr-create-flow when current user is uploader', async () => {
+ const uploaderId = 123 as AccountId;
+ const currentUserId = 123 as AccountId;
+ setChangeWithUploader(changeModel, uploaderId);
+ userModel.setState({
+ account: createAccountDetailWithId(currentUserId),
+ accountLoaded: true,
+ });
+ await element.updateComplete;
+
+ const createFlow = element.shadowRoot!.querySelector('gr-create-flow');
+ assert.isNotNull(createFlow);
+ });
+
+ test('hides gr-create-flow when current user is not uploader', async () => {
+ const uploaderId = 456 as AccountId;
+ const currentUserId = 123 as AccountId;
+ setChangeWithUploader(changeModel, uploaderId);
+ userModel.setState({
+ account: createAccountDetailWithId(currentUserId),
+ accountLoaded: true,
+ });
+ await element.updateComplete;
+
+ const createFlow = element.shadowRoot!.querySelector('gr-create-flow');
+ assert.isNull(createFlow);
+ });
+ });
});