Allow plugins to output change query attributes
Create a ChangeAttributeFactory interface to allow plugins to register to provide additonal attributes to be output in a change query result. Example Usage: https://gerrit-review.googlesource.com/#/c/102650/ Change-Id: I4a09d9abd8bda09a3ecde7ca203434d6ab8ab7be
This commit is contained in:

committed by
James Melvin

parent
78a3037859
commit
cffb2459dc
@@ -736,6 +736,68 @@ public class SshModule extends AbstractModule {
|
||||
}
|
||||
----
|
||||
|
||||
[[query_attributes]]
|
||||
=== Query Attributes ===
|
||||
|
||||
Plugins can provide additional attributes to be returned in Gerrit queries by
|
||||
implementing the ChangeAttributeFactory interface and registering it to the
|
||||
ChangeQueryProcessor.ChangeAttributeFactory class in the plugin module's
|
||||
'configure()' method. The new attribute(s) will be output under a "plugin"
|
||||
attribute in the change query output.
|
||||
|
||||
The example below shows a plugin that adds two attributes ('exampleName' and
|
||||
'changeValue'), to the change query output.
|
||||
|
||||
[source, java]
|
||||
----
|
||||
public class Module extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ChangeAttributeFactory.class)
|
||||
.annotatedWith(Exports.named("example"))
|
||||
.to(AttributeFactory.class);
|
||||
}
|
||||
}
|
||||
|
||||
public class AttributeFactory implements ChangeAttributeFactory {
|
||||
|
||||
public class PluginAttribute extends PluginDefinedInfo {
|
||||
public String exampleName;
|
||||
public String changeValue;
|
||||
|
||||
public PluginAttribute(ChangeData c) {
|
||||
this.exampleName = "Attribute Example";
|
||||
this.changeValue = Integer.toString(c.getId().get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginDefinedInfo create(ChangeData c, ChangeQueryProcessor qp, String plugin) {
|
||||
return new PluginAttribute(c);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Example
|
||||
----
|
||||
|
||||
ssh -p 29418 localhost gerrit query "change:1" --format json
|
||||
|
||||
Output:
|
||||
|
||||
{
|
||||
"url" : "http://localhost:8080/1",
|
||||
"plugins" : [
|
||||
{
|
||||
"name" : "myplugin-name",
|
||||
"exampleName" : "Attribute Example",
|
||||
"changeValue" : "1"
|
||||
}
|
||||
],
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
[[simple-configuration]]
|
||||
== Simple Configuration in `gerrit.config`
|
||||
|
||||
|
@@ -63,4 +63,5 @@ public class ChangeInfo {
|
||||
public Boolean _moreChanges;
|
||||
|
||||
public List<ProblemInfo> problems;
|
||||
public List<PluginDefinedInfo> plugins;
|
||||
}
|
||||
|
@@ -0,0 +1,19 @@
|
||||
// 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.common;
|
||||
|
||||
public class PluginDefinedInfo {
|
||||
public String name;
|
||||
}
|
@@ -68,6 +68,7 @@ import com.google.gerrit.server.project.ProjectCacheImpl;
|
||||
import com.google.gerrit.server.project.ProjectState;
|
||||
import com.google.gerrit.server.project.SectionSortCache;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
|
||||
import com.google.gerrit.server.update.BatchUpdate;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Module;
|
||||
@@ -112,6 +113,8 @@ public class BatchProgramModule extends FactoryModule {
|
||||
bind(new TypeLiteral<List<CommentLinkInfo>>() {})
|
||||
.toProvider(CommentLinkProvider.class)
|
||||
.in(SINGLETON);
|
||||
bind(new TypeLiteral<DynamicMap<ChangeQueryProcessor.ChangeAttributeFactory>>() {})
|
||||
.toInstance(DynamicMap.<ChangeQueryProcessor.ChangeAttributeFactory>emptyMap());
|
||||
bind(String.class)
|
||||
.annotatedWith(CanonicalWebUrl.class)
|
||||
.toProvider(CanonicalWebUrlProvider.class);
|
||||
|
@@ -118,6 +118,7 @@ import com.google.gerrit.server.project.SubmitRuleOptions;
|
||||
import com.google.gerrit.server.query.QueryResult;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
|
||||
import com.google.gerrit.server.query.change.PluginDefinedAttributesFactory;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -213,6 +214,7 @@ public class ChangeJson {
|
||||
private boolean lazyLoad = true;
|
||||
private AccountLoader accountLoader;
|
||||
private FixInput fix;
|
||||
private PluginDefinedAttributesFactory pluginDefinedAttributesFactory;
|
||||
|
||||
@Inject
|
||||
ChangeJson(
|
||||
@@ -276,6 +278,10 @@ public class ChangeJson {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setPluginDefinedAttributesFactory(PluginDefinedAttributesFactory pluginsFactory) {
|
||||
this.pluginDefinedAttributesFactory = pluginsFactory;
|
||||
}
|
||||
|
||||
public ChangeInfo format(ChangeResource rsrc) throws OrmException {
|
||||
return format(changeDataFactory.create(db.get(), rsrc.getControl()));
|
||||
}
|
||||
@@ -516,6 +522,8 @@ public class ChangeJson {
|
||||
|
||||
out.labels = labelsFor(ctl, cd, has(LABELS), has(DETAILED_LABELS));
|
||||
out.submitted = getSubmittedOn(cd);
|
||||
out.plugins =
|
||||
pluginDefinedAttributesFactory != null ? pluginDefinedAttributesFactory.create(cd) : null;
|
||||
|
||||
if (out.labels != null && has(DETAILED_LABELS)) {
|
||||
// If limited to specific patch sets but not the current patch set, don't
|
||||
|
@@ -166,6 +166,7 @@ import com.google.gerrit.server.project.ProjectState;
|
||||
import com.google.gerrit.server.project.SectionSortCache;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
|
||||
import com.google.gerrit.server.query.change.ConflictsCacheImpl;
|
||||
import com.google.gerrit.server.ssh.SshAddressesModule;
|
||||
import com.google.gerrit.server.tools.ToolsCatalog;
|
||||
@@ -378,6 +379,8 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
|
||||
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class);
|
||||
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class);
|
||||
DynamicMap.mapOf(binder(), ChangeQueryProcessor.ChangeAttributeFactory.class);
|
||||
|
||||
install(new GitwebConfig.LegacyModule(cfg));
|
||||
|
||||
bind(AnonymousUser.class);
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.server.data;
|
||||
|
||||
import com.google.gerrit.extensions.common.PluginDefinedInfo;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,4 +44,5 @@ public class ChangeAttribute {
|
||||
public List<DependencyAttribute> neededBy;
|
||||
public List<SubmitRecordAttribute> submitRecords;
|
||||
public List<AccountAttribute> allReviewers;
|
||||
public List<PluginDefinedInfo> plugins;
|
||||
}
|
||||
|
@@ -17,6 +17,8 @@ package com.google.gerrit.server.query.change;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_LIMIT;
|
||||
|
||||
import com.google.gerrit.extensions.common.PluginDefinedInfo;
|
||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.index.IndexConfig;
|
||||
@@ -32,12 +34,26 @@ import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryProcessor;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ChangeQueryProcessor extends QueryProcessor<ChangeData> {
|
||||
public class ChangeQueryProcessor extends QueryProcessor<ChangeData>
|
||||
implements PluginDefinedAttributesFactory {
|
||||
/**
|
||||
* Register a ChangeAttributeFactory in a config Module like this:
|
||||
*
|
||||
* <p>bind(ChangeAttributeFactory.class) .annotatedWith(Exports.named("export-name"))
|
||||
* .to(YourClass.class);
|
||||
*/
|
||||
public interface ChangeAttributeFactory {
|
||||
PluginDefinedInfo create(ChangeData a, ChangeQueryProcessor qp, String plugin);
|
||||
}
|
||||
|
||||
private final Provider<ReviewDb> db;
|
||||
private final ChangeControl.GenericFactory changeControlFactory;
|
||||
private final ChangeNotes.Factory notesFactory;
|
||||
private final DynamicMap<ChangeAttributeFactory> attributeFactories;
|
||||
|
||||
static {
|
||||
// It is assumed that basic rewrites do not touch visibleto predicates.
|
||||
@@ -55,7 +71,8 @@ public class ChangeQueryProcessor extends QueryProcessor<ChangeData> {
|
||||
ChangeIndexRewriter rewriter,
|
||||
Provider<ReviewDb> db,
|
||||
ChangeControl.GenericFactory changeControlFactory,
|
||||
ChangeNotes.Factory notesFactory) {
|
||||
ChangeNotes.Factory notesFactory,
|
||||
DynamicMap<ChangeAttributeFactory> attributeFactories) {
|
||||
super(
|
||||
userProvider,
|
||||
metrics,
|
||||
@@ -67,6 +84,7 @@ public class ChangeQueryProcessor extends QueryProcessor<ChangeData> {
|
||||
this.db = db;
|
||||
this.changeControlFactory = changeControlFactory;
|
||||
this.notesFactory = notesFactory;
|
||||
this.attributeFactories = attributeFactories;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,6 +99,30 @@ public class ChangeQueryProcessor extends QueryProcessor<ChangeData> {
|
||||
return IndexedChangeQuery.createOptions(indexConfig, start, limit, requestedFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PluginDefinedInfo> create(ChangeData cd) {
|
||||
List<PluginDefinedInfo> plugins = new ArrayList<>(attributeFactories.plugins().size());
|
||||
for (String plugin : attributeFactories.plugins()) {
|
||||
for (Provider<ChangeAttributeFactory> provider :
|
||||
attributeFactories.byPlugin(plugin).values()) {
|
||||
PluginDefinedInfo pda = null;
|
||||
try {
|
||||
pda = provider.get().create(cd, this, plugin);
|
||||
} catch (RuntimeException e) {
|
||||
/* Eat runtime exceptions so that queries don't fail. */
|
||||
}
|
||||
if (pda != null) {
|
||||
pda.name = plugin;
|
||||
plugins.add(pda);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (plugins.isEmpty()) {
|
||||
plugins = null;
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Predicate<ChangeData> enforceVisibility(Predicate<ChangeData> pred) {
|
||||
return new AndChangeSource(
|
||||
|
@@ -313,6 +313,7 @@ public class OutputStreamQuery {
|
||||
eventFactory.addDependencies(rw, c, d.change(), d.currentPatchSet());
|
||||
}
|
||||
|
||||
c.plugins = queryProcessor.create(d);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,22 @@
|
||||
// Copyright (C) 2016 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.extensions.common.PluginDefinedInfo;
|
||||
import java.util.List;
|
||||
|
||||
public interface PluginDefinedAttributesFactory {
|
||||
List<PluginDefinedInfo> create(ChangeData cd);
|
||||
}
|
@@ -137,13 +137,18 @@ public class QueryChanges implements RestReadView<TopLevelResource> {
|
||||
|
||||
int cnt = queries.size();
|
||||
List<QueryResult<ChangeData>> results = imp.query(qb.parse(queries));
|
||||
|
||||
boolean requireLazyLoad =
|
||||
containsAnyOf(options, ImmutableSet.of(DETAILED_LABELS, LABELS))
|
||||
&& !qb.getArgs().getSchema().hasField(ChangeField.STORED_SUBMIT_RECORD_LENIENT);
|
||||
|
||||
ChangeJson cjson = json.create(options);
|
||||
cjson.setPluginDefinedAttributesFactory(this.imp);
|
||||
List<List<ChangeInfo>> res =
|
||||
json.create(options)
|
||||
cjson
|
||||
.lazyLoad(requireLazyLoad || containsAnyOf(options, ChangeJson.REQUIRE_LAZY_LOAD))
|
||||
.formatQueryResults(results);
|
||||
|
||||
for (int n = 0; n < cnt; n++) {
|
||||
List<ChangeInfo> info = res.get(n);
|
||||
if (results.get(n).more()) {
|
||||
|
@@ -24,7 +24,7 @@ import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
@CommandMetaData(name = "query", description = "Query the change database")
|
||||
class Query extends SshCommand {
|
||||
public class Query extends SshCommand {
|
||||
@Inject private OutputStreamQuery processor;
|
||||
|
||||
@Option(name = "--format", metaVar = "FMT", usage = "Output display format")
|
||||
|
Reference in New Issue
Block a user