Allow plugins to define 'has' search operands.

Create a ChangeHasOperandFactory interface which extends a more
generic ChangeOperandFactory in the ChangeQueryBuilder class along
with support methods and enhancements to support defining has
operands that will be called from the 'has' operator in the
ChangeQueryBuilder class.

Change-Id: I9014b0094a9d79c2cbcafb0dcd375fbb93a46748
This commit is contained in:
Craig Chapel
2016-11-14 09:25:17 -07:00
committed by Martin Fick
parent 01d42968a5
commit dba4e892b1
3 changed files with 82 additions and 10 deletions

View File

@@ -684,6 +684,43 @@ public class SampleOperator
}
----
[[search_operands]]
=== Search Operands ===
Plugins can define new search operands to extend change searching.
Plugin methods implementing search operands (returning a
`Predicate<ChangeData>`), must be defined on a class implementing
one of the `ChangeQueryBuilder.ChangeOperandsFactory` interfaces
(.e.g., ChangeQueryBuilder.ChangeHasOperandFactory). The specific
`ChangeOperandFactory` class must also be bound to the `DynamicSet` from
a module's `configure()` method in the plugin.
The new operand, when used in a search would appear as:
operatorName:operandName_pluginName
A sample `ChangeHasOperandFactory` class implementing, and registering, a
new `has:sample_pluginName` operand is shown below:
====
@Singleton
public class SampleHasOperand implements ChangeHasOperandFactory {
public static class Module extends AbstractModule {
@Override
protected void configure() {
bind(ChangeHasOperandFactory.class)
.annotatedWith(Exports.named("sample")
.to(SampleHasOperand.class);
}
}
@Override
public Predicate<ChangeData> create(ChangeQueryBuilder builder)
throws QueryParseException {
return new HasSamplePredicate();
}
====
[[simple-configuration]]
== Simple Configuration in `gerrit.config`

View File

@@ -376,6 +376,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), UploadValidationListener.class);
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeOperatorFactory.class);
DynamicMap.mapOf(binder(), ChangeQueryBuilder.ChangeHasOperandFactory.class);
install(new GitwebConfig.LegacyModule(cfg));
bind(AnonymousUser.class);

View File

@@ -28,6 +28,7 @@ import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.errors.NotSignedInException;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch;
@@ -98,6 +99,27 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
extends OperatorFactory<ChangeData, ChangeQueryBuilder> {
}
/**
* Converts a operand (operator value) passed to an operator into a
* {@link Predicate}.
*
* Register a ChangeOperandFactory in a config Module like this (note, for
* an example we are using the has predicate, when other predicate plugin
* operands are created they can be registered in a similar manner):
*
* bind(ChangeHasOperandFactory.class)
* .annotatedWith(Exports.named("your has operand"))
* .to(YourClass.class);
*
*/
private interface ChangeOperandFactory {
Predicate<ChangeData> create(ChangeQueryBuilder builder)
throws QueryParseException;
}
public interface ChangeHasOperandFactory extends ChangeOperandFactory {
}
private static final Pattern PAT_LEGACY_ID = Pattern.compile("^[1-9][0-9]*$");
private static final Pattern PAT_CHANGE_ID = Pattern.compile(CHANGE_ID_PATTERN);
private static final Pattern DEF_CHANGE = Pattern.compile(
@@ -166,6 +188,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
final Provider<InternalChangeQuery> queryProvider;
final ChangeIndexRewriter rewriter;
final DynamicMap<ChangeOperatorFactory> opFactories;
final DynamicMap<ChangeHasOperandFactory> hasOperands;
final IdentifiedUser.GenericFactory userFactory;
final CapabilityControl.Factory capabilityControlFactory;
final ChangeControl.GenericFactory changeControlGenericFactory;
@@ -199,6 +222,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
Provider<InternalChangeQuery> queryProvider,
ChangeIndexRewriter rewriter,
DynamicMap<ChangeOperatorFactory> opFactories,
DynamicMap<ChangeHasOperandFactory> hasOperands,
IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self,
CapabilityControl.Factory capabilityControlFactory,
@@ -224,11 +248,9 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
StarredChangesUtil starredChangesUtil,
AccountCache accountCache,
@GerritServerConfig Config cfg) {
this(db, queryProvider, rewriter, opFactories, userFactory, self,
capabilityControlFactory, changeControlGenericFactory, notesFactory,
changeDataFactory, fillArgs, commentsUtil, accountResolver, groupBackend,
allProjectsName, allUsersName, patchListCache, repoManager,
projectCache, listChildProjects, submitDryRun, conflictsCache,
this(db, queryProvider, rewriter, opFactories, hasOperands, userFactory,
self, capabilityControlFactory, changeControlGenericFactory, notesFactory, changeDataFactory, fillArgs, commentsUtil,
accountResolver, groupBackend, allProjectsName, allUsersName, patchListCache, repoManager, projectCache, listChildProjects, submitDryRun, conflictsCache,
trackingFooters, indexes != null ? indexes.getSearchIndex() : null,
indexConfig, listMembers, starredChangesUtil, accountCache,
cfg == null ? true : cfg.getBoolean("change", "allowDrafts", true));
@@ -239,6 +261,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
Provider<InternalChangeQuery> queryProvider,
ChangeIndexRewriter rewriter,
DynamicMap<ChangeOperatorFactory> opFactories,
DynamicMap<ChangeHasOperandFactory> hasOperands,
IdentifiedUser.GenericFactory userFactory,
Provider<CurrentUser> self,
CapabilityControl.Factory capabilityControlFactory,
@@ -293,15 +316,16 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
this.starredChangesUtil = starredChangesUtil;
this.accountCache = accountCache;
this.allowsDrafts = allowsDrafts;
this.hasOperands = hasOperands;
}
Arguments asUser(CurrentUser otherUser) {
return new Arguments(db, queryProvider, rewriter, opFactories, userFactory,
Providers.of(otherUser),
return new Arguments(db, queryProvider, rewriter, opFactories,
hasOperands, userFactory, Providers.of(otherUser),
capabilityControlFactory, changeControlGenericFactory, notesFactory,
changeDataFactory, fillArgs, commentsUtil, accountResolver, groupBackend,
allProjectsName, allUsersName, patchListCache, repoManager,
projectCache, listChildProjects, submitDryRun,
changeDataFactory, fillArgs, commentsUtil, accountResolver,
groupBackend, allProjectsName, allUsersName, patchListCache,
repoManager, projectCache, listChildProjects, submitDryRun,
conflictsCache, trackingFooters, index, indexConfig, listMembers,
starredChangesUtil, accountCache, allowsDrafts);
}
@@ -453,6 +477,16 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
if ("edit".equalsIgnoreCase(value)) {
return new EditByPredicate(self());
}
// for plugins the value will be operandName_pluginName
String[] names = value.split("_");
if (names.length == 2) {
ChangeHasOperandFactory op = args.hasOperands.get(names[1], names[0]);
if (op != null) {
return op.create(this);
}
}
throw new IllegalArgumentException();
}