Merge changes Iee55a0f0,I3cf28f16

* changes:
  Merge branch 'stable-2.15'
  Update JGit to 4.9.2.201712150930-r.175-gd8a24ac1c
This commit is contained in:
David Pursehouse
2017-12-21 23:00:38 +00:00
committed by Gerrit Code Review
34 changed files with 490 additions and 62 deletions

View File

@@ -261,6 +261,33 @@ oldTopic:: Topic name before it was changed.
eventCreatedOn:: Time in seconds since the UNIX epoch when this event was eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
created. created.
=== Work In Progress State Changed
Sent when the the link:intro-user.html#wip[WIP] state of the change has changed.
type:: wip-state-changed
change:: link:json.html#change[change attribute]
changer:: link:json.html#account[account attribute]
eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
created.
=== Private State Changed
Sent when the the link:intro-user.html#private-changes[private] state of the
change has changed.
type:: private-state-changed
change:: link:json.html#change[change attribute]
changer:: link:json.html#account[account attribute]
eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
created.
=== Vote Deleted === Vote Deleted
Sent when a vote was removed from a change. Sent when a vote was removed from a change.

View File

@@ -45,6 +45,12 @@ status:: Current state of this change.
ABANDONED;; Change was abandoned by its owner or administrator. ABANDONED;; Change was abandoned by its owner or administrator.
private:: Boolean indicating if the change is
link:intro-user.html#private-changes[private].
wip:: Boolean indicating if the change is
link:intro-user.html#wip[work in progress].
comments:: All inline/file comments for this change in <<message,message attributes>>. comments:: All inline/file comments for this change in <<message,message attributes>>.
trackingIds:: Issue tracking system links in trackingIds:: Issue tracking system links in

View File

@@ -1386,9 +1386,6 @@ allowed].
|`large_change` || |`large_change` ||
link:config-gerrit.html#change.largeChange[Number of changed lines from link:config-gerrit.html#change.largeChange[Number of changed lines from
which on a change is considered as a large change]. which on a change is considered as a large change].
|`private_by_default` |not set if `false`|
Returns true if changes are by default created as private.
See link:config-gerrit.html#change.privateByDefault[privateByDefault]
|`reply_label` || |`reply_label` ||
link:config-gerrit.html#change.replyTooltip[Label name for the reply link:config-gerrit.html#change.replyTooltip[Label name for the reply
button]. button].

View File

@@ -1165,12 +1165,14 @@ As result a link:#project-access-info[ProjectAccessInfo] entity is returned.
{ {
"remove": [ "remove": [
"refs/*": { {
"permissions": { "refs/*": {
"read": { "permissions": {
"rules": { "read": {
"c2ce4749a32ceb82cd6adcce65b8216e12afb41c": { "rules": {
"action": "ALLOW" "c2ce4749a32ceb82cd6adcce65b8216e12afb41c": {
"action": "ALLOW"
}
} }
} }
} }

View File

@@ -19,7 +19,6 @@ public class ChangeConfigInfo {
public Boolean showAssigneeInChangesTable; public Boolean showAssigneeInChangesTable;
public Boolean allowDrafts; public Boolean allowDrafts;
public int largeChange; public int largeChange;
public Boolean privateByDefault;
public String replyLabel; public String replyLabel;
public String replyTooltip; public String replyTooltip;
public int updateDelay; public int updateDelay;

View File

@@ -0,0 +1,21 @@
// 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.extensions.events;
public interface PrivateStateChangedListener {
interface Event extends ChangeEvent {}
void onPrivateStateChanged(Event event);
}

View File

@@ -0,0 +1,21 @@
// 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.extensions.events;
public interface WorkInProgressStateChangedListener {
interface Event extends ChangeEvent {}
void onWorkInProgressStateChanged(Event event);
}

View File

@@ -31,6 +31,7 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.ChangeResource; import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.index.DummyIndexModule; import com.google.gerrit.server.index.DummyIndexModule;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator; import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
@@ -145,7 +146,12 @@ public class MigrateToNoteDb extends SiteProgram {
// their server is offline. // their server is offline.
List<String> reindexArgs = List<String> reindexArgs =
ImmutableList.of( ImmutableList.of(
"--site-path", getSitePath().toString(), "--threads", Integer.toString(threads)); "--site-path",
getSitePath().toString(),
"--threads",
Integer.toString(threads),
"--index",
ChangeSchemaDefinitions.NAME);
System.out.println("Migration complete, reindexing changes with:"); System.out.println("Migration complete, reindexing changes with:");
System.out.println(" reindex " + reindexArgs.stream().collect(joining(" "))); System.out.println(" reindex " + reindexArgs.stream().collect(joining(" ")));
Reindex reindexPgm = new Reindex(); Reindex reindexPgm = new Reindex();

View File

@@ -40,17 +40,20 @@ public class DeletePrivate
private final ChangeMessagesUtil cmUtil; private final ChangeMessagesUtil cmUtil;
private final Provider<ReviewDb> dbProvider; private final Provider<ReviewDb> dbProvider;
private final PermissionBackend permissionBackend; private final PermissionBackend permissionBackend;
private final SetPrivateOp.Factory setPrivateOpFactory;
@Inject @Inject
DeletePrivate( DeletePrivate(
Provider<ReviewDb> dbProvider, Provider<ReviewDb> dbProvider,
RetryHelper retryHelper, RetryHelper retryHelper,
ChangeMessagesUtil cmUtil, ChangeMessagesUtil cmUtil,
PermissionBackend permissionBackend) { PermissionBackend permissionBackend,
SetPrivateOp.Factory setPrivateOpFactory) {
super(retryHelper); super(retryHelper);
this.dbProvider = dbProvider; this.dbProvider = dbProvider;
this.cmUtil = cmUtil; this.cmUtil = cmUtil;
this.permissionBackend = permissionBackend; this.permissionBackend = permissionBackend;
this.setPrivateOpFactory = setPrivateOpFactory;
} }
@Override @Override
@@ -65,7 +68,7 @@ public class DeletePrivate
throw new ResourceConflictException("change is not private"); throw new ResourceConflictException("change is not private");
} }
SetPrivateOp op = new SetPrivateOp(cmUtil, false, input); SetPrivateOp op = setPrivateOpFactory.create(cmUtil, false, input);
try (BatchUpdate u = try (BatchUpdate u =
updateFactory.create( updateFactory.create(
dbProvider.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) { dbProvider.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {

View File

@@ -32,8 +32,9 @@ public class DeletePrivateByPost extends DeletePrivate implements UiAction<Chang
Provider<ReviewDb> dbProvider, Provider<ReviewDb> dbProvider,
RetryHelper retryHelper, RetryHelper retryHelper,
ChangeMessagesUtil cmUtil, ChangeMessagesUtil cmUtil,
PermissionBackend permissionBackend) { PermissionBackend permissionBackend,
super(dbProvider, retryHelper, cmUtil, permissionBackend); SetPrivateOp.Factory setPrivateOpFactory) {
super(dbProvider, retryHelper, cmUtil, permissionBackend, setPrivateOpFactory);
} }
@Override @Override

View File

@@ -177,6 +177,7 @@ public class Module extends RestApiModule {
factory(ReviewerResource.Factory.class); factory(ReviewerResource.Factory.class);
factory(SetAssigneeOp.Factory.class); factory(SetAssigneeOp.Factory.class);
factory(SetHashtagsOp.Factory.class); factory(SetHashtagsOp.Factory.class);
factory(SetPrivateOp.Factory.class);
factory(WorkInProgressOp.Factory.class); factory(WorkInProgressOp.Factory.class);
} }
} }

View File

@@ -43,17 +43,20 @@ public class PostPrivate
private final ChangeMessagesUtil cmUtil; private final ChangeMessagesUtil cmUtil;
private final Provider<ReviewDb> dbProvider; private final Provider<ReviewDb> dbProvider;
private final PermissionBackend permissionBackend; private final PermissionBackend permissionBackend;
private final SetPrivateOp.Factory setPrivateOpFactory;
@Inject @Inject
PostPrivate( PostPrivate(
Provider<ReviewDb> dbProvider, Provider<ReviewDb> dbProvider,
RetryHelper retryHelper, RetryHelper retryHelper,
ChangeMessagesUtil cmUtil, ChangeMessagesUtil cmUtil,
PermissionBackend permissionBackend) { PermissionBackend permissionBackend,
SetPrivateOp.Factory setPrivateOpFactory) {
super(retryHelper); super(retryHelper);
this.dbProvider = dbProvider; this.dbProvider = dbProvider;
this.cmUtil = cmUtil; this.cmUtil = cmUtil;
this.permissionBackend = permissionBackend; this.permissionBackend = permissionBackend;
this.setPrivateOpFactory = setPrivateOpFactory;
} }
@Override @Override
@@ -68,7 +71,7 @@ public class PostPrivate
return Response.ok(""); return Response.ok("");
} }
SetPrivateOp op = new SetPrivateOp(cmUtil, true, input); SetPrivateOp op = setPrivateOpFactory.create(cmUtil, true, input);
try (BatchUpdate u = try (BatchUpdate u =
updateFactory.create( updateFactory.create(
dbProvider.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) { dbProvider.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {

View File

@@ -19,10 +19,14 @@ import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage; import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.server.ChangeMessagesUtil; import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.extensions.events.PrivateStateChanged;
import com.google.gerrit.server.notedb.ChangeUpdate; import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.update.BatchUpdateOp; import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext; import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.Context;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
public class SetPrivateOp implements BatchUpdateOp { public class SetPrivateOp implements BatchUpdateOp {
public static class Input { public static class Input {
@@ -35,19 +39,32 @@ public class SetPrivateOp implements BatchUpdateOp {
} }
} }
public interface Factory {
SetPrivateOp create(ChangeMessagesUtil cmUtil, boolean isPrivate, Input input);
}
private final ChangeMessagesUtil cmUtil; private final ChangeMessagesUtil cmUtil;
private final boolean isPrivate; private final boolean isPrivate;
private final Input input; private final Input input;
private final PrivateStateChanged privateStateChanged;
SetPrivateOp(ChangeMessagesUtil cmUtil, boolean isPrivate, Input input) { private Change change;
@Inject
SetPrivateOp(
PrivateStateChanged privateStateChanged,
@Assisted ChangeMessagesUtil cmUtil,
@Assisted boolean isPrivate,
@Assisted Input input) {
this.cmUtil = cmUtil; this.cmUtil = cmUtil;
this.isPrivate = isPrivate; this.isPrivate = isPrivate;
this.input = input; this.input = input;
this.privateStateChanged = privateStateChanged;
} }
@Override @Override
public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, OrmException { public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, OrmException {
Change change = ctx.getChange(); change = ctx.getChange();
ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId()); ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
change.setPrivate(isPrivate); change.setPrivate(isPrivate);
change.setLastUpdatedOn(ctx.getWhen()); change.setLastUpdatedOn(ctx.getWhen());
@@ -56,6 +73,11 @@ public class SetPrivateOp implements BatchUpdateOp {
return true; return true;
} }
@Override
public void postUpdate(Context ctx) {
privateStateChanged.fire(change, ctx.getAccount(), ctx.getWhen());
}
private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException { private void addMessage(ChangeContext ctx, ChangeUpdate update) throws OrmException {
Change c = ctx.getChange(); Change c = ctx.getChange();
StringBuilder buf = new StringBuilder(c.isPrivate() ? "Set private" : "Unset private"); StringBuilder buf = new StringBuilder(c.isPrivate() ? "Set private" : "Unset private");

View File

@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeMessagesUtil; import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.PatchSetUtil; import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.extensions.events.WorkInProgressStateChanged;
import com.google.gerrit.server.notedb.ChangeNotes; import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate; import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.update.BatchUpdateOp; import com.google.gerrit.server.update.BatchUpdateOp;
@@ -58,6 +59,7 @@ public class WorkInProgressOp implements BatchUpdateOp {
private final boolean workInProgress; private final boolean workInProgress;
private final Input in; private final Input in;
private final NotifyHandling notify; private final NotifyHandling notify;
private final WorkInProgressStateChanged stateChanged;
private Change change; private Change change;
private ChangeNotes notes; private ChangeNotes notes;
@@ -69,11 +71,13 @@ public class WorkInProgressOp implements BatchUpdateOp {
ChangeMessagesUtil cmUtil, ChangeMessagesUtil cmUtil,
EmailReviewComments.Factory email, EmailReviewComments.Factory email,
PatchSetUtil psUtil, PatchSetUtil psUtil,
WorkInProgressStateChanged stateChanged,
@Assisted boolean workInProgress, @Assisted boolean workInProgress,
@Assisted Input in) { @Assisted Input in) {
this.cmUtil = cmUtil; this.cmUtil = cmUtil;
this.email = email; this.email = email;
this.psUtil = psUtil; this.psUtil = psUtil;
this.stateChanged = stateChanged;
this.workInProgress = workInProgress; this.workInProgress = workInProgress;
this.in = in; this.in = in;
notify = notify =
@@ -121,6 +125,7 @@ public class WorkInProgressOp implements BatchUpdateOp {
@Override @Override
public void postUpdate(Context ctx) { public void postUpdate(Context ctx) {
stateChanged.fire(change, ctx.getAccount(), ctx.getWhen());
if (workInProgress || notify.ordinal() < NotifyHandling.OWNER_REVIEWERS.ordinal()) { if (workInProgress || notify.ordinal() < NotifyHandling.OWNER_REVIEWERS.ordinal()) {
return; return;
} }

View File

@@ -46,6 +46,7 @@ import com.google.gerrit.extensions.events.HeadUpdatedListener;
import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.events.NewProjectCreatedListener; import com.google.gerrit.extensions.events.NewProjectCreatedListener;
import com.google.gerrit.extensions.events.PluginEventListener; import com.google.gerrit.extensions.events.PluginEventListener;
import com.google.gerrit.extensions.events.PrivateStateChangedListener;
import com.google.gerrit.extensions.events.ProjectDeletedListener; import com.google.gerrit.extensions.events.ProjectDeletedListener;
import com.google.gerrit.extensions.events.ProjectIndexedListener; import com.google.gerrit.extensions.events.ProjectIndexedListener;
import com.google.gerrit.extensions.events.ReviewerAddedListener; import com.google.gerrit.extensions.events.ReviewerAddedListener;
@@ -54,6 +55,7 @@ import com.google.gerrit.extensions.events.RevisionCreatedListener;
import com.google.gerrit.extensions.events.TopicEditedListener; import com.google.gerrit.extensions.events.TopicEditedListener;
import com.google.gerrit.extensions.events.UsageDataPublishedListener; import com.google.gerrit.extensions.events.UsageDataPublishedListener;
import com.google.gerrit.extensions.events.VoteDeletedListener; import com.google.gerrit.extensions.events.VoteDeletedListener;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.registration.DynamicSet;
@@ -326,9 +328,11 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), ChangeRestoredListener.class); DynamicSet.setOf(binder(), ChangeRestoredListener.class);
DynamicSet.setOf(binder(), ChangeRevertedListener.class); DynamicSet.setOf(binder(), ChangeRevertedListener.class);
DynamicSet.setOf(binder(), PrivateStateChangedListener.class);
DynamicSet.setOf(binder(), ReviewerAddedListener.class); DynamicSet.setOf(binder(), ReviewerAddedListener.class);
DynamicSet.setOf(binder(), ReviewerDeletedListener.class); DynamicSet.setOf(binder(), ReviewerDeletedListener.class);
DynamicSet.setOf(binder(), VoteDeletedListener.class); DynamicSet.setOf(binder(), VoteDeletedListener.class);
DynamicSet.setOf(binder(), WorkInProgressStateChangedListener.class);
DynamicSet.setOf(binder(), RevisionCreatedListener.class); DynamicSet.setOf(binder(), RevisionCreatedListener.class);
DynamicSet.setOf(binder(), TopicEditedListener.class); DynamicSet.setOf(binder(), TopicEditedListener.class);
DynamicSet.setOf(binder(), AgreementSignupListener.class); DynamicSet.setOf(binder(), AgreementSignupListener.class);

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.server.data;
import com.google.gerrit.extensions.common.PluginDefinedInfo; import com.google.gerrit.extensions.common.PluginDefinedInfo;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
import com.google.gson.annotations.SerializedName;
import java.util.List; import java.util.List;
public class ChangeAttribute { public class ChangeAttribute {
@@ -35,6 +36,10 @@ public class ChangeAttribute {
public Boolean open; public Boolean open;
public Change.Status status; public Change.Status status;
public List<MessageAttribute> comments; public List<MessageAttribute> comments;
public Boolean wip;
@SerializedName("private")
public Boolean isPrivate;
public List<TrackingIdAttribute> trackingIds; public List<TrackingIdAttribute> trackingIds;
public PatchSetAttribute currentPatchSet; public PatchSetAttribute currentPatchSet;

View File

@@ -158,6 +158,8 @@ public class EventFactory {
a.assignee = asAccountAttribute(change.getAssignee()); a.assignee = asAccountAttribute(change.getAssignee());
a.status = change.getStatus(); a.status = change.getStatus();
a.createdOn = change.getCreatedOn().getTime() / 1000L; a.createdOn = change.getCreatedOn().getTime() / 1000L;
a.wip = change.isWorkInProgress() ? true : null;
a.isPrivate = change.isPrivate() ? true : null;
return a; return a;
} }

View File

@@ -30,6 +30,7 @@ public class EventTypes {
register(CommitReceivedEvent.TYPE, CommitReceivedEvent.class); register(CommitReceivedEvent.TYPE, CommitReceivedEvent.class);
register(HashtagsChangedEvent.TYPE, HashtagsChangedEvent.class); register(HashtagsChangedEvent.TYPE, HashtagsChangedEvent.class);
register(PatchSetCreatedEvent.TYPE, PatchSetCreatedEvent.class); register(PatchSetCreatedEvent.TYPE, PatchSetCreatedEvent.class);
register(PrivateStateChangedEvent.TYPE, PrivateStateChangedEvent.class);
register(ProjectCreatedEvent.TYPE, ProjectCreatedEvent.class); register(ProjectCreatedEvent.TYPE, ProjectCreatedEvent.class);
register(RefReceivedEvent.TYPE, RefReceivedEvent.class); register(RefReceivedEvent.TYPE, RefReceivedEvent.class);
register(RefUpdatedEvent.TYPE, RefUpdatedEvent.class); register(RefUpdatedEvent.TYPE, RefUpdatedEvent.class);
@@ -37,6 +38,7 @@ public class EventTypes {
register(ReviewerDeletedEvent.TYPE, ReviewerDeletedEvent.class); register(ReviewerDeletedEvent.TYPE, ReviewerDeletedEvent.class);
register(TopicChangedEvent.TYPE, TopicChangedEvent.class); register(TopicChangedEvent.TYPE, TopicChangedEvent.class);
register(VoteDeletedEvent.TYPE, VoteDeletedEvent.class); register(VoteDeletedEvent.TYPE, VoteDeletedEvent.class);
register(WorkInProgressStateChangedEvent.TYPE, WorkInProgressStateChangedEvent.class);
} }
/** /**

View File

@@ -0,0 +1,28 @@
// 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.events;
import com.google.common.base.Supplier;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class PrivateStateChangedEvent extends ChangeEvent {
static final String TYPE = "private-state-changed";
public Supplier<AccountAttribute> changer;
protected PrivateStateChangedEvent(Change change) {
super(TYPE, change);
}
}

View File

@@ -31,11 +31,13 @@ import com.google.gerrit.extensions.events.CommentAddedListener;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener; import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.events.HashtagsEditedListener; import com.google.gerrit.extensions.events.HashtagsEditedListener;
import com.google.gerrit.extensions.events.NewProjectCreatedListener; import com.google.gerrit.extensions.events.NewProjectCreatedListener;
import com.google.gerrit.extensions.events.PrivateStateChangedListener;
import com.google.gerrit.extensions.events.ReviewerAddedListener; import com.google.gerrit.extensions.events.ReviewerAddedListener;
import com.google.gerrit.extensions.events.ReviewerDeletedListener; import com.google.gerrit.extensions.events.ReviewerDeletedListener;
import com.google.gerrit.extensions.events.RevisionCreatedListener; import com.google.gerrit.extensions.events.RevisionCreatedListener;
import com.google.gerrit.extensions.events.TopicEditedListener; import com.google.gerrit.extensions.events.TopicEditedListener;
import com.google.gerrit.extensions.events.VoteDeletedListener; import com.google.gerrit.extensions.events.VoteDeletedListener;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
@@ -76,6 +78,8 @@ public class StreamEventsApiListener
ChangeAbandonedListener, ChangeAbandonedListener,
ChangeMergedListener, ChangeMergedListener,
ChangeRestoredListener, ChangeRestoredListener,
WorkInProgressStateChangedListener,
PrivateStateChangedListener,
CommentAddedListener, CommentAddedListener,
GitReferenceUpdatedListener, GitReferenceUpdatedListener,
HashtagsEditedListener, HashtagsEditedListener,
@@ -99,11 +103,15 @@ public class StreamEventsApiListener
.to(StreamEventsApiListener.class); .to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), HashtagsEditedListener.class).to(StreamEventsApiListener.class); DynamicSet.bind(binder(), HashtagsEditedListener.class).to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), NewProjectCreatedListener.class).to(StreamEventsApiListener.class); DynamicSet.bind(binder(), NewProjectCreatedListener.class).to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), PrivateStateChangedListener.class)
.to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), ReviewerAddedListener.class).to(StreamEventsApiListener.class); DynamicSet.bind(binder(), ReviewerAddedListener.class).to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), ReviewerDeletedListener.class).to(StreamEventsApiListener.class); DynamicSet.bind(binder(), ReviewerDeletedListener.class).to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), RevisionCreatedListener.class).to(StreamEventsApiListener.class); DynamicSet.bind(binder(), RevisionCreatedListener.class).to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), TopicEditedListener.class).to(StreamEventsApiListener.class); DynamicSet.bind(binder(), TopicEditedListener.class).to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), VoteDeletedListener.class).to(StreamEventsApiListener.class); DynamicSet.bind(binder(), VoteDeletedListener.class).to(StreamEventsApiListener.class);
DynamicSet.bind(binder(), WorkInProgressStateChangedListener.class)
.to(StreamEventsApiListener.class);
} }
} }
@@ -461,6 +469,36 @@ public class StreamEventsApiListener
} }
} }
@Override
public void onWorkInProgressStateChanged(WorkInProgressStateChangedListener.Event ev) {
try {
Change change = getChange(ev.getChange());
WorkInProgressStateChangedEvent event = new WorkInProgressStateChangedEvent(change);
event.change = changeAttributeSupplier(change);
event.changer = accountAttributeSupplier(ev.getWho());
dispatcher.get().postEvent(change, event);
} catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@Override
public void onPrivateStateChanged(PrivateStateChangedListener.Event ev) {
try {
Change change = getChange(ev.getChange());
PrivateStateChangedEvent event = new PrivateStateChangedEvent(change);
event.change = changeAttributeSupplier(change);
event.changer = accountAttributeSupplier(ev.getWho());
dispatcher.get().postEvent(change, event);
} catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@Override @Override
public void onVoteDeleted(VoteDeletedListener.Event ev) { public void onVoteDeleted(VoteDeletedListener.Event ev) {
try { try {

View File

@@ -0,0 +1,28 @@
// 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.events;
import com.google.common.base.Supplier;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.data.AccountAttribute;
public class WorkInProgressStateChangedEvent extends ChangeEvent {
static final String TYPE = "wip-state-changed";
public Supplier<AccountAttribute> changer;
protected WorkInProgressStateChangedEvent(Change change) {
super(TYPE, change);
}
}

View File

@@ -0,0 +1,68 @@
// 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.extensions.events;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.PrivateStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.sql.Timestamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PrivateStateChanged {
private static final Logger log = LoggerFactory.getLogger(PrivateStateChanged.class);
private final DynamicSet<PrivateStateChangedListener> listeners;
private final EventUtil util;
@Inject
PrivateStateChanged(DynamicSet<PrivateStateChangedListener> listeners, EventUtil util) {
this.listeners = listeners;
this.util = util;
}
public void fire(Change change, Account account, Timestamp when) {
if (!listeners.iterator().hasNext()) {
return;
}
try {
Event event = new Event(util.changeInfo(change), util.accountInfo(account), when);
for (PrivateStateChangedListener l : listeners) {
try {
l.onPrivateStateChanged(event);
} catch (Exception e) {
util.logEventListenerError(event, l, e);
}
}
} catch (OrmException e) {
log.error("Couldn't fire event", e);
}
}
private static class Event extends AbstractChangeEvent
implements PrivateStateChangedListener.Event {
protected Event(ChangeInfo change, AccountInfo who, Timestamp when) {
super(change, who, when, NotifyHandling.ALL);
}
}
}

View File

@@ -0,0 +1,69 @@
// 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.extensions.events;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.events.WorkInProgressStateChangedListener;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.sql.Timestamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WorkInProgressStateChanged {
private static final Logger log = LoggerFactory.getLogger(WorkInProgressStateChanged.class);
private final DynamicSet<WorkInProgressStateChangedListener> listeners;
private final EventUtil util;
@Inject
WorkInProgressStateChanged(
DynamicSet<WorkInProgressStateChangedListener> listeners, EventUtil util) {
this.listeners = listeners;
this.util = util;
}
public void fire(Change change, Account account, Timestamp when) {
if (!listeners.iterator().hasNext()) {
return;
}
try {
Event event = new Event(util.changeInfo(change), util.accountInfo(account), when);
for (WorkInProgressStateChangedListener l : listeners) {
try {
l.onWorkInProgressStateChanged(event);
} catch (Exception e) {
util.logEventListenerError(event, l, e);
}
}
} catch (OrmException e) {
log.error("Couldn't fire event", e);
}
}
private static class Event extends AbstractChangeEvent
implements WorkInProgressStateChangedListener.Event {
protected Event(ChangeInfo change, AccountInfo who, Timestamp when) {
super(change, who, when, NotifyHandling.ALL);
}
}
}

View File

@@ -147,7 +147,10 @@ public abstract class AbstractChangeNotes<T> {
throw new OrmException("NoteDb is required to read change " + changeId); throw new OrmException("NoteDb is required to read change " + changeId);
} }
boolean readOrWrite = read || args.migration.rawWriteChangesSetting(); boolean readOrWrite = read || args.migration.rawWriteChangesSetting();
if (!readOrWrite && !autoRebuild) { if (!readOrWrite) {
// Don't even open the repo if we neither write to nor read from NoteDb. It's possible that
// there is some garbage in the noteDbState field and/or the repo, but at this point NoteDb is
// completely off so it's none of our business.
loadDefaults(); loadDefaults();
return self(); return self();
} }

View File

@@ -737,6 +737,9 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
if (state == null) { if (state == null) {
return super.openHandle(repo, id); return super.openHandle(repo, id);
} else if (shouldExist) { } else if (shouldExist) {
// TODO(dborowitz): This means we have a state recorded in noteDbState but the ref doesn't
// exist for whatever reason. Doesn't this mean we should trigger an auto-rebuild, rather
// than throwing?
throw new NoSuchChangeException(getChangeId()); throw new NoSuchChangeException(getChangeId());
} }
} }

View File

@@ -265,7 +265,7 @@ public class PrimaryStorageMigrator {
// the primary storage to NoteDb. // the primary storage to NoteDb.
setPrimaryStorageNoteDb(id, rebuiltState); setPrimaryStorageNoteDb(id, rebuiltState);
log.info("Migrated change {} to NoteDb primary in {}ms", id, sw.elapsed(MILLISECONDS)); log.debug("Migrated change {} to NoteDb primary in {}ms", id, sw.elapsed(MILLISECONDS));
} }
private Change setReadOnlyInReviewDb(Change.Id id) throws OrmException { private Change setReadOnlyInReviewDb(Change.Id id) throws OrmException {
@@ -399,7 +399,7 @@ public class PrimaryStorageMigrator {
rebuilder.rebuildReviewDb(db(), project, id); rebuilder.rebuildReviewDb(db(), project, id);
setPrimaryStorageReviewDb(id, newMetaId); setPrimaryStorageReviewDb(id, newMetaId);
releaseReadOnlyLeaseInNoteDb(project, id); releaseReadOnlyLeaseInNoteDb(project, id);
log.info("Migrated change {} to ReviewDb primary in {}ms", id, sw.elapsed(MILLISECONDS)); log.debug("Migrated change {} to ReviewDb primary in {}ms", id, sw.elapsed(MILLISECONDS));
} }
private ObjectId setReadOnlyInNoteDb(Project.NameKey project, Change.Id id) private ObjectId setReadOnlyInNoteDb(Project.NameKey project, Change.Id id)

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.notedb.rebuild; package com.google.gerrit.server.notedb.rebuild;
import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef; import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS; import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_HASHTAGS;
@@ -228,9 +229,16 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
throw new NoSuchChangeException(changeId); throw new NoSuchChangeException(changeId);
} }
final String oldNoteDbState = change.getNoteDbState(); String oldNoteDbStateStr = change.getNoteDbState();
Result r = manager.stageAndApplyDelta(change); Result r = manager.stageAndApplyDelta(change);
final String newNoteDbState = change.getNoteDbState(); String newNoteDbStateStr = change.getNoteDbState();
if (newNoteDbStateStr == null) {
throw new OrmException(
"Rebuilding change %s produced no writes to NoteDb: "
+ bundleReader.fromReviewDb(db, changeId));
}
NoteDbChangeState newNoteDbState =
checkNotNull(NoteDbChangeState.parse(changeId, newNoteDbStateStr));
try { try {
db.changes() db.changes()
.atomicUpdate( .atomicUpdate(
@@ -241,15 +249,15 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
if (checkReadOnly) { if (checkReadOnly) {
NoteDbChangeState.checkNotReadOnly(change, skewMs); NoteDbChangeState.checkNotReadOnly(change, skewMs);
} }
String currNoteDbState = change.getNoteDbState(); String currNoteDbStateStr = change.getNoteDbState();
if (Objects.equals(currNoteDbState, newNoteDbState)) { if (Objects.equals(currNoteDbStateStr, newNoteDbStateStr)) {
// Another thread completed the same rebuild we were about to. // Another thread completed the same rebuild we were about to.
throw new AbortUpdateException(); throw new AbortUpdateException();
} else if (!Objects.equals(oldNoteDbState, currNoteDbState)) { } else if (!Objects.equals(oldNoteDbStateStr, currNoteDbStateStr)) {
// Another thread updated the state to something else. // Another thread updated the state to something else.
throw new ConflictingUpdateRuntimeException(change, oldNoteDbState); throw new ConflictingUpdateRuntimeException(change, oldNoteDbStateStr);
} }
change.setNoteDbState(newNoteDbState); change.setNoteDbState(newNoteDbStateStr);
return change; return change;
} }
}); });
@@ -259,10 +267,9 @@ public class ChangeRebuilderImpl extends ChangeRebuilder {
// rebuild had executed before the other thread. // rebuild had executed before the other thread.
throw new ConflictingUpdateException(e); throw new ConflictingUpdateException(e);
} catch (AbortUpdateException e) { } catch (AbortUpdateException e) {
if (NoteDbChangeState.parse(changeId, newNoteDbState) if (newNoteDbState.isUpToDate(
.isUpToDate( manager.getChangeRepo().cmds.getRepoRefCache(),
manager.getChangeRepo().cmds.getRepoRefCache(), manager.getAllUsersRepo().cmds.getRepoRefCache())) {
manager.getAllUsersRepo().cmds.getRepoRefCache())) {
// If the state in ReviewDb matches NoteDb at this point, it means another thread // If the state in ReviewDb matches NoteDb at this point, it means another thread
// successfully completed this rebuild. It's ok to not execute the update in this case, // successfully completed this rebuild. It's ok to not execute the update in this case,
// since the object referenced in the Result was flushed to the repo by whatever thread won // since the object referenced in the Result was flushed to the repo by whatever thread won

View File

@@ -91,6 +91,7 @@ import java.util.function.Predicate;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.internal.storage.file.FileRepository; import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.internal.storage.file.PackInserter;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ObjectReader;
@@ -748,9 +749,12 @@ public class NoteDbMigrator implements AutoCloseable {
} }
private static ObjectInserter newPackInserter(Repository repo) { private static ObjectInserter newPackInserter(Repository repo) {
return repo instanceof FileRepository if (!(repo instanceof FileRepository)) {
? ((FileRepository) repo).getObjectDatabase().newPackInserter() return repo.newObjectInserter();
: repo.newObjectInserter(); }
PackInserter ins = ((FileRepository) repo).getObjectDatabase().newPackInserter();
ins.checkExisting(false);
return ins;
} }
private boolean rebuildProject( private boolean rebuildProject(

View File

@@ -239,7 +239,6 @@ public class GetServerInfo implements RestReadView<ConfigResource> {
toBoolean( toBoolean(
cfg.getBoolean("change", "showAssigneeInChangesTable", false) && hasAssigneeInIndex); cfg.getBoolean("change", "showAssigneeInChangesTable", false) && hasAssigneeInIndex);
info.largeChange = cfg.getInt("change", "largeChange", 500); info.largeChange = cfg.getInt("change", "largeChange", 500);
info.privateByDefault = toBoolean(cfg.getBoolean("change", "privateByDefault", false));
info.replyTooltip = info.replyTooltip =
Optional.ofNullable(cfg.getString("change", null, "replyTooltip")).orElse("Reply and score") Optional.ofNullable(cfg.getString("change", null, "replyTooltip")).orElse("Reply and score")
+ " (Shortcut: a)"; + " (Shortcut: a)";

View File

@@ -14,6 +14,9 @@
package com.google.gerrit.server.schema; package com.google.gerrit.server.schema;
import static java.util.stream.Collectors.toMap;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.GerritPersonIdent;
@@ -22,23 +25,40 @@ import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TextProgressMonitor; import org.eclipse.jgit.lib.TextProgressMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Migrate accounts to NoteDb. */ /** Migrate accounts to NoteDb. */
public class Schema_154 extends SchemaVersion { public class Schema_154 extends SchemaVersion {
private static final Logger log = LoggerFactory.getLogger(Schema_154.class);
private static final String TABLE = "accounts";
private static final Map<String, AccountSetter> ACCOUNT_FIELDS_MAP =
ImmutableMap.<String, AccountSetter>builder()
.put("full_name", (a, rs, field) -> a.setFullName(rs.getString(field)))
.put("preferred_email", (a, rs, field) -> a.setPreferredEmail(rs.getString(field)))
.put("status", (a, rs, field) -> a.setStatus(rs.getString(field)))
.put("inactive", (a, rs, field) -> a.setActive(rs.getString(field).equals("N")))
.build();
private final GitRepositoryManager repoManager; private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName; private final AllUsersName allUsersName;
private final Provider<PersonIdent> serverIdent; private final Provider<PersonIdent> serverIdent;
@@ -76,23 +96,24 @@ public class Schema_154 extends SchemaVersion {
} }
private Set<Account> scanAccounts(ReviewDb db, ProgressMonitor pm) throws SQLException { private Set<Account> scanAccounts(ReviewDb db, ProgressMonitor pm) throws SQLException {
Map<String, AccountSetter> fields = getFields(db);
if (fields.isEmpty()) {
log.warn("Only account_id and registered_on fields are migrated for accounts");
}
List<String> queryFields = new ArrayList<>();
queryFields.add("account_id");
queryFields.add("registered_on");
queryFields.addAll(fields.keySet());
String query = "SELECT " + String.join(", ", queryFields) + String.format(" FROM %s", TABLE);
try (Statement stmt = newStatement(db); try (Statement stmt = newStatement(db);
ResultSet rs = ResultSet rs = stmt.executeQuery(query)) {
stmt.executeQuery(
"SELECT account_id,"
+ " registered_on,"
+ " full_name, "
+ " preferred_email,"
+ " status,"
+ " inactive"
+ " FROM accounts")) {
Set<Account> s = new HashSet<>(); Set<Account> s = new HashSet<>();
while (rs.next()) { while (rs.next()) {
Account a = new Account(new Account.Id(rs.getInt(1)), rs.getTimestamp(2)); Account a = new Account(new Account.Id(rs.getInt(1)), rs.getTimestamp(2));
a.setFullName(rs.getString(3)); for (Map.Entry<String, AccountSetter> field : fields.entrySet()) {
a.setPreferredEmail(rs.getString(4)); field.getValue().set(a, rs, field.getKey());
a.setStatus(rs.getString(5)); }
a.setActive(rs.getString(6).equals("N"));
s.add(a); s.add(a);
pm.update(1); pm.update(1);
} }
@@ -100,6 +121,17 @@ public class Schema_154 extends SchemaVersion {
} }
} }
private Map<String, AccountSetter> getFields(ReviewDb db) throws SQLException {
JdbcSchema schema = (JdbcSchema) db;
Connection connection = schema.getConnection();
Set<String> columns = schema.getDialect().listColumns(connection, TABLE);
return ACCOUNT_FIELDS_MAP
.entrySet()
.stream()
.filter(e -> columns.contains(e.getKey()))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
}
private void updateAccountInNoteDb(Repository allUsersRepo, Account account) private void updateAccountInNoteDb(Repository allUsersRepo, Account account)
throws IOException, ConfigInvalidException { throws IOException, ConfigInvalidException {
MetaDataUpdate md = MetaDataUpdate md =
@@ -112,4 +144,9 @@ public class Schema_154 extends SchemaVersion {
accountConfig.setAccount(account); accountConfig.setAccount(account);
accountConfig.commit(md); accountConfig.commit(md);
} }
@FunctionalInterface
private interface AccountSetter {
void set(Account a, ResultSet rs, String field) throws SQLException;
}
} }

View File

@@ -56,7 +56,6 @@ public class ServerInfoIT extends AbstractDaemonTest {
// change // change
@GerritConfig(name = "change.allowDrafts", value = "false") @GerritConfig(name = "change.allowDrafts", value = "false")
@GerritConfig(name = "change.largeChange", value = "300") @GerritConfig(name = "change.largeChange", value = "300")
@GerritConfig(name = "change.privateByDefault", value = "true")
@GerritConfig(name = "change.replyTooltip", value = "Publish votes and draft comments") @GerritConfig(name = "change.replyTooltip", value = "Publish votes and draft comments")
@GerritConfig(name = "change.replyLabel", value = "Vote") @GerritConfig(name = "change.replyLabel", value = "Vote")
@GerritConfig(name = "change.updateDelay", value = "50s") @GerritConfig(name = "change.updateDelay", value = "50s")
@@ -101,7 +100,6 @@ public class ServerInfoIT extends AbstractDaemonTest {
// change // change
assertThat(i.change.allowDrafts).isNull(); assertThat(i.change.allowDrafts).isNull();
assertThat(i.change.largeChange).isEqualTo(300); assertThat(i.change.largeChange).isEqualTo(300);
assertThat(i.change.privateByDefault).isTrue();
assertThat(i.change.replyTooltip).startsWith("Publish votes and draft comments"); assertThat(i.change.replyTooltip).startsWith("Publish votes and draft comments");
assertThat(i.change.replyLabel).isEqualTo("Vote\u2026"); assertThat(i.change.replyLabel).isEqualTo("Vote\u2026");
assertThat(i.change.updateDelay).isEqualTo(50); assertThat(i.change.updateDelay).isEqualTo(50);

View File

@@ -1287,6 +1287,28 @@ public class ChangeRebuilderIT extends AbstractDaemonTest {
assertThat(newPs3.getCreatedOn()).isGreaterThan(ps1.getCreatedOn()); assertThat(newPs3.getCreatedOn()).isGreaterThan(ps1.getCreatedOn());
} }
@Test
public void ignoreNoteDbStateWithNoCorrespondingRefWhenWritesAndReadsDisabled() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
ReviewDb db = getUnwrappedDb();
Change c = db.changes().get(id);
c.setNoteDbState("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
db.changes().update(Collections.singleton(c));
c = db.changes().get(id);
String refName = RefNames.changeMetaRef(id);
assertThat(getMetaRef(project, refName)).isNull();
ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
assertThat(notes.getChange().getRowVersion()).isEqualTo(c.getRowVersion());
notes = notesFactory.createChecked(dbProvider.get(), project, id);
assertThat(notes.getChange().getRowVersion()).isEqualTo(c.getRowVersion());
assertThat(getMetaRef(project, refName)).isNull();
}
private void assertChangesReadOnly(RestApiException e) throws Exception { private void assertChangesReadOnly(RestApiException e) throws Exception {
Throwable cause = e.getCause(); Throwable cause = e.getCause();
assertThat(cause).isInstanceOf(UpdateException.class); assertThat(cause).isInstanceOf(UpdateException.class);

View File

@@ -1,6 +1,6 @@
load("//tools/bzl:maven_jar.bzl", "GERRIT", "MAVEN_LOCAL", "MAVEN_CENTRAL", "maven_jar") load("//tools/bzl:maven_jar.bzl", "GERRIT", "MAVEN_LOCAL", "MAVEN_CENTRAL", "maven_jar")
_JGIT_VERS = "4.9.2.201712150930-r.171-gfdbaa25db" _JGIT_VERS = "4.9.2.201712150930-r.175-gd8a24ac1c"
_DOC_VERS = "4.9.2.201712150930-r" # Set to _JGIT_VERS unless using a snapshot _DOC_VERS = "4.9.2.201712150930-r" # Set to _JGIT_VERS unless using a snapshot
@@ -26,28 +26,28 @@ def jgit_maven_repos():
name = "jgit_lib", name = "jgit_lib",
artifact = "org.eclipse.jgit:org.eclipse.jgit:" + _JGIT_VERS, artifact = "org.eclipse.jgit:org.eclipse.jgit:" + _JGIT_VERS,
repository = _JGIT_REPO, repository = _JGIT_REPO,
sha1 = "29b822410b29286a09df728f8379e5cb8b1a486e", sha1 = "4286555f5851fbfcf0ff89ec884f7f806b0c7e37",
src_sha1 = "5106b81910a057470cfd2584d9cb3502bcbebbc2", src_sha1 = "5e38b7e7936ebbd778914dc4f9d76d245a5a4518",
unsign = True, unsign = True,
) )
maven_jar( maven_jar(
name = "jgit_servlet", name = "jgit_servlet",
artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + _JGIT_VERS, artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + _JGIT_VERS,
repository = _JGIT_REPO, repository = _JGIT_REPO,
sha1 = "01f6718f6b629e28caad38e00190811b38574e74", sha1 = "da0d2c7a048cc213274cd06a5baf277c85ea152e",
unsign = True, unsign = True,
) )
maven_jar( maven_jar(
name = "jgit_archive", name = "jgit_archive",
artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + _JGIT_VERS, artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + _JGIT_VERS,
repository = _JGIT_REPO, repository = _JGIT_REPO,
sha1 = "9c9e9332e7dc724dbe1837e21feccd98bc25e6b4", sha1 = "1cd91bedf8b591626d341c2d896181ddba5f9aa9",
) )
maven_jar( maven_jar(
name = "jgit_junit", name = "jgit_junit",
artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + _JGIT_VERS, artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + _JGIT_VERS,
repository = _JGIT_REPO, repository = _JGIT_REPO,
sha1 = "4154c70b78b62035dad446332b24f7816b7a2a1b", sha1 = "5b7cc1aa0ba062ad587b6daa64743b704b997f74",
unsign = True, unsign = True,
) )

View File

@@ -45,10 +45,7 @@ def gerrit_plugin(
native.java_binary( native.java_binary(
name = '%s__non_stamped' % name, name = '%s__non_stamped' % name,
deploy_manifest_lines = manifest_entries + [ deploy_manifest_lines = manifest_entries + ["Gerrit-ApiType: plugin"],
"Gerrit-ApiType: plugin",
"Implementation-Vendor: Gerrit Code Review",
],
main_class = 'Dummy', main_class = 'Dummy',
runtime_deps = [ runtime_deps = [
':%s__plugin' % name, ':%s__plugin' % name,