Move stream-based QueryProcessor output to its own class
QueryProcessor now has a much more reasonable scope of just turning lists of queries into QueryResults. Change-Id: I306b72264b5d9398a12a22f26d4e5397b0dd8a11
This commit is contained in:
@@ -14,8 +14,8 @@
|
||||
|
||||
package com.google.gerrit.httpd.rpc.change;
|
||||
|
||||
import com.google.gerrit.server.query.change.QueryProcessor;
|
||||
import com.google.gerrit.server.query.change.QueryProcessor.OutputFormat;
|
||||
import com.google.gerrit.server.query.change.OutputStreamQuery;
|
||||
import com.google.gerrit.server.query.change.OutputStreamQuery.OutputFormat;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -31,11 +31,11 @@ import javax.servlet.http.HttpServletResponse;
|
||||
@Singleton
|
||||
public class DeprecatedChangeQueryServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Provider<QueryProcessor> processor;
|
||||
private final Provider<OutputStreamQuery> queryProvider;
|
||||
|
||||
@Inject
|
||||
DeprecatedChangeQueryServlet(Provider<QueryProcessor> processor) {
|
||||
this.processor = processor;
|
||||
DeprecatedChangeQueryServlet(Provider<OutputStreamQuery> queryProvider) {
|
||||
this.queryProvider = queryProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +44,7 @@ public class DeprecatedChangeQueryServlet extends HttpServlet {
|
||||
rsp.setContentType("text/json");
|
||||
rsp.setCharacterEncoding("UTF-8");
|
||||
|
||||
QueryProcessor p = processor.get();
|
||||
OutputStreamQuery p = queryProvider.get();
|
||||
OutputFormat format = OutputFormat.JSON;
|
||||
try {
|
||||
format = OutputFormat.valueOf(get(req, "format", format.toString()));
|
||||
|
@@ -0,0 +1,408 @@
|
||||
// Copyright (C) 2014 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.query.change;
|
||||
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.common.data.LabelTypes;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.config.TrackingFooters;
|
||||
import com.google.gerrit.server.data.ChangeAttribute;
|
||||
import com.google.gerrit.server.data.PatchSetAttribute;
|
||||
import com.google.gerrit.server.data.QueryStatsAttribute;
|
||||
import com.google.gerrit.server.events.EventFactory;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.project.SubmitRuleEvaluator;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.eclipse.jgit.util.io.DisabledOutputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Change query implementation that outputs to a stream in the style of an SSH
|
||||
* command.
|
||||
*/
|
||||
public class OutputStreamQuery {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(OutputStreamQuery.class);
|
||||
|
||||
public static enum OutputFormat {
|
||||
TEXT, JSON
|
||||
}
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
private final SimpleDateFormat sdf =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz");
|
||||
|
||||
private final QueryProcessor queryProcessor;
|
||||
private final EventFactory eventFactory;
|
||||
private final TrackingFooters trackingFooters;
|
||||
private final CurrentUser user;
|
||||
|
||||
private OutputFormat outputFormat = OutputFormat.TEXT;
|
||||
private boolean includePatchSets;
|
||||
private boolean includeCurrentPatchSet;
|
||||
private boolean includeApprovals;
|
||||
private boolean includeComments;
|
||||
private boolean includeFiles;
|
||||
private boolean includeCommitMessage;
|
||||
private boolean includeDependencies;
|
||||
private boolean includeSubmitRecords;
|
||||
private boolean includeAllReviewers;
|
||||
|
||||
private OutputStream outputStream = DisabledOutputStream.INSTANCE;
|
||||
private PrintWriter out;
|
||||
|
||||
@Inject
|
||||
OutputStreamQuery(QueryProcessor queryProcessor,
|
||||
EventFactory eventFactory,
|
||||
TrackingFooters trackingFooters,
|
||||
CurrentUser user) {
|
||||
this.queryProcessor = queryProcessor;
|
||||
this.eventFactory = eventFactory;
|
||||
this.trackingFooters = trackingFooters;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
void setLimit(int n) {
|
||||
queryProcessor.setLimit(n);
|
||||
}
|
||||
|
||||
public void setStart(int n) {
|
||||
queryProcessor.setStart(n);
|
||||
}
|
||||
|
||||
public void setIncludePatchSets(boolean on) {
|
||||
includePatchSets = on;
|
||||
}
|
||||
|
||||
public boolean getIncludePatchSets() {
|
||||
return includePatchSets;
|
||||
}
|
||||
|
||||
public void setIncludeCurrentPatchSet(boolean on) {
|
||||
includeCurrentPatchSet = on;
|
||||
}
|
||||
|
||||
public boolean getIncludeCurrentPatchSet() {
|
||||
return includeCurrentPatchSet;
|
||||
}
|
||||
|
||||
public void setIncludeApprovals(boolean on) {
|
||||
includeApprovals = on;
|
||||
}
|
||||
|
||||
public void setIncludeComments(boolean on) {
|
||||
includeComments = on;
|
||||
}
|
||||
|
||||
public void setIncludeFiles(boolean on) {
|
||||
includeFiles = on;
|
||||
}
|
||||
|
||||
public boolean getIncludeFiles() {
|
||||
return includeFiles;
|
||||
}
|
||||
|
||||
public void setIncludeDependencies(boolean on) {
|
||||
includeDependencies = on;
|
||||
}
|
||||
|
||||
public boolean getIncludeDependencies() {
|
||||
return includeDependencies;
|
||||
}
|
||||
|
||||
public void setIncludeCommitMessage(boolean on) {
|
||||
includeCommitMessage = on;
|
||||
}
|
||||
|
||||
public void setIncludeSubmitRecords(boolean on) {
|
||||
includeSubmitRecords = on;
|
||||
}
|
||||
|
||||
public void setIncludeAllReviewers(boolean on) {
|
||||
includeAllReviewers = on;
|
||||
}
|
||||
|
||||
public void setOutput(OutputStream out, OutputFormat fmt) {
|
||||
this.outputStream = out;
|
||||
this.outputFormat = fmt;
|
||||
}
|
||||
|
||||
public void query(String queryString) throws IOException {
|
||||
out = new PrintWriter( //
|
||||
new BufferedWriter( //
|
||||
new OutputStreamWriter(outputStream, "UTF-8")));
|
||||
try {
|
||||
if (queryProcessor.isDisabled()) {
|
||||
ErrorMessage m = new ErrorMessage();
|
||||
m.message = "query disabled";
|
||||
show(m);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final QueryStatsAttribute stats = new QueryStatsAttribute();
|
||||
stats.runTimeMilliseconds = TimeUtil.nowMs();
|
||||
|
||||
QueryResult results = queryProcessor.queryByString(queryString);
|
||||
ChangeAttribute c = null;
|
||||
for (ChangeData d : results.changes()) {
|
||||
ChangeControl cc = d.changeControl().forUser(user);
|
||||
|
||||
LabelTypes labelTypes = cc.getLabelTypes();
|
||||
c = eventFactory.asChangeAttribute(d.change());
|
||||
eventFactory.extend(c, d.change());
|
||||
|
||||
if (!trackingFooters.isEmpty()) {
|
||||
eventFactory.addTrackingIds(c,
|
||||
trackingFooters.extract(d.commitFooters()));
|
||||
}
|
||||
|
||||
if (includeAllReviewers) {
|
||||
eventFactory.addAllReviewers(c, d.notes());
|
||||
}
|
||||
|
||||
if (includeSubmitRecords) {
|
||||
eventFactory.addSubmitRecords(c, new SubmitRuleEvaluator(d)
|
||||
.setAllowClosed(true)
|
||||
.setAllowDraft(true)
|
||||
.canSubmit());
|
||||
}
|
||||
|
||||
if (includeCommitMessage) {
|
||||
eventFactory.addCommitMessage(c, d.commitMessage());
|
||||
}
|
||||
|
||||
if (includePatchSets) {
|
||||
if (includeFiles) {
|
||||
eventFactory.addPatchSets(c, d.patches(),
|
||||
includeApprovals ? d.approvals().asMap() : null,
|
||||
includeFiles, d.change(), labelTypes);
|
||||
} else {
|
||||
eventFactory.addPatchSets(c, d.patches(),
|
||||
includeApprovals ? d.approvals().asMap() : null,
|
||||
labelTypes);
|
||||
}
|
||||
}
|
||||
|
||||
if (includeCurrentPatchSet) {
|
||||
PatchSet current = d.currentPatchSet();
|
||||
if (current != null) {
|
||||
c.currentPatchSet = eventFactory.asPatchSetAttribute(current);
|
||||
eventFactory.addApprovals(c.currentPatchSet,
|
||||
d.currentApprovals(), labelTypes);
|
||||
|
||||
if (includeFiles) {
|
||||
eventFactory.addPatchSetFileNames(c.currentPatchSet,
|
||||
d.change(), d.currentPatchSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (includeComments) {
|
||||
eventFactory.addComments(c, d.messages());
|
||||
if (includePatchSets) {
|
||||
for (PatchSetAttribute attribute : c.patchSets) {
|
||||
eventFactory.addPatchSetComments(attribute, d.publishedComments());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (includeDependencies) {
|
||||
eventFactory.addDependencies(c, d.change());
|
||||
}
|
||||
|
||||
show(c);
|
||||
}
|
||||
|
||||
stats.rowCount = results.changes().size();
|
||||
if (results.moreChanges()) {
|
||||
stats.resumeSortKey = c.sortKey;
|
||||
}
|
||||
stats.runTimeMilliseconds =
|
||||
TimeUtil.nowMs() - stats.runTimeMilliseconds;
|
||||
show(stats);
|
||||
} catch (OrmException err) {
|
||||
log.error("Cannot execute query: " + queryString, err);
|
||||
|
||||
ErrorMessage m = new ErrorMessage();
|
||||
m.message = "cannot query database";
|
||||
show(m);
|
||||
|
||||
} catch (QueryParseException e) {
|
||||
ErrorMessage m = new ErrorMessage();
|
||||
m.message = e.getMessage();
|
||||
show(m);
|
||||
} catch (NoSuchChangeException e) {
|
||||
log.error("Missing change: " + e.getMessage(), e);
|
||||
ErrorMessage m = new ErrorMessage();
|
||||
m.message = "missing change " + e.getMessage();
|
||||
show(m);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
out.flush();
|
||||
} finally {
|
||||
out = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void show(Object data) {
|
||||
switch (outputFormat) {
|
||||
default:
|
||||
case TEXT:
|
||||
if (data instanceof ChangeAttribute) {
|
||||
out.print("change ");
|
||||
out.print(((ChangeAttribute) data).id);
|
||||
out.print("\n");
|
||||
showText(data, 1);
|
||||
} else {
|
||||
showText(data, 0);
|
||||
}
|
||||
out.print('\n');
|
||||
break;
|
||||
|
||||
case JSON:
|
||||
out.print(gson.toJson(data));
|
||||
out.print('\n');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void showText(Object data, int depth) {
|
||||
for (Field f : fieldsOf(data.getClass())) {
|
||||
Object val;
|
||||
try {
|
||||
val = f.get(data);
|
||||
} catch (IllegalArgumentException err) {
|
||||
continue;
|
||||
} catch (IllegalAccessException err) {
|
||||
continue;
|
||||
}
|
||||
if (val == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
showField(f.getName(), val, depth);
|
||||
}
|
||||
}
|
||||
|
||||
private String indent(int spaces) {
|
||||
if (spaces == 0) {
|
||||
return "";
|
||||
} else {
|
||||
return String.format("%" + spaces + "s", " ");
|
||||
}
|
||||
}
|
||||
|
||||
private void showField(String field, Object value, int depth) {
|
||||
final int spacesDepthRatio = 2;
|
||||
String indent = indent(depth * spacesDepthRatio);
|
||||
out.print(indent);
|
||||
out.print(field);
|
||||
out.print(':');
|
||||
if (value instanceof String && ((String) value).contains("\n")) {
|
||||
out.print(' ');
|
||||
// Idention for multi-line text is
|
||||
// current depth indetion + length of field + length of ": "
|
||||
indent = indent(indent.length() + field.length() + spacesDepthRatio);
|
||||
out.print(((String) value).replaceAll("\n", "\n" + indent).trim());
|
||||
out.print('\n');
|
||||
} else if (value instanceof Long && isDateField(field)) {
|
||||
out.print(' ');
|
||||
out.print(sdf.format(new Date(((Long) value) * 1000L)));
|
||||
out.print('\n');
|
||||
} else if (isPrimitive(value)) {
|
||||
out.print(' ');
|
||||
out.print(value);
|
||||
out.print('\n');
|
||||
} else if (value instanceof Collection) {
|
||||
out.print('\n');
|
||||
boolean firstElement = true;
|
||||
for (Object thing : ((Collection<?>) value)) {
|
||||
// The name of the collection was initially printed at the beginning
|
||||
// of this routine. Beginning at the second sub-element, reprint
|
||||
// the collection name so humans can separate individual elements
|
||||
// with less strain and error.
|
||||
//
|
||||
if (firstElement) {
|
||||
firstElement = false;
|
||||
} else {
|
||||
out.print(indent);
|
||||
out.print(field);
|
||||
out.print(":\n");
|
||||
}
|
||||
if (isPrimitive(thing)) {
|
||||
out.print(' ');
|
||||
out.print(value);
|
||||
out.print('\n');
|
||||
} else {
|
||||
showText(thing, depth + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.print('\n');
|
||||
showText(value, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPrimitive(Object value) {
|
||||
return value instanceof String //
|
||||
|| value instanceof Number //
|
||||
|| value instanceof Boolean //
|
||||
|| value instanceof Enum;
|
||||
}
|
||||
|
||||
private static boolean isDateField(String name) {
|
||||
return "lastUpdated".equals(name) //
|
||||
|| "grantedOn".equals(name) //
|
||||
|| "timestamp".equals(name) //
|
||||
|| "createdOn".equals(name);
|
||||
}
|
||||
|
||||
private List<Field> fieldsOf(Class<?> type) {
|
||||
List<Field> r = new ArrayList<>();
|
||||
if (type.getSuperclass() != null) {
|
||||
r.addAll(fieldsOf(type.getSuperclass()));
|
||||
}
|
||||
r.addAll(Arrays.asList(type.getDeclaredFields()));
|
||||
return r;
|
||||
}
|
||||
|
||||
static class ErrorMessage {
|
||||
public final String type = "error";
|
||||
public String message;
|
||||
}
|
||||
}
|
@@ -16,88 +16,31 @@ package com.google.gerrit.server.query.change;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.common.data.GlobalCapability;
|
||||
import com.google.gerrit.common.data.LabelTypes;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.config.TrackingFooters;
|
||||
import com.google.gerrit.server.data.ChangeAttribute;
|
||||
import com.google.gerrit.server.data.PatchSetAttribute;
|
||||
import com.google.gerrit.server.data.QueryStatsAttribute;
|
||||
import com.google.gerrit.server.events.EventFactory;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.project.SubmitRuleEvaluator;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.eclipse.jgit.util.io.DisabledOutputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class QueryProcessor {
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(QueryProcessor.class);
|
||||
|
||||
public static enum OutputFormat {
|
||||
TEXT, JSON
|
||||
}
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
private final SimpleDateFormat sdf =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz");
|
||||
|
||||
private final EventFactory eventFactory;
|
||||
private final ChangeQueryBuilder queryBuilder;
|
||||
private final ChangeQueryRewriter queryRewriter;
|
||||
private final TrackingFooters trackingFooters;
|
||||
private final CurrentUser user;
|
||||
private final int permittedLimit;
|
||||
|
||||
private OutputFormat outputFormat = OutputFormat.TEXT;
|
||||
private int limitFromCaller;
|
||||
private int start;
|
||||
private boolean includePatchSets;
|
||||
private boolean includeCurrentPatchSet;
|
||||
private boolean includeApprovals;
|
||||
private boolean includeComments;
|
||||
private boolean includeFiles;
|
||||
private boolean includeCommitMessage;
|
||||
private boolean includeDependencies;
|
||||
private boolean includeSubmitRecords;
|
||||
private boolean includeAllReviewers;
|
||||
|
||||
private OutputStream outputStream = DisabledOutputStream.INSTANCE;
|
||||
private PrintWriter out;
|
||||
|
||||
@Inject
|
||||
QueryProcessor(EventFactory eventFactory,
|
||||
ChangeQueryBuilder.Factory queryBuilder, CurrentUser currentUser,
|
||||
ChangeQueryRewriter queryRewriter,
|
||||
TrackingFooters trackingFooters) {
|
||||
this.eventFactory = eventFactory;
|
||||
QueryProcessor(ChangeQueryBuilder.Factory queryBuilder,
|
||||
CurrentUser currentUser,
|
||||
ChangeQueryRewriter queryRewriter) {
|
||||
this.queryBuilder = queryBuilder.create(currentUser);
|
||||
this.queryRewriter = queryRewriter;
|
||||
this.trackingFooters = trackingFooters;
|
||||
this.user = currentUser;
|
||||
this.permittedLimit = currentUser.getCapabilities()
|
||||
.getRange(GlobalCapability.QUERY_LIMIT)
|
||||
.getMax();
|
||||
@@ -111,63 +54,6 @@ public class QueryProcessor {
|
||||
start = n;
|
||||
}
|
||||
|
||||
public void setIncludePatchSets(boolean on) {
|
||||
includePatchSets = on;
|
||||
}
|
||||
|
||||
public boolean getIncludePatchSets() {
|
||||
return includePatchSets;
|
||||
}
|
||||
|
||||
public void setIncludeCurrentPatchSet(boolean on) {
|
||||
includeCurrentPatchSet = on;
|
||||
}
|
||||
|
||||
public boolean getIncludeCurrentPatchSet() {
|
||||
return includeCurrentPatchSet;
|
||||
}
|
||||
|
||||
public void setIncludeApprovals(boolean on) {
|
||||
includeApprovals = on;
|
||||
}
|
||||
|
||||
public void setIncludeComments(boolean on) {
|
||||
includeComments = on;
|
||||
}
|
||||
|
||||
public void setIncludeFiles(boolean on) {
|
||||
includeFiles = on;
|
||||
}
|
||||
|
||||
public boolean getIncludeFiles() {
|
||||
return includeFiles;
|
||||
}
|
||||
|
||||
public void setIncludeDependencies(boolean on) {
|
||||
includeDependencies = on;
|
||||
}
|
||||
|
||||
public boolean getIncludeDependencies() {
|
||||
return includeDependencies;
|
||||
}
|
||||
|
||||
public void setIncludeCommitMessage(boolean on) {
|
||||
includeCommitMessage = on;
|
||||
}
|
||||
|
||||
public void setIncludeSubmitRecords(boolean on) {
|
||||
includeSubmitRecords = on;
|
||||
}
|
||||
|
||||
public void setIncludeAllReviewers(boolean on) {
|
||||
includeAllReviewers = on;
|
||||
}
|
||||
|
||||
public void setOutput(OutputStream out, OutputFormat fmt) {
|
||||
this.outputStream = out;
|
||||
this.outputFormat = fmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for changes that match the query string.
|
||||
*
|
||||
@@ -275,126 +161,6 @@ public class QueryProcessor {
|
||||
return out;
|
||||
}
|
||||
|
||||
public void query(String queryString) throws IOException {
|
||||
out = new PrintWriter( //
|
||||
new BufferedWriter( //
|
||||
new OutputStreamWriter(outputStream, "UTF-8")));
|
||||
try {
|
||||
if (isDisabled()) {
|
||||
ErrorMessage m = new ErrorMessage();
|
||||
m.message = "query disabled";
|
||||
show(m);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final QueryStatsAttribute stats = new QueryStatsAttribute();
|
||||
stats.runTimeMilliseconds = TimeUtil.nowMs();
|
||||
|
||||
QueryResult results = queryByString(queryString);
|
||||
ChangeAttribute c = null;
|
||||
for (ChangeData d : results.changes()) {
|
||||
ChangeControl cc = d.changeControl().forUser(user);
|
||||
|
||||
LabelTypes labelTypes = cc.getLabelTypes();
|
||||
c = eventFactory.asChangeAttribute(d.change());
|
||||
eventFactory.extend(c, d.change());
|
||||
|
||||
if (!trackingFooters.isEmpty()) {
|
||||
eventFactory.addTrackingIds(c,
|
||||
trackingFooters.extract(d.commitFooters()));
|
||||
}
|
||||
|
||||
if (includeAllReviewers) {
|
||||
eventFactory.addAllReviewers(c, d.notes());
|
||||
}
|
||||
|
||||
if (includeSubmitRecords) {
|
||||
eventFactory.addSubmitRecords(c, new SubmitRuleEvaluator(d)
|
||||
.setAllowClosed(true)
|
||||
.setAllowDraft(true)
|
||||
.canSubmit());
|
||||
}
|
||||
|
||||
if (includeCommitMessage) {
|
||||
eventFactory.addCommitMessage(c, d.commitMessage());
|
||||
}
|
||||
|
||||
if (includePatchSets) {
|
||||
if (includeFiles) {
|
||||
eventFactory.addPatchSets(c, d.patches(),
|
||||
includeApprovals ? d.approvals().asMap() : null,
|
||||
includeFiles, d.change(), labelTypes);
|
||||
} else {
|
||||
eventFactory.addPatchSets(c, d.patches(),
|
||||
includeApprovals ? d.approvals().asMap() : null,
|
||||
labelTypes);
|
||||
}
|
||||
}
|
||||
|
||||
if (includeCurrentPatchSet) {
|
||||
PatchSet current = d.currentPatchSet();
|
||||
if (current != null) {
|
||||
c.currentPatchSet = eventFactory.asPatchSetAttribute(current);
|
||||
eventFactory.addApprovals(c.currentPatchSet,
|
||||
d.currentApprovals(), labelTypes);
|
||||
|
||||
if (includeFiles) {
|
||||
eventFactory.addPatchSetFileNames(c.currentPatchSet,
|
||||
d.change(), d.currentPatchSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (includeComments) {
|
||||
eventFactory.addComments(c, d.messages());
|
||||
if (includePatchSets) {
|
||||
for (PatchSetAttribute attribute : c.patchSets) {
|
||||
eventFactory.addPatchSetComments(attribute, d.publishedComments());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (includeDependencies) {
|
||||
eventFactory.addDependencies(c, d.change());
|
||||
}
|
||||
|
||||
show(c);
|
||||
}
|
||||
|
||||
stats.rowCount = results.changes().size();
|
||||
if (results.moreChanges()) {
|
||||
stats.resumeSortKey = c.sortKey;
|
||||
}
|
||||
stats.runTimeMilliseconds =
|
||||
TimeUtil.nowMs() - stats.runTimeMilliseconds;
|
||||
show(stats);
|
||||
} catch (OrmException err) {
|
||||
log.error("Cannot execute query: " + queryString, err);
|
||||
|
||||
ErrorMessage m = new ErrorMessage();
|
||||
m.message = "cannot query database";
|
||||
show(m);
|
||||
|
||||
} catch (QueryParseException e) {
|
||||
ErrorMessage m = new ErrorMessage();
|
||||
m.message = e.getMessage();
|
||||
show(m);
|
||||
} catch (NoSuchChangeException e) {
|
||||
log.error("Missing change: " + e.getMessage(), e);
|
||||
ErrorMessage m = new ErrorMessage();
|
||||
m.message = "missing change " + e.getMessage();
|
||||
show(m);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
out.flush();
|
||||
} finally {
|
||||
out = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isDisabled() {
|
||||
return permittedLimit <= 0;
|
||||
}
|
||||
@@ -411,131 +177,4 @@ public class QueryProcessor {
|
||||
}
|
||||
return Ordering.natural().min(possibleLimits);
|
||||
}
|
||||
|
||||
private void show(Object data) {
|
||||
switch (outputFormat) {
|
||||
default:
|
||||
case TEXT:
|
||||
if (data instanceof ChangeAttribute) {
|
||||
out.print("change ");
|
||||
out.print(((ChangeAttribute) data).id);
|
||||
out.print("\n");
|
||||
showText(data, 1);
|
||||
} else {
|
||||
showText(data, 0);
|
||||
}
|
||||
out.print('\n');
|
||||
break;
|
||||
|
||||
case JSON:
|
||||
out.print(gson.toJson(data));
|
||||
out.print('\n');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void showText(Object data, int depth) {
|
||||
for (Field f : fieldsOf(data.getClass())) {
|
||||
Object val;
|
||||
try {
|
||||
val = f.get(data);
|
||||
} catch (IllegalArgumentException err) {
|
||||
continue;
|
||||
} catch (IllegalAccessException err) {
|
||||
continue;
|
||||
}
|
||||
if (val == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
showField(f.getName(), val, depth);
|
||||
}
|
||||
}
|
||||
|
||||
private String indent(int spaces) {
|
||||
if (spaces == 0) {
|
||||
return "";
|
||||
} else {
|
||||
return String.format("%" + spaces + "s", " ");
|
||||
}
|
||||
}
|
||||
|
||||
private void showField(String field, Object value, int depth) {
|
||||
final int spacesDepthRatio = 2;
|
||||
String indent = indent(depth * spacesDepthRatio);
|
||||
out.print(indent);
|
||||
out.print(field);
|
||||
out.print(':');
|
||||
if (value instanceof String && ((String) value).contains("\n")) {
|
||||
out.print(' ');
|
||||
// Idention for multi-line text is
|
||||
// current depth indetion + length of field + length of ": "
|
||||
indent = indent(indent.length() + field.length() + spacesDepthRatio);
|
||||
out.print(((String) value).replaceAll("\n", "\n" + indent).trim());
|
||||
out.print('\n');
|
||||
} else if (value instanceof Long && isDateField(field)) {
|
||||
out.print(' ');
|
||||
out.print(sdf.format(new Date(((Long) value) * 1000L)));
|
||||
out.print('\n');
|
||||
} else if (isPrimitive(value)) {
|
||||
out.print(' ');
|
||||
out.print(value);
|
||||
out.print('\n');
|
||||
} else if (value instanceof Collection) {
|
||||
out.print('\n');
|
||||
boolean firstElement = true;
|
||||
for (Object thing : ((Collection<?>) value)) {
|
||||
// The name of the collection was initially printed at the beginning
|
||||
// of this routine. Beginning at the second sub-element, reprint
|
||||
// the collection name so humans can separate individual elements
|
||||
// with less strain and error.
|
||||
//
|
||||
if (firstElement) {
|
||||
firstElement = false;
|
||||
} else {
|
||||
out.print(indent);
|
||||
out.print(field);
|
||||
out.print(":\n");
|
||||
}
|
||||
if (isPrimitive(thing)) {
|
||||
out.print(' ');
|
||||
out.print(value);
|
||||
out.print('\n');
|
||||
} else {
|
||||
showText(thing, depth + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.print('\n');
|
||||
showText(value, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPrimitive(Object value) {
|
||||
return value instanceof String //
|
||||
|| value instanceof Number //
|
||||
|| value instanceof Boolean //
|
||||
|| value instanceof Enum;
|
||||
}
|
||||
|
||||
private static boolean isDateField(String name) {
|
||||
return "lastUpdated".equals(name) //
|
||||
|| "grantedOn".equals(name) //
|
||||
|| "timestamp".equals(name) //
|
||||
|| "createdOn".equals(name);
|
||||
}
|
||||
|
||||
private List<Field> fieldsOf(Class<?> type) {
|
||||
List<Field> r = new ArrayList<>();
|
||||
if (type.getSuperclass() != null) {
|
||||
r.addAll(fieldsOf(type.getSuperclass()));
|
||||
}
|
||||
r.addAll(Arrays.asList(type.getDeclaredFields()));
|
||||
return r;
|
||||
}
|
||||
|
||||
static class ErrorMessage {
|
||||
public final String type = "error";
|
||||
public String message;
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,8 @@
|
||||
|
||||
package com.google.gerrit.sshd.commands;
|
||||
|
||||
import com.google.gerrit.server.query.change.QueryProcessor;
|
||||
import com.google.gerrit.server.query.change.OutputStreamQuery;
|
||||
import com.google.gerrit.server.query.change.OutputStreamQuery.OutputFormat;
|
||||
import com.google.gerrit.sshd.CommandMetaData;
|
||||
import com.google.gerrit.sshd.SshCommand;
|
||||
import com.google.inject.Inject;
|
||||
@@ -27,10 +28,10 @@ import java.util.List;
|
||||
@CommandMetaData(name = "query", description = "Query the change database")
|
||||
class Query extends SshCommand {
|
||||
@Inject
|
||||
private QueryProcessor processor;
|
||||
private OutputStreamQuery processor;
|
||||
|
||||
@Option(name = "--format", metaVar = "FMT", usage = "Output display format")
|
||||
void setFormat(QueryProcessor.OutputFormat format) {
|
||||
void setFormat(OutputFormat format) {
|
||||
processor.setOutput(out, format);
|
||||
}
|
||||
|
||||
@@ -97,7 +98,7 @@ class Query extends SshCommand {
|
||||
|
||||
@Override
|
||||
protected void parseCommandLine() throws UnloggedFailure {
|
||||
processor.setOutput(out, QueryProcessor.OutputFormat.TEXT);
|
||||
processor.setOutput(out, OutputFormat.TEXT);
|
||||
super.parseCommandLine();
|
||||
if (processor.getIncludeFiles() &&
|
||||
!(processor.getIncludePatchSets() || processor.getIncludeCurrentPatchSet())) {
|
||||
|
Reference in New Issue
Block a user