Add implementation of PluginApi

- install
- get status
- enable
- disable
- reload

Change-Id: I62fdc63372918a7e63760f4ea49b203d7fcb244d
This commit is contained in:
David Pursehouse
2017-07-27 14:21:43 +01:00
parent 92cacc93eb
commit 5b002a187c
12 changed files with 268 additions and 14 deletions

View File

@@ -15,16 +15,89 @@
package com.google.gerrit.acceptance.api.plugin; package com.google.gerrit.acceptance.api.plugin;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.common.RawInputUtil;
import com.google.gerrit.extensions.api.plugins.PluginApi;
import com.google.gerrit.extensions.api.plugins.Plugins.ListRequest;
import com.google.gerrit.extensions.common.InstallPluginInput;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import java.util.List;
import org.junit.Test; import org.junit.Test;
@NoHttpd @NoHttpd
public class PluginIT extends AbstractDaemonTest { public class PluginIT extends AbstractDaemonTest {
private static final byte[] JS_PLUGIN_CONTENT =
"Gerrit.install(function(self){});\n".getBytes(UTF_8);
private static final List<String> PLUGINS =
ImmutableList.of("plugin-a", "plugin-b", "plugin-c", "plugin-d");
@Test @Test
public void list() throws Exception { @GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
assertThat(gApi.plugins().list().get()).isEmpty(); public void pluginManagement() throws Exception {
assertThat(gApi.plugins().list().all().get()).isEmpty(); // No plugins are loaded
assertThat(list().get()).isEmpty();
assertThat(list().all().get()).isEmpty();
PluginApi test;
PluginInfo info;
// Install all the plugins
InstallPluginInput input = new InstallPluginInput();
input.raw = RawInputUtil.create(JS_PLUGIN_CONTENT);
for (String plugin : PLUGINS) {
test = gApi.plugins().install(plugin + ".js", input);
assertThat(test).isNotNull();
info = test.get();
assertThat(info.id).isEqualTo(plugin);
assertThat(info.disabled).isNull();
}
assertPlugins(list().get(), PLUGINS);
// Disable
test = gApi.plugins().name("plugin-a");
test.disable();
test = gApi.plugins().name("plugin-a");
info = test.get();
assertThat(info.disabled).isTrue();
assertPlugins(list().get(), PLUGINS.subList(1, PLUGINS.size()));
assertPlugins(list().all().get(), PLUGINS);
// Enable
test.enable();
test = gApi.plugins().name("plugin-a");
info = test.get();
assertThat(info.disabled).isNull();
assertPlugins(list().get(), PLUGINS);
}
@Test
public void installNotAllowed() throws Exception {
exception.expect(MethodNotAllowedException.class);
exception.expectMessage("remote installation is disabled");
gApi.plugins().install("test.js", new InstallPluginInput());
}
@Test
public void getNonExistingThrowsNotFound() throws Exception {
exception.expect(ResourceNotFoundException.class);
gApi.plugins().name("does-not-exist");
}
private ListRequest list() throws RestApiException {
return gApi.plugins().list();
}
private void assertPlugins(List<PluginInfo> actual, List<String> expected) {
List<String> _actual = actual.stream().map(p -> p.id).collect(toList());
assertThat(_actual).containsExactlyElementsIn(expected);
} }
} }

View File

@@ -0,0 +1,55 @@
// 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.api.plugins;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
public interface PluginApi {
PluginInfo get() throws RestApiException;
void enable() throws RestApiException;
void disable() throws RestApiException;
void reload() throws RestApiException;
/**
* A default implementation which allows source compatibility when adding new methods to the
* interface.
*/
class NotImplemented implements PluginApi {
@Override
public PluginInfo get() throws RestApiException {
throw new NotImplementedException();
}
@Override
public void enable() throws RestApiException {
throw new NotImplementedException();
}
@Override
public void disable() throws RestApiException {
throw new NotImplementedException();
}
@Override
public void reload() throws RestApiException {
throw new NotImplementedException();
}
}
}

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.extensions.api.plugins; package com.google.gerrit.extensions.api.plugins;
import com.google.gerrit.extensions.common.InstallPluginInput;
import com.google.gerrit.extensions.common.PluginInfo; import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException; import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
@@ -24,7 +25,11 @@ import java.util.SortedMap;
public interface Plugins { public interface Plugins {
ListRequest list(); ListRequest list() throws RestApiException;
PluginApi name(String name) throws RestApiException;
PluginApi install(String name, InstallPluginInput input) throws RestApiException;
abstract class ListRequest { abstract class ListRequest {
private boolean all; private boolean all;
@@ -59,5 +64,15 @@ public interface Plugins {
public ListRequest list() { public ListRequest list() {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@Override
public PluginApi name(String name) {
throw new NotImplementedException();
}
@Override
public PluginApi install(String name, InstallPluginInput input) throws RestApiException {
throw new NotImplementedException();
}
} }
} }

View File

@@ -0,0 +1,72 @@
// 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.server.api.plugins;
import com.google.gerrit.extensions.api.plugins.PluginApi;
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.plugins.DisablePlugin;
import com.google.gerrit.server.plugins.EnablePlugin;
import com.google.gerrit.server.plugins.GetStatus;
import com.google.gerrit.server.plugins.PluginResource;
import com.google.gerrit.server.plugins.ReloadPlugin;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
public class PluginApiImpl implements PluginApi {
public interface Factory {
PluginApiImpl create(PluginResource resource);
}
private final GetStatus getStatus;
private final EnablePlugin enable;
private final DisablePlugin disable;
private final ReloadPlugin reload;
private final PluginResource resource;
@Inject
PluginApiImpl(
GetStatus getStatus,
EnablePlugin enable,
DisablePlugin disable,
ReloadPlugin reload,
@Assisted PluginResource resource) {
this.getStatus = getStatus;
this.enable = enable;
this.disable = disable;
this.reload = reload;
this.resource = resource;
}
@Override
public PluginInfo get() throws RestApiException {
return getStatus.apply(resource);
}
@Override
public void enable() throws RestApiException {
enable.apply(resource, new EnablePlugin.Input());
}
@Override
public void disable() throws RestApiException {
disable.apply(resource, new DisablePlugin.Input());
}
@Override
public void reload() throws RestApiException {
reload.apply(resource, new ReloadPlugin.Input());
}
}

View File

@@ -14,22 +14,44 @@
package com.google.gerrit.server.api.plugins; package com.google.gerrit.server.api.plugins;
import com.google.gerrit.extensions.api.plugins.PluginApi;
import com.google.gerrit.extensions.api.plugins.Plugins; import com.google.gerrit.extensions.api.plugins.Plugins;
import com.google.gerrit.extensions.common.InstallPluginInput;
import com.google.gerrit.extensions.common.PluginInfo; import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.server.plugins.InstallPlugin;
import com.google.gerrit.server.plugins.ListPlugins; import com.google.gerrit.server.plugins.ListPlugins;
import com.google.gerrit.server.plugins.PluginsCollection;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.io.IOException;
import java.util.SortedMap; import java.util.SortedMap;
@Singleton @Singleton
public class PluginsImpl implements Plugins { public class PluginsImpl implements Plugins {
private final PluginsCollection plugins;
private final Provider<ListPlugins> listProvider; private final Provider<ListPlugins> listProvider;
private final Provider<InstallPlugin> installProvider;
private final PluginApiImpl.Factory pluginApi;
@Inject @Inject
PluginsImpl(Provider<ListPlugins> listProvider) { PluginsImpl(
PluginsCollection plugins,
Provider<ListPlugins> listProvider,
Provider<InstallPlugin> installProvider,
PluginApiImpl.Factory pluginApi) {
this.plugins = plugins;
this.listProvider = listProvider; this.listProvider = listProvider;
this.installProvider = installProvider;
this.pluginApi = pluginApi;
}
@Override
public PluginApi name(String name) throws RestApiException {
return pluginApi.create(plugins.parse(name));
} }
@Override @Override
@@ -43,4 +65,15 @@ public class PluginsImpl implements Plugins {
} }
}; };
} }
@Override
public PluginApi install(String name, InstallPluginInput input) throws RestApiException {
try {
Response<PluginInfo> created =
installProvider.get().setName(name).apply(TopLevelResource.INSTANCE, input);
return pluginApi.create(plugins.parse(created.value().id));
} catch (IOException e) {
throw new RestApiException("could not install plugin", e);
}
}
} }

View File

@@ -26,8 +26,8 @@ import com.google.inject.Singleton;
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER) @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@Singleton @Singleton
class DisablePlugin implements RestModifyView<PluginResource, Input> { public class DisablePlugin implements RestModifyView<PluginResource, Input> {
static class Input {} public static class Input {}
private final PluginLoader loader; private final PluginLoader loader;

View File

@@ -29,8 +29,8 @@ import java.io.StringWriter;
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER) @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@Singleton @Singleton
class EnablePlugin implements RestModifyView<PluginResource, Input> { public class EnablePlugin implements RestModifyView<PluginResource, Input> {
static class Input {} public static class Input {}
private final PluginLoader loader; private final PluginLoader loader;

View File

@@ -19,7 +19,7 @@ import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@Singleton @Singleton
class GetStatus implements RestReadView<PluginResource> { public class GetStatus implements RestReadView<PluginResource> {
@Override @Override
public PluginInfo apply(PluginResource resource) { public PluginInfo apply(PluginResource resource) {
return ListPlugins.toPluginInfo(resource.getPlugin()); return ListPlugins.toPluginInfo(resource.getPlugin());

View File

@@ -33,7 +33,7 @@ import java.net.URL;
import java.util.zip.ZipException; import java.util.zip.ZipException;
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER) @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
class InstallPlugin implements RestModifyView<TopLevelResource, InstallPluginInput> { public class InstallPlugin implements RestModifyView<TopLevelResource, InstallPluginInput> {
private final PluginLoader loader; private final PluginLoader loader;
private String name; private String name;

View File

@@ -19,6 +19,7 @@ import static com.google.gerrit.server.plugins.PluginResource.PLUGIN_KIND;
import com.google.gerrit.extensions.api.plugins.Plugins; import com.google.gerrit.extensions.api.plugins.Plugins;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestApiModule; import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.gerrit.server.api.plugins.PluginApiImpl;
import com.google.gerrit.server.api.plugins.PluginsImpl; import com.google.gerrit.server.api.plugins.PluginsImpl;
public class PluginRestApiModule extends RestApiModule { public class PluginRestApiModule extends RestApiModule {
@@ -33,5 +34,6 @@ public class PluginRestApiModule extends RestApiModule {
post(PLUGIN_KIND, "enable").to(EnablePlugin.class); post(PLUGIN_KIND, "enable").to(EnablePlugin.class);
post(PLUGIN_KIND, "reload").to(ReloadPlugin.class); post(PLUGIN_KIND, "reload").to(ReloadPlugin.class);
bind(Plugins.class).to(PluginsImpl.class); bind(Plugins.class).to(PluginsImpl.class);
factory(PluginApiImpl.Factory.class);
} }
} }

View File

@@ -55,7 +55,11 @@ public class PluginsCollection
@Override @Override
public PluginResource parse(TopLevelResource parent, IdString id) public PluginResource parse(TopLevelResource parent, IdString id)
throws ResourceNotFoundException { throws ResourceNotFoundException {
Plugin p = loader.get(id.get()); return parse(id.get());
}
public PluginResource parse(String id) throws ResourceNotFoundException {
Plugin p = loader.get(id);
if (p == null) { if (p == null) {
throw new ResourceNotFoundException(id); throw new ResourceNotFoundException(id);
} }

View File

@@ -28,8 +28,8 @@ import java.io.StringWriter;
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER) @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@Singleton @Singleton
class ReloadPlugin implements RestModifyView<PluginResource, Input> { public class ReloadPlugin implements RestModifyView<PluginResource, Input> {
static class Input {} public static class Input {}
private final PluginLoader loader; private final PluginLoader loader;