Merge changes from topic "mark-reviewed"

* changes:
  Replace Mute/Unmute by Mark as Reviewed/Unreviewed (Part 3)
  Replace Mute/Unmute by Mark as Reviewed/Unreviewed (Part 2)
  Replace Mute/Unmute by Mark as Reviewed/Unreviewed (Part 1)
This commit is contained in:
Dave Borowitz
2017-10-01 13:31:30 +00:00
committed by Gerrit Code Review
18 changed files with 331 additions and 164 deletions

View File

@@ -61,18 +61,24 @@ request. They can then decide to remove the ignore star.
The ignore star is represented by the special star label 'ignore'.
[[mute-star]]
== Mute Star
[[reviewed-star]]
== Reviewed Star
If the "mute/<patchset_id>"-star is set by a user, and <patchset_id>
If the "reviewed/<patchset_id>"-star is set by a user, and <patchset_id>
matches the current patch set, the change is always reported as "reviewed"
in the ChangeInfo.
This allows users to "de-highlight" changes in a dashboard until a new
patchset has been uploaded.
The ChangeInfo muted-field will show if the change is currently in a
mute state.
[[unreviewed-star]]
== Unreviewed Star
If the "unreviewed/<patchset_id>"-star is set by a user, and <patchset_id>
matches the current patch set, the change is always reported as "unreviewed"
in the ChangeInfo.
This allows users to "highlight" changes in a dashboard.
[[query-stars]]
== Query Stars

View File

@@ -2329,13 +2329,13 @@ Un-marks a change as ignored.
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/unignore HTTP/1.0
----
[[mute]]
=== Mute
[[mark-as-reviewed]]
=== Mark as Reviewed
--
'PUT /changes/link:#change-id[\{change-id\}]/mute'
'PUT /changes/link:#change-id[\{change-id\}]/reviewed'
--
Marks a change as muted.
Marks a change as reviewed.
This allows users to "de-highlight" changes in their dashboard until a new
patch set is uploaded.
@@ -2347,20 +2347,22 @@ link:#unignore[unignored] again.
.Request
----
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/mute HTTP/1.0
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/reviewed HTTP/1.0
----
[[unmute]]
=== Unmute
[[mark-as-unreviewed]]
=== Mark as Unreviewed
--
'PUT /changes/link:#change-id[\{change-id\}]/unmute'
'PUT /changes/link:#change-id[\{change-id\}]/unreviewed'
--
Unmutes a change.
Marks a change as unreviewed.
This allows users to "highlight" changes in their dashboard
.Request
----
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/unmute HTTP/1.0
PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/unreviewed HTTP/1.0
----
[[get-hashtags]]
@@ -5641,8 +5643,6 @@ A list of star labels that are applied by the calling user to this
change. The labels are lexicographically sorted.
|`reviewed` |not set if `false`|
Whether the change was reviewed by the calling user.
|`muted` |not set if `false`|
Whether the change has been link:#mute[muted] by the calling user.
Only set if link:#reviewed[reviewed] is requested.
|`submit_type` |optional|
The link:project-configuration.html#submit_type[submit type] of the change. +

View File

@@ -3374,7 +3374,7 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
public void mute() throws Exception {
public void markAsReviewed() throws Exception {
TestAccount user2 = accountCreator.user2();
PushOneCommit.Result r = createChange();
@@ -3384,16 +3384,16 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(r.getChangeId()).addReviewer(in);
setApiUser(user);
assertThat(gApi.changes().id(r.getChangeId()).muted()).isFalse();
gApi.changes().id(r.getChangeId()).mute(true);
assertThat(gApi.changes().id(r.getChangeId()).muted()).isTrue();
assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isNull();
gApi.changes().id(r.getChangeId()).markAsReviewed(true);
assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isTrue();
setApiUser(user2);
sender.clear();
amendChange(r.getChangeId());
setApiUser(user);
assertThat(gApi.changes().id(r.getChangeId()).muted()).isFalse();
assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isNull();
List<Message> messages = sender.getMessages();
assertThat(messages).hasSize(1);
@@ -3401,12 +3401,73 @@ public class ChangeIT extends AbstractDaemonTest {
}
@Test
public void cannotMuteOwnChange() throws Exception {
public void cannotSetUnreviewedLabelForPatchSetThatAlreadyHasReviewedLabel() throws Exception {
String changeId = createChange().getChangeId();
setApiUser(user);
gApi.changes().id(changeId).markAsReviewed(true);
assertThat(gApi.changes().id(changeId).get().reviewed).isTrue();
exception.expect(BadRequestException.class);
exception.expectMessage("cannot mute own change");
gApi.changes().id(changeId).mute(true);
exception.expectMessage(
"The labels "
+ StarredChangesUtil.REVIEWED_LABEL
+ "/"
+ 1
+ " and "
+ StarredChangesUtil.UNREVIEWED_LABEL
+ "/"
+ 1
+ " are mutually exclusive. Only one of them can be set.");
gApi.accounts()
.self()
.setStars(
changeId, new StarsInput(ImmutableSet.of(StarredChangesUtil.UNREVIEWED_LABEL + "/1")));
}
@Test
public void cannotSetReviewedLabelForPatchSetThatAlreadyHasUnreviewedLabel() throws Exception {
String changeId = createChange().getChangeId();
setApiUser(user);
gApi.changes().id(changeId).markAsReviewed(false);
assertThat(gApi.changes().id(changeId).get().reviewed).isNull();
exception.expect(BadRequestException.class);
exception.expectMessage(
"The labels "
+ StarredChangesUtil.REVIEWED_LABEL
+ "/"
+ 1
+ " and "
+ StarredChangesUtil.UNREVIEWED_LABEL
+ "/"
+ 1
+ " are mutually exclusive. Only one of them can be set.");
gApi.accounts()
.self()
.setStars(
changeId, new StarsInput(ImmutableSet.of(StarredChangesUtil.REVIEWED_LABEL + "/1")));
}
@Test
public void setReviewedAndUnreviewedLabelsForDifferentPatchSets() throws Exception {
String changeId = createChange().getChangeId();
setApiUser(user);
gApi.changes().id(changeId).markAsReviewed(true);
assertThat(gApi.changes().id(changeId).get().reviewed).isTrue();
amendChange(changeId);
assertThat(gApi.changes().id(changeId).get().reviewed).isNull();
gApi.changes().id(changeId).markAsReviewed(false);
assertThat(gApi.changes().id(changeId).get().reviewed).isNull();
assertThat(gApi.accounts().self().getStars(changeId))
.containsExactly(
StarredChangesUtil.REVIEWED_LABEL + "/" + 1,
StarredChangesUtil.UNREVIEWED_LABEL + "/" + 2);
}
@Test

View File

@@ -119,18 +119,12 @@ public interface ChangeApi {
boolean ignored() throws RestApiException;
/**
* Mute or un-mute this change.
* Mark this change as reviewed/unreviewed.
*
* @param mute mute the change if true
* @param reviewed flag to decide if this change should be marked as reviewed ({@code true}) or
* unreviewed ({@code false})
*/
void mute(boolean mute) throws RestApiException;
/**
* Check if this change is muted.
*
* @return true if the change is muted.
*/
boolean muted() throws RestApiException;
void markAsReviewed(boolean reviewed) throws RestApiException;
/**
* Create a new change that reverts this change.
@@ -583,12 +577,7 @@ public interface ChangeApi {
}
@Override
public void mute(boolean mute) throws RestApiException {
throw new NotImplementedException();
}
@Override
public boolean muted() throws RestApiException {
public void markAsReviewed(boolean reviewed) throws RestApiException {
throw new NotImplementedException();
}

View File

@@ -37,7 +37,6 @@ public class ChangeInfo {
public Timestamp submitted;
public AccountInfo submitter;
public Boolean starred;
public Boolean muted;
public Collection<String> stars;
public Boolean reviewed;
public SubmitType submitType;

View File

@@ -138,8 +138,6 @@ public class ChangeInfo extends JavaScriptObject {
public final native boolean starred() /*-{ return this.starred ? true : false; }-*/;
public final native boolean muted() /*-{ return this.muted ? true : false; }-*/;
public final native boolean reviewed() /*-{ return this.reviewed ? true : false; }-*/;
public final native boolean isPrivate() /*-{ return this.is_private ? true : false; }-*/;

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
import com.google.auto.value.AutoValue;
import com.google.common.base.CharMatcher;
@@ -26,6 +27,7 @@ import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account;
@@ -49,6 +51,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -154,7 +157,8 @@ public class StarredChangesUtil {
public static final String DEFAULT_LABEL = "star";
public static final String IGNORE_LABEL = "ignore";
public static final String MUTE_LABEL = "mute";
public static final String REVIEWED_LABEL = "reviewed";
public static final String UNREVIEWED_LABEL = "unreviewed";
public static final ImmutableSortedSet<String> DEFAULT_LABELS =
ImmutableSortedSet.of(DEFAULT_LABEL);
@@ -330,37 +334,41 @@ public class StarredChangesUtil {
return isIgnoredBy(rsrc.getChange().getId(), rsrc.getUser().asIdentifiedUser().getAccountId());
}
private static String getMuteLabel(Change change) {
return MUTE_LABEL + "/" + change.currentPatchSetId().get();
private static String getReviewedLabel(Change change) {
return getReviewedLabel(change.currentPatchSetId().get());
}
public void mute(ChangeResource rsrc) throws OrmException, IllegalLabelException {
private static String getReviewedLabel(int ps) {
return REVIEWED_LABEL + "/" + ps;
}
private static String getUnreviewedLabel(Change change) {
return getUnreviewedLabel(change.currentPatchSetId().get());
}
private static String getUnreviewedLabel(int ps) {
return UNREVIEWED_LABEL + "/" + ps;
}
public void markAsReviewed(ChangeResource rsrc) throws OrmException, IllegalLabelException {
star(
rsrc.getUser().asIdentifiedUser().getAccountId(),
rsrc.getProject(),
rsrc.getChange().getId(),
ImmutableSet.of(getMuteLabel(rsrc.getChange())),
ImmutableSet.of());
ImmutableSet.of(getReviewedLabel(rsrc.getChange())),
ImmutableSet.of(getUnreviewedLabel(rsrc.getChange())));
}
public void unmute(ChangeResource rsrc) throws OrmException, IllegalLabelException {
public void markAsUnreviewed(ChangeResource rsrc) throws OrmException, IllegalLabelException {
star(
rsrc.getUser().asIdentifiedUser().getAccountId(),
rsrc.getProject(),
rsrc.getChange().getId(),
ImmutableSet.of(),
ImmutableSet.of(getMuteLabel(rsrc.getChange())));
ImmutableSet.of(getUnreviewedLabel(rsrc.getChange())),
ImmutableSet.of(getReviewedLabel(rsrc.getChange())));
}
public boolean isMutedBy(Change change, Account.Id accountId) throws OrmException {
return getLabels(accountId, change.getId()).contains(getMuteLabel(change));
}
public boolean isMuted(ChangeResource rsrc) throws OrmException {
return isMutedBy(rsrc.getChange(), rsrc.getUser().asIdentifiedUser().getAccountId());
}
private static StarRef readLabels(Repository repo, String refName) throws IOException {
public static StarRef readLabels(Repository repo, String refName) throws IOException {
Ref ref = repo.exactRef(refName);
if (ref == null) {
return StarRef.MISSING;
@@ -394,6 +402,25 @@ public class StarredChangesUtil {
if (labels.containsAll(ImmutableSet.of(DEFAULT_LABEL, IGNORE_LABEL))) {
throw new MutuallyExclusiveLabelsException(DEFAULT_LABEL, IGNORE_LABEL);
}
Set<Integer> reviewedPatchSets =
labels
.stream()
.filter(l -> l.startsWith(REVIEWED_LABEL))
.map(l -> Integer.valueOf(l.substring(REVIEWED_LABEL.length() + 1)))
.collect(toSet());
Set<Integer> unreviewedPatchSets =
labels
.stream()
.filter(l -> l.startsWith(UNREVIEWED_LABEL))
.map(l -> Integer.valueOf(l.substring(UNREVIEWED_LABEL.length() + 1)))
.collect(toSet());
Optional<Integer> ps =
Sets.intersection(reviewedPatchSets, unreviewedPatchSets).stream().findFirst();
if (ps.isPresent()) {
throw new MutuallyExclusiveLabelsException(
getReviewedLabel(ps.get()), getUnreviewedLabel(ps.get()));
}
}
private static void validateLabels(Collection<String> labels) throws InvalidLabelsException {

View File

@@ -69,8 +69,9 @@ import com.google.gerrit.server.change.Index;
import com.google.gerrit.server.change.ListChangeComments;
import com.google.gerrit.server.change.ListChangeDrafts;
import com.google.gerrit.server.change.ListChangeRobotComments;
import com.google.gerrit.server.change.MarkAsReviewed;
import com.google.gerrit.server.change.MarkAsUnreviewed;
import com.google.gerrit.server.change.Move;
import com.google.gerrit.server.change.Mute;
import com.google.gerrit.server.change.PostHashtags;
import com.google.gerrit.server.change.PostPrivate;
import com.google.gerrit.server.change.PostReviewers;
@@ -88,7 +89,6 @@ import com.google.gerrit.server.change.SetWorkInProgress;
import com.google.gerrit.server.change.SubmittedTogether;
import com.google.gerrit.server.change.SuggestChangeReviewers;
import com.google.gerrit.server.change.Unignore;
import com.google.gerrit.server.change.Unmute;
import com.google.gerrit.server.change.WorkInProgressOp;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -140,8 +140,8 @@ class ChangeApiImpl implements ChangeApi {
private final DeletePrivate deletePrivate;
private final Ignore ignore;
private final Unignore unignore;
private final Mute mute;
private final Unmute unmute;
private final MarkAsReviewed markAsReviewed;
private final MarkAsUnreviewed markAsUnreviewed;
private final SetWorkInProgress setWip;
private final SetReadyForReview setReady;
private final PutMessage putMessage;
@@ -185,8 +185,8 @@ class ChangeApiImpl implements ChangeApi {
DeletePrivate deletePrivate,
Ignore ignore,
Unignore unignore,
Mute mute,
Unmute unmute,
MarkAsReviewed markAsReviewed,
MarkAsUnreviewed markAsUnreviewed,
SetWorkInProgress setWip,
SetReadyForReview setReady,
PutMessage putMessage,
@@ -228,8 +228,8 @@ class ChangeApiImpl implements ChangeApi {
this.deletePrivate = deletePrivate;
this.ignore = ignore;
this.unignore = unignore;
this.mute = mute;
this.unmute = unmute;
this.markAsReviewed = markAsReviewed;
this.markAsUnreviewed = markAsUnreviewed;
this.setWip = setWip;
this.setReady = setReady;
this.putMessage = putMessage;
@@ -677,26 +677,18 @@ class ChangeApiImpl implements ChangeApi {
}
@Override
public void mute(boolean mute) throws RestApiException {
public void markAsReviewed(boolean reviewed) throws RestApiException {
// TODO(dborowitz): Convert to RetryingRestModifyView. Needs to plumb BatchUpdate.Factory into
// StarredChangesUtil.
try {
if (mute) {
this.mute.apply(change, new Mute.Input());
if (reviewed) {
markAsReviewed.apply(change, new MarkAsReviewed.Input());
} else {
unmute.apply(change, new Unmute.Input());
markAsUnreviewed.apply(change, new MarkAsUnreviewed.Input());
}
} catch (OrmException | IllegalLabelException e) {
throw asRestApiException("Cannot mute change", e);
}
}
@Override
public boolean muted() throws RestApiException {
try {
return stars.isMuted(change);
} catch (OrmException e) {
throw asRestApiException("Cannot check if muted", e);
throw asRestApiException(
"Cannot mark change as " + (reviewed ? "reviewed" : "unreviewed"), e);
}
}

View File

@@ -551,22 +551,13 @@ public class ChangeJson {
if (user.isIdentifiedUser()) {
Collection<String> stars = cd.stars(user.getAccountId());
out.starred = stars.contains(StarredChangesUtil.DEFAULT_LABEL) ? true : null;
out.muted =
stars.contains(StarredChangesUtil.MUTE_LABEL + "/" + cd.currentPatchSet().getPatchSetId())
? true
: null;
if (!stars.isEmpty()) {
out.stars = stars;
}
}
if (in.getStatus().isOpen() && has(REVIEWED) && user.isIdentifiedUser()) {
Account.Id accountId = user.getAccountId();
if (out.muted != null) {
out.reviewed = true;
} else {
out.reviewed = cd.reviewedBy().contains(accountId) ? true : null;
}
out.reviewed = cd.isReviewedBy(user.getAccountId()) ? true : null;
}
out.labels = labelsFor(perm, cd, has(LABELS), has(DETAILED_LABELS));

View File

@@ -14,66 +14,64 @@
package com.google.gerrit.server.change;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class Mute implements RestModifyView<ChangeResource, Mute.Input>, UiAction<ChangeResource> {
private static final Logger log = LoggerFactory.getLogger(Mute.class);
public class MarkAsReviewed
implements RestModifyView<ChangeResource, MarkAsReviewed.Input>, UiAction<ChangeResource> {
private static final Logger log = LoggerFactory.getLogger(MarkAsReviewed.class);
public static class Input {}
private final Provider<ReviewDb> dbProvider;
private final ChangeData.Factory changeDataFactory;
private final StarredChangesUtil stars;
@Inject
Mute(StarredChangesUtil stars) {
MarkAsReviewed(
Provider<ReviewDb> dbProvider,
ChangeData.Factory changeDataFactory,
StarredChangesUtil stars) {
this.dbProvider = dbProvider;
this.changeDataFactory = changeDataFactory;
this.stars = stars;
}
@Override
public Description getDescription(ChangeResource rsrc) {
return new UiAction.Description()
.setLabel("Mute")
.setTitle("Mute the change to unhighlight it in the dashboard")
.setVisible(isMuteable(rsrc));
.setLabel("Mark Reviewed")
.setTitle("Mark the change as reviewed to unhighlight it in the dashboard")
.setVisible(!isReviewed(rsrc));
}
@Override
public Response<String> apply(ChangeResource rsrc, Input input)
throws RestApiException, OrmException, IllegalLabelException {
if (rsrc.isUserOwner()) {
throw new BadRequestException("cannot mute own change");
}
if (!isMuted(rsrc)) {
stars.mute(rsrc);
}
stars.markAsReviewed(rsrc);
return Response.ok("");
}
private boolean isMuted(ChangeResource rsrc) {
private boolean isReviewed(ChangeResource rsrc) {
try {
return stars.isMuted(rsrc);
return changeDataFactory
.create(dbProvider.get(), rsrc.getNotes())
.isReviewedBy(rsrc.getUser().asIdentifiedUser().getAccountId());
} catch (OrmException e) {
log.error("failed to check muted star", e);
}
return false;
}
private boolean isMuteable(ChangeResource rsrc) {
try {
return !rsrc.isUserOwner() && !isMuted(rsrc) && !stars.isIgnored(rsrc);
} catch (OrmException e) {
log.error("failed to check ignored star", e);
log.error("failed to check if change is reviewed", e);
}
return false;
}

View File

@@ -17,50 +17,60 @@ package com.google.gerrit.server.change;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class Unmute
implements RestModifyView<ChangeResource, Unmute.Input>, UiAction<ChangeResource> {
private static final Logger log = LoggerFactory.getLogger(Unmute.class);
public class MarkAsUnreviewed
implements RestModifyView<ChangeResource, MarkAsUnreviewed.Input>, UiAction<ChangeResource> {
private static final Logger log = LoggerFactory.getLogger(MarkAsUnreviewed.class);
public static class Input {}
private final Provider<ReviewDb> dbProvider;
private final ChangeData.Factory changeDataFactory;
private final StarredChangesUtil stars;
@Inject
Unmute(StarredChangesUtil stars) {
MarkAsUnreviewed(
Provider<ReviewDb> dbProvider,
ChangeData.Factory changeDataFactory,
StarredChangesUtil stars) {
this.dbProvider = dbProvider;
this.changeDataFactory = changeDataFactory;
this.stars = stars;
}
@Override
public Description getDescription(ChangeResource rsrc) {
return new UiAction.Description()
.setLabel("Unmute")
.setTitle("Unmute the change")
.setVisible(isMuted(rsrc));
.setLabel("Mark Unreviewed")
.setTitle("Mark the change as unreviewed to highlight it in the dashboard")
.setVisible(isReviewed(rsrc));
}
@Override
public Response<String> apply(ChangeResource rsrc, Input input)
throws OrmException, IllegalLabelException {
if (isMuted(rsrc)) {
stars.unmute(rsrc);
}
stars.markAsUnreviewed(rsrc);
return Response.ok("");
}
private boolean isMuted(ChangeResource rsrc) {
private boolean isReviewed(ChangeResource rsrc) {
try {
return stars.isMuted(rsrc);
return changeDataFactory
.create(dbProvider.get(), rsrc.getNotes())
.isReviewedBy(rsrc.getUser().asIdentifiedUser().getAccountId());
} catch (OrmException e) {
log.error("failed to check muted star", e);
log.error("failed to check if change is reviewed", e);
}
return false;
}

View File

@@ -90,8 +90,8 @@ public class Module extends RestApiModule {
delete(CHANGE_KIND, "private").to(DeletePrivate.class);
put(CHANGE_KIND, "ignore").to(Ignore.class);
put(CHANGE_KIND, "unignore").to(Unignore.class);
put(CHANGE_KIND, "mute").to(Mute.class);
put(CHANGE_KIND, "unmute").to(Unmute.class);
put(CHANGE_KIND, "reviewed").to(MarkAsReviewed.class);
put(CHANGE_KIND, "unreviewed").to(MarkAsUnreviewed.class);
post(CHANGE_KIND, "wip").to(SetWorkInProgress.class);
post(CHANGE_KIND, "ready").to(SetReadyForReview.class);
put(CHANGE_KIND, "message").to(PutMessage.class);

View File

@@ -91,7 +91,10 @@ public class ChangeSchemaDefinitions extends SchemaDefinitions<ChangeData> {
@Deprecated static final Schema<ChangeData> V46 = schema(V45);
// Removal of draft change workflow requires reindexing
static final Schema<ChangeData> V47 = schema(V46);
@Deprecated static final Schema<ChangeData> V47 = schema(V46);
// Rename of star label 'mute' to 'reviewed' requires reindexing
static final Schema<ChangeData> V48 = schema(V47);
public static final String NAME = "changes";
public static final ChangeSchemaDefinitions INSTANCE = new ChangeSchemaDefinitions();

View File

@@ -1084,6 +1084,22 @@ public class ChangeData {
return draftsByUser;
}
public boolean isReviewedBy(Account.Id accountId) throws OrmException {
Collection<String> stars = stars(accountId);
if (stars.contains(
StarredChangesUtil.REVIEWED_LABEL + "/" + currentPatchSet().getPatchSetId())) {
return true;
}
if (stars.contains(
StarredChangesUtil.UNREVIEWED_LABEL + "/" + currentPatchSet().getPatchSetId())) {
return false;
}
return reviewedBy().contains(accountId);
}
public Set<Account.Id> reviewedBy() throws OrmException {
if (reviewedBy == null) {
if (!lazyLoad) {

View File

@@ -35,7 +35,7 @@ import java.util.concurrent.TimeUnit;
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
public static final Class<Schema_160> C = Schema_160.class;
public static final Class<Schema_161> C = Schema_161.class;
public static int getBinaryVersion() {
return guessVersion(C);

View File

@@ -0,0 +1,76 @@
// Copyright (C) 2017 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.
package com.google.gerrit.server.schema;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
import com.google.gerrit.server.StarredChangesUtil.StarRef;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
public class Schema_161 extends SchemaVersion {
private static final String MUTE_LABEL = "mute";
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
@Inject
Schema_161(
Provider<Schema_160> prior, GitRepositoryManager repoManager, AllUsersName allUsersName) {
super(prior);
this.repoManager = repoManager;
this.allUsersName = allUsersName;
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
try (Repository git = repoManager.openRepository(allUsersName);
RevWalk rw = new RevWalk(git)) {
BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
for (Ref ref : git.getRefDatabase().getRefs(RefNames.REFS_STARRED_CHANGES).values()) {
StarRef starRef = StarredChangesUtil.readLabels(git, ref.getName());
if (starRef.labels().contains(MUTE_LABEL)) {
ObjectId id =
StarredChangesUtil.writeLabels(
git,
starRef
.labels()
.stream()
.map(l -> l.equals(MUTE_LABEL) ? StarredChangesUtil.REVIEWED_LABEL : l)
.collect(toList()));
bru.addCommand(new ReceiveCommand(ObjectId.zeroId(), id, ref.getName()));
}
}
bru.execute(rw, new TextProgressMonitor());
} catch (IOException | IllegalLabelException ex) {
throw new OrmException(ex);
}
}
}

View File

@@ -54,15 +54,15 @@
DELETE_EDIT: 'deleteEdit',
IGNORE: 'ignore',
MOVE: 'move',
MUTE: 'mute',
PRIVATE: 'private',
PRIVATE_DELETE: 'private.delete',
PUBLISH_EDIT: 'publishEdit',
REBASE_EDIT: 'rebaseEdit',
RESTORE: 'restore',
REVERT: 'revert',
REVIEWED: 'reviewed',
UNIGNORE: 'unignore',
UNMUTE: 'unmute',
UNREVIEWED: 'unreviewed',
WIP: 'wip',
};
@@ -267,11 +267,11 @@
},
{
type: ActionType.CHANGE,
key: ChangeActions.MUTE,
key: ChangeActions.REVIEWED,
},
{
type: ActionType.CHANGE,
key: ChangeActions.UNMUTE,
key: ChangeActions.UNREVIEWED,
},
{
type: ActionType.CHANGE,

View File

@@ -866,22 +866,22 @@ limitations under the License.
});
});
suite('mute change', () => {
suite('reviewed change', () => {
setup(done => {
sandbox.stub(element, '_fireAction');
const MuteAction = {
__key: 'mute',
const ReviewedAction = {
__key: 'reviewed',
__type: 'change',
__primary: false,
method: 'PUT',
label: 'Mute',
label: 'Mark reviewed',
title: 'Working...',
enabled: true,
};
element.actions = {
mute: MuteAction,
reviewed: ReviewedAction,
};
element.changeNum = '2';
@@ -890,37 +890,38 @@ limitations under the License.
element.reload().then(() => { flush(done); });
});
test('make sure the mute button is not outside of the overflow menu',
test('make sure the reviewed button is not outside of the overflow menu',
() => {
assert.isNotOk(element.$$('[data-action-key="mute"]'));
assert.isNotOk(element.$$('[data-action-key="reviewed"]'));
});
test('muting change', () => {
assert.isOk(element.$.moreActions.$$('span[data-id="mute-change"]'));
element.setActionOverflow('change', 'mute', false);
test('reviewing change', () => {
assert.isOk(
element.$.moreActions.$$('span[data-id="reviewed-change"]'));
element.setActionOverflow('change', 'reviewed', false);
flushAsynchronousOperations();
assert.isOk(element.$$('[data-action-key="mute"]'));
assert.isOk(element.$$('[data-action-key="reviewed"]'));
assert.isNotOk(
element.$.moreActions.$$('span[data-id="mute-change"]'));
element.$.moreActions.$$('span[data-id="reviewed-change"]'));
});
});
suite('unmute change', () => {
suite('unreviewed change', () => {
setup(done => {
sandbox.stub(element, '_fireAction');
const UnmuteAction = {
__key: 'unmute',
const UnreviewedAction = {
__key: 'unreviewed',
__type: 'change',
__primary: false,
method: 'PUT',
label: 'Unmute',
label: 'Mark unreviewed',
title: 'Working...',
enabled: true,
};
element.actions = {
unmute: UnmuteAction,
unreviewed: UnreviewedAction,
};
element.changeNum = '2';
@@ -930,18 +931,18 @@ limitations under the License.
});
test('unmute button not outside of the overflow menu', () => {
assert.isNotOk(element.$$('[data-action-key="unmute"]'));
test('unreviewed button not outside of the overflow menu', () => {
assert.isNotOk(element.$$('[data-action-key="unreviewed"]'));
});
test('unmuting change', () => {
test('unreviewed change', () => {
assert.isOk(
element.$.moreActions.$$('span[data-id="unmute-change"]'));
element.setActionOverflow('change', 'unmute', false);
element.$.moreActions.$$('span[data-id="unreviewed-change"]'));
element.setActionOverflow('change', 'unreviewed', false);
flushAsynchronousOperations();
assert.isOk(element.$$('[data-action-key="unmute"]'));
assert.isOk(element.$$('[data-action-key="unreviewed"]'));
assert.isNotOk(
element.$.moreActions.$$('span[data-id="unmute-change"]'));
element.$.moreActions.$$('span[data-id="unreviewed-change"]'));
});
});