diff options
author | Prudhvi Akhil Alahari <prudhvi@codeaurora.org> | 2021-08-13 21:35:38 +0530 |
---|---|---|
committer | Prudhvi Akhil Alahari <prudhvi@codeaurora.org> | 2021-09-14 10:28:52 +0530 |
commit | ac4c305046ca3dc7095f021c6307b4eefae85eff (patch) | |
tree | 6df4a4bb9b08add0f4d569c91536a32745d34e4f | |
parent | 6cb377f998cf6c3543d25b3250dfca8d0ee18532 (diff) |
Fix DynamicOptions to invoke listeners registered to BeanParseListener
The change which added support for plugin fields in GetChange(I9254d46b)
was unintentionally assigning the DynamicOptions class object as
beanClass. Due to this, if there were any bean parse listeners exported
with bean's canonical class name as export name, they were not invoked.
Fix this by assigning bean's canonical class name as beanClass in
DynamicOptions.
Also add documentation and integration tests for this scenario.
Change-Id: Ibaafb98be0f940d835f042a74f914741efa259fb
4 files changed, 194 insertions, 1 deletions
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index 8ad55358c7..21903261dc 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt @@ -796,6 +796,63 @@ public class SshModule extends AbstractModule { } ---- +Plugins can receive a bean object for each of the gerrit ssh and the REST API +commands by implementing BeanParseListener interface and registering it to a +command class name in the plugin module's `configure()` method. The below +example shows a plugin that always limits the number of projects returned +by the ls-projects SSH command. + +[source, java] +---- +protected static class PluginModule extends AbstractModule { + @Override + public void configure() { + bind(DynamicOptions.DynamicBean.class) + .annotatedWith(Exports.named(ListProjectsCommand.class)) + .to(ListProjectsCommandBeanListener.class); + } + + protected static class ListProjectsCommandBeanListener + implements DynamicOptions.BeanParseListener { + @Override + public void onBeanParseStart(String plugin, Object bean) { + ListProjectsCommand command = (ListProjectsCommand) bean; + command.impl.setLimit(1); + } + + @Override + public void onBeanParseEnd(String plugin, Object bean) {} + } +} +---- + +The below example shows a plugin that always limits the number of projects +returned by the /projects/ REST API. + +[source, java] +---- +protected static class PluginModule extends AbstractModule { + @Override + public void configure() { + bind(DynamicOptions.DynamicBean.class) + .annotatedWith(Exports.named(ListProjects.class)) + .to(ListProjectsBeanListener.class); + } + + protected static class ListProjectsBeanListener + implements DynamicOptions.BeanParseListener { + @Override + public void onBeanParseStart(String plugin, Object bean) { + ListProjects listProjects = (ListProjects) bean; + listProjects.setLimit(1); + } + + @Override + public void onBeanParseEnd(String plugin, Object bean) {} + } +} +---- + === Calling Command Options === Within an OptionHandler, during the processing of an option, plugins can diff --git a/java/com/google/gerrit/server/DynamicOptions.java b/java/com/google/gerrit/server/DynamicOptions.java index 41dc0828b9..ebf4ec64ab 100644 --- a/java/com/google/gerrit/server/DynamicOptions.java +++ b/java/com/google/gerrit/server/DynamicOptions.java @@ -213,7 +213,7 @@ public class DynamicOptions { Class<?> beanClass = (bean instanceof BeanReceiver) ? ((BeanReceiver) bean).getExportedBeanReceiver() - : getClass(); + : bean.getClass(); for (String plugin : dynamicBeans.plugins()) { Provider<DynamicBean> provider = dynamicBeans.byPlugin(plugin).get(beanClass.getCanonicalName()); diff --git a/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java b/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java new file mode 100644 index 0000000000..39f1e8d75e --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java @@ -0,0 +1,67 @@ +// Copyright (C) 2021 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.acceptance.rest; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.extensions.annotations.Exports; +import com.google.gerrit.json.OutputFormat; +import com.google.gerrit.server.DynamicOptions; +import com.google.gerrit.server.restapi.project.ListProjects; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.inject.AbstractModule; +import java.util.Map; +import org.junit.Test; + +public class DynamicOptionsBeanParseListenerIT extends AbstractDaemonTest { + private static final Gson GSON = OutputFormat.JSON.newGson(); + + @Test + public void testBeanParseListener() throws Exception { + createProjectOverAPI("project1", project, true, null); + createProjectOverAPI("project2", project, true, null); + try (AutoCloseable ignored = installPlugin("my-plugin", PluginModule.class)) { + assertThat(getProjects(adminRestSession.get("/projects/"))).hasSize(1); + } + } + + protected Map<String, Object> getProjects(RestResponse res) throws Exception { + res.assertOK(); + return GSON.fromJson(res.getReader(), new TypeToken<Map<String, Object>>() {}.getType()); + } + + protected static class ListProjectsBeanListener implements DynamicOptions.BeanParseListener { + @Override + public void onBeanParseStart(String plugin, Object bean) { + ListProjects listProjects = (ListProjects) bean; + listProjects.setLimit(1); + } + + @Override + public void onBeanParseEnd(String plugin, Object bean) {} + } + + protected static class PluginModule extends AbstractModule { + @Override + public void configure() { + bind(DynamicOptions.DynamicBean.class) + .annotatedWith(Exports.named(ListProjects.class)) + .to(ListProjectsBeanListener.class); + } + } +} diff --git a/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java b/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java new file mode 100644 index 0000000000..0afdbc6b9e --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java @@ -0,0 +1,69 @@ +// Copyright (C) 2021 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.acceptance.ssh; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.NoHttpd; +import com.google.gerrit.acceptance.UseSsh; +import com.google.gerrit.extensions.annotations.Exports; +import com.google.gerrit.server.DynamicOptions; +import com.google.gerrit.sshd.commands.ListProjectsCommand; +import com.google.inject.AbstractModule; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; + +@NoHttpd +@UseSsh +public class DynamicOptionsBeanParseListenerIT extends AbstractDaemonTest { + + @Test + public void testBeanParseListener() throws Exception { + createProjectOverAPI("project1", project, true, null); + createProjectOverAPI("project2", project, true, null); + try (AutoCloseable ignored = installPlugin("my-plugin", PluginModule.class)) { + String output = adminSshSession.exec("gerrit ls-projects"); + adminSshSession.assertSuccess(); + assertThat(getProjects(output)).hasSize(1); + } + } + + protected List<String> getProjects(String sshOutput) { + return Arrays.asList(sshOutput.split("\n")); + } + + protected static class ListProjectsCommandBeanListener + implements DynamicOptions.BeanParseListener { + @Override + public void onBeanParseStart(String plugin, Object bean) { + ListProjectsCommand command = (ListProjectsCommand) bean; + command.impl.setLimit(1); + } + + @Override + public void onBeanParseEnd(String plugin, Object bean) {} + } + + protected static class PluginModule extends AbstractModule { + @Override + public void configure() { + bind(DynamicOptions.DynamicBean.class) + .annotatedWith(Exports.named(ListProjectsCommand.class)) + .to(ListProjectsCommandBeanListener.class); + } + } +} |