Standardize REST endpoints for getting changes

The /detail and /review endpoints are practically identical to the
standard change endpoint except for the default set of options. Make
this equivalence explicit in the code by delegating to a GetChange
instance from the other handlers.

This change naturally gets caching for the other endpoints as well.

Allow specifying options on /change/X; prior to this, users could add
options to the default set from /detail but could not remove options.

Change-Id: I03bb96141e79ae00359f8d5ec51413d1b27204dc
This commit is contained in:
Dave Borowitz
2013-10-03 09:34:30 -07:00
parent 50ceb44fc3
commit 0314f7364d
5 changed files with 59 additions and 24 deletions

View File

@@ -336,6 +336,11 @@ Get Change
Retrieves a change. Retrieves a change.
Additional fields can be obtained by adding `o` parameters, each
option requires more database lookups and slows down the query
response time to the client so they are generally disabled by
default. Fields are described in link:#list-changes[Query Changes].
.Request .Request
---- ----
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940 HTTP/1.0 GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940 HTTP/1.0

View File

@@ -14,13 +14,30 @@
package com.google.gerrit.server.change; package com.google.gerrit.server.change;
import com.google.gerrit.common.changes.ListChangesOption;
import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.kohsuke.args4j.Option;
import java.util.concurrent.TimeUnit;
public class GetChange implements RestReadView<ChangeResource> { public class GetChange implements RestReadView<ChangeResource> {
private final ChangeJson json; private final ChangeJson json;
@Option(name = "-o", multiValued = true, usage = "Output options")
void addOption(ListChangesOption o) {
json.addOption(o);
}
@Option(name = "-O", usage = "Output option flags, in hex")
void setOptionFlagsHex(String hex) {
json.addOptions(ListChangesOption.fromBits(Integer.parseInt(hex, 16)));
}
@Inject @Inject
GetChange(ChangeJson json) { GetChange(ChangeJson json) {
this.json = json; this.json = json;
@@ -28,6 +45,15 @@ public class GetChange implements RestReadView<ChangeResource> {
@Override @Override
public Object apply(ChangeResource rsrc) throws OrmException { public Object apply(ChangeResource rsrc) throws OrmException {
return json.format(rsrc); return cache(json.format(rsrc));
}
Object apply(RevisionResource rsrc) throws OrmException {
return cache(json.format(rsrc));
}
private Object cache(Object res) {
return Response.ok(res)
.caching(CacheControl.PRIVATE(0, TimeUnit.SECONDS).setMustRevalidate());
} }
} }

View File

@@ -15,41 +15,36 @@
package com.google.gerrit.server.change; package com.google.gerrit.server.change;
import com.google.gerrit.common.changes.ListChangesOption; import com.google.gerrit.common.changes.ListChangesOption;
import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.kohsuke.args4j.Option; import org.kohsuke.args4j.Option;
import java.util.concurrent.TimeUnit;
public class GetDetail implements RestReadView<ChangeResource> { public class GetDetail implements RestReadView<ChangeResource> {
private final ChangeJson json; private final GetChange delegate;
@Option(name = "-o", multiValued = true, usage = "Output options") @Option(name = "-o", multiValued = true, usage = "Output options")
void addOption(ListChangesOption o) { void addOption(ListChangesOption o) {
json.addOption(o); delegate.addOption(o);
} }
@Option(name = "-O", usage = "Output option flags, in hex") @Option(name = "-O", usage = "Output option flags, in hex")
void setOptionFlagsHex(String hex) { void setOptionFlagsHex(String hex) {
json.addOptions(ListChangesOption.fromBits(Integer.parseInt(hex, 16))); delegate.setOptionFlagsHex(hex);
} }
@Inject @Inject
GetDetail(ChangeJson json) { GetDetail(GetChange delegate) {
this.json = json this.delegate = delegate;
.addOption(ListChangesOption.LABELS) delegate.addOption(ListChangesOption.LABELS);
.addOption(ListChangesOption.DETAILED_LABELS) delegate.addOption(ListChangesOption.DETAILED_LABELS);
.addOption(ListChangesOption.DETAILED_ACCOUNTS) delegate.addOption(ListChangesOption.DETAILED_ACCOUNTS);
.addOption(ListChangesOption.MESSAGES); delegate.addOption(ListChangesOption.MESSAGES);
} }
@Override @Override
public Object apply(ChangeResource rsrc) throws OrmException { public Object apply(ChangeResource rsrc) throws OrmException {
return Response.ok(json.format(rsrc)) return delegate.apply(rsrc);
.caching(CacheControl.PRIVATE(0, TimeUnit.SECONDS).setMustRevalidate());
} }
} }

View File

@@ -20,17 +20,17 @@ import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
public class GetReview implements RestReadView<RevisionResource> { public class GetReview implements RestReadView<RevisionResource> {
private final ChangeJson json; private final GetChange delegate;
@Inject @Inject
GetReview(ChangeJson json) { GetReview(GetChange delegate) {
this.json = json this.delegate = delegate;
.addOption(ListChangesOption.DETAILED_LABELS) delegate.addOption(ListChangesOption.DETAILED_LABELS);
.addOption(ListChangesOption.DETAILED_ACCOUNTS); delegate.addOption(ListChangesOption.DETAILED_ACCOUNTS);
} }
@Override @Override
public Object apply(RevisionResource resource) throws OrmException { public Object apply(RevisionResource rsrc) throws OrmException {
return json.format(resource); return delegate.apply(rsrc);
} }
} }

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.change; package com.google.gerrit.server.change;
import com.google.gerrit.extensions.restapi.RestResource; import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestResource.HasETag;
import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Change;
@@ -23,7 +24,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
public class RevisionResource implements RestResource { public class RevisionResource implements RestResource, HasETag {
public static final TypeLiteral<RestView<RevisionResource>> REVISION_KIND = public static final TypeLiteral<RestView<RevisionResource>> REVISION_KIND =
new TypeLiteral<RestView<RevisionResource>>() {}; new TypeLiteral<RestView<RevisionResource>>() {};
@@ -56,6 +57,14 @@ public class RevisionResource implements RestResource {
return ps; return ps;
} }
@Override
public String getETag() {
// Conservative estimate: refresh the revision if its parent change has
// changed, so we don't have to check whether a given modification affected
// this revision specifically.
return change.getETag();
}
Account.Id getAccountId() { Account.Id getAccountId() {
return getUser().getAccountId(); return getUser().getAccountId();
} }