Fix DynamicOptions to support a custom bean

In the lifecycle listeners change (I84670dcab), a bug has been
introduced where the DynamicOptions no longer supports processing
custom bean. This change addresses that issue. Added a test case which
illustrates the use of the custom bean. Also, try-with-resources block
is used to instantiate DynamicOptions at all the places such that
the resources are closed as expected.

Change-Id: I7e9c518461408d272d0d0673b56a93b77cc6d2a8
This commit is contained in:
Prudhvi Akhil Alahari
2020-11-24 19:25:24 +05:30
parent 30ba6a1c0a
commit 149c1bcfab
12 changed files with 239 additions and 49 deletions

View File

@@ -177,6 +177,8 @@ class ChangeApiImpl implements ChangeApi {
private final Provider<GetPureRevert> getPureRevertProvider;
private final StarredChangesUtil stars;
private final DynamicOptionParser dynamicOptionParser;
private final Injector injector;
private final DynamicMap<DynamicOptions.DynamicBean> dynamicBeans;
@Inject
ChangeApiImpl(
@@ -230,7 +232,9 @@ class ChangeApiImpl implements ChangeApi {
Provider<GetPureRevert> getPureRevertProvider,
StarredChangesUtil stars,
DynamicOptionParser dynamicOptionParser,
@Assisted ChangeResource change) {
@Assisted ChangeResource change,
Injector injector,
DynamicMap<DynamicOptions.DynamicBean> dynamicBeans) {
this.changeApi = changeApi;
this.revert = revert;
this.revertSubmission = revertSubmission;
@@ -282,6 +286,8 @@ class ChangeApiImpl implements ChangeApi {
this.stars = stars;
this.dynamicOptionParser = dynamicOptionParser;
this.change = change;
this.injector = injector;
this.dynamicBeans = dynamicBeans;
}
@Override
@@ -500,10 +506,10 @@ class ChangeApiImpl implements ChangeApi {
public ChangeInfo get(
EnumSet<ListChangesOption> options, ImmutableListMultimap<String, String> pluginOptions)
throws RestApiException {
try {
try (DynamicOptions dynamicOptions = new DynamicOptions(injector, dynamicBeans)) {
GetChange getChange = getChangeProvider.get();
options.forEach(getChange::addOption);
dynamicOptionParser.parseDynamicOptions(getChange, pluginOptions);
dynamicOptionParser.parseDynamicOptions(getChange, pluginOptions, dynamicOptions);
return getChange.apply(change).value();
} catch (Exception e) {
throw asRestApiException("Cannot retrieve change", e);
@@ -759,8 +765,6 @@ class ChangeApiImpl implements ChangeApi {
@Singleton
static class DynamicOptionParser {
private final CmdLineParser.Factory cmdLineParserFactory;
private final Injector injector;
private final DynamicMap<DynamicOptions.DynamicBean> dynamicBeans;
@Inject
DynamicOptionParser(
@@ -768,14 +772,14 @@ class ChangeApiImpl implements ChangeApi {
Injector injector,
DynamicMap<DynamicOptions.DynamicBean> dynamicBeans) {
this.cmdLineParserFactory = cmdLineParserFactory;
this.injector = injector;
this.dynamicBeans = dynamicBeans;
}
void parseDynamicOptions(Object bean, ListMultimap<String, String> pluginOptions)
void parseDynamicOptions(
Object bean, ListMultimap<String, String> pluginOptions, DynamicOptions dynamicOptions)
throws BadRequestException {
CmdLineParser clp = cmdLineParserFactory.create(bean);
DynamicOptions dynamicOptions = new DynamicOptions(bean, injector, dynamicBeans);
dynamicOptions.setBean(bean);
dynamicOptions.startLifecycleListeners();
dynamicOptions.parseDynamicBeans(clp);
dynamicOptions.setDynamicBeans();
dynamicOptions.onBeanParseStart();

View File

@@ -26,15 +26,18 @@ import com.google.gerrit.extensions.api.changes.Changes;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.server.DynamicOptions;
import com.google.gerrit.server.api.changes.ChangeApiImpl.DynamicOptionParser;
import com.google.gerrit.server.restapi.change.ChangesCollection;
import com.google.gerrit.server.restapi.change.CreateChange;
import com.google.gerrit.server.restapi.change.QueryChanges;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.List;
@@ -46,6 +49,8 @@ class ChangesImpl implements Changes {
private final CreateChange createChange;
private final DynamicOptionParser dynamicOptionParser;
private final Provider<QueryChanges> queryProvider;
private final Injector injector;
private final DynamicMap<DynamicOptions.DynamicBean> dynamicBeans;
@Inject
ChangesImpl(
@@ -53,12 +58,16 @@ class ChangesImpl implements Changes {
ChangeApiImpl.Factory api,
CreateChange createChange,
DynamicOptionParser dynamicOptionParser,
Provider<QueryChanges> queryProvider) {
Provider<QueryChanges> queryProvider,
Injector injector,
DynamicMap<DynamicOptions.DynamicBean> dynamicBeans) {
this.changes = changes;
this.api = api;
this.createChange = createChange;
this.dynamicOptionParser = dynamicOptionParser;
this.queryProvider = queryProvider;
this.injector = injector;
this.dynamicBeans = dynamicBeans;
}
@Override
@@ -123,34 +132,36 @@ class ChangesImpl implements Changes {
}
private List<ChangeInfo> get(QueryRequest q) throws RestApiException {
QueryChanges qc = queryProvider.get();
if (q.getQuery() != null) {
qc.addQuery(q.getQuery());
}
qc.setLimit(q.getLimit());
qc.setStart(q.getStart());
qc.setNoLimit(q.getNoLimit());
for (ListChangesOption option : q.getOptions()) {
qc.addOption(option);
}
dynamicOptionParser.parseDynamicOptions(qc, q.getPluginOptions());
try {
List<?> result = qc.apply(TopLevelResource.INSTANCE).value();
if (result.isEmpty()) {
return ImmutableList.of();
try (DynamicOptions dynamicOptions = new DynamicOptions(injector, dynamicBeans)) {
QueryChanges qc = queryProvider.get();
if (q.getQuery() != null) {
qc.addQuery(q.getQuery());
}
qc.setLimit(q.getLimit());
qc.setStart(q.getStart());
qc.setNoLimit(q.getNoLimit());
for (ListChangesOption option : q.getOptions()) {
qc.addOption(option);
}
dynamicOptionParser.parseDynamicOptions(qc, q.getPluginOptions(), dynamicOptions);
// Check type safety of result; the extension API should be safer than the
// REST API in this case, since it's intended to be used in Java.
Object first = requireNonNull(result.iterator().next());
checkState(first instanceof ChangeInfo);
@SuppressWarnings("unchecked")
List<ChangeInfo> infos = (List<ChangeInfo>) result;
try {
List<?> result = qc.apply(TopLevelResource.INSTANCE).value();
if (result.isEmpty()) {
return ImmutableList.of();
}
return ImmutableList.copyOf(infos);
} catch (Exception e) {
throw asRestApiException("Cannot query changes", e);
// Check type safety of result; the extension API should be safer than the
// REST API in this case, since it's intended to be used in Java.
Object first = requireNonNull(result.iterator().next());
checkState(first instanceof ChangeInfo);
@SuppressWarnings("unchecked")
List<ChangeInfo> infos = (List<ChangeInfo>) result;
return ImmutableList.copyOf(infos);
} catch (Exception e) {
throw asRestApiException("Cannot query changes", e);
}
}
}
}