Adds list of extra plugins available from CI Display the new plugins available for installation under the list of the ones currently installed. Plugins repository is injectable and configurable; at the moment only the Gerrit CI (gerrit-ci.gerritforge.com) is implemented. Jenkins CI URL is configured under gerri.config: [plugin "plugin-manager"] jenkinsUrl = https://myjenkins.myserver.com Change-Id: I7fc5140363700c9edc5c28da460ed8606ed73d78
diff --git a/BUCK b/BUCK index de8caf8..8499ba4 100644 --- a/BUCK +++ b/BUCK
@@ -1,3 +1,5 @@ +include_defs('//bucklets/gerrit_plugin.bucklet') + gerrit_plugin( name = 'plugin-manager', srcs = glob(['src/main/java/**/*.java']), @@ -9,4 +11,9 @@ 'Implementation-Title: Plugin manager', 'Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/plugin-manager', ], + provided_deps = [ + '//lib:gson', + '//lib/log:log4j' + ], ) +
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/AvailablePluginsCollection.java b/src/main/java/com/googlesource/gerrit/plugins/manager/AvailablePluginsCollection.java new file mode 100644 index 0000000..6c0629c --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/AvailablePluginsCollection.java
@@ -0,0 +1,67 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager; + +import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.restapi.AcceptsCreate; +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.RestCollection; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.extensions.restapi.RestView; +import com.google.gerrit.extensions.restapi.TopLevelResource; +import com.google.gerrit.server.plugins.PluginResource; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +@Singleton +public class AvailablePluginsCollection implements + RestCollection<TopLevelResource, PluginResource>, + AcceptsCreate<TopLevelResource> { + + private final DynamicMap<RestView<PluginResource>> views; + private final Provider<ListAvailablePlugins> list; + + @Inject + AvailablePluginsCollection(DynamicMap<RestView<PluginResource>> views, + Provider<ListAvailablePlugins> list) { + this.views = views; + this.list = list; + } + + @Override + public RestView<TopLevelResource> list() throws ResourceNotFoundException { + return list.get(); + } + + @Override + public PluginResource parse(TopLevelResource parent, IdString id) + throws ResourceNotFoundException { + throw new ResourceNotFoundException(id); + } + + @Override + public DynamicMap<RestView<PluginResource>> views() { + return views; + } + + @Override + public <I> RestModifyView<TopLevelResource, I> create( + TopLevelResource parent, IdString id) throws RestApiException { + throw new IllegalArgumentException("Operation not supported"); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranch.java b/src/main/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranch.java new file mode 100644 index 0000000..75256b2 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/GerritVersionBranch.java
@@ -0,0 +1,42 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager; + +public class GerritVersionBranch { + private static final String GERRIT_NEXT_VERSION = "2.13"; + + public static String getBranch(String gerritVersion) { + if (gerritVersion == null || gerritVersion.trim().isEmpty() + || !Character.isDigit(gerritVersion.trim().charAt(0)) + || gerritVersion.startsWith(GERRIT_NEXT_VERSION)) { + return "master"; + } else { + String[] versionNumbers = gerritVersion.split("\\."); + + if (versionNumbers.length > 2) { + String fixVersionNumber = versionNumbers[2]; + if (fixVersionNumber.contains("-")) { + String nextVersion = + String.format("%s.%d", versionNumbers[0], + Integer.parseInt(versionNumbers[1]) + 1); + if (nextVersion.equals(GERRIT_NEXT_VERSION)) { + return "master"; + } + } + } + return "stable-" + versionNumbers[0] + "." + versionNumbers[1]; + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java b/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java new file mode 100644 index 0000000..d45c16f --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/ListAvailablePlugins.java
@@ -0,0 +1,74 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager; + +import com.google.common.collect.Maps; +import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.restapi.RestApiException; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.extensions.restapi.TopLevelResource; +import com.google.gerrit.server.OutputFormat; +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; +import com.google.inject.Inject; + +import com.googlesource.gerrit.plugins.manager.repository.PluginInfo; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** List plugins available for installation. */ +@RequiresCapability(GlobalCapability.VIEW_PLUGINS) +public class ListAvailablePlugins implements RestReadView<TopLevelResource> { + private final PluginsCentralLoader loader; + + @Inject + public ListAvailablePlugins(PluginsCentralLoader loader) { + this.loader = loader; + } + + @Override + public Object apply(TopLevelResource resource) throws RestApiException { + return display(); + } + + public JsonElement display() throws RestApiException { + Map<String, PluginInfo> output = Maps.newTreeMap(); + List<PluginInfo> plugins; + try { + plugins = loader.availablePlugins(); + } catch (IOException e) { + throw new RestApiException( + "Unable to load the list of available plugins", e); + } + Collections.sort(plugins, new Comparator<PluginInfo>() { + @Override + public int compare(PluginInfo a, PluginInfo b) { + return a.name.compareTo(b.name); + } + }); + + for (PluginInfo p : plugins) { + output.put(p.name, p); + } + + return OutputFormat.JSON.newGson().toJsonTree(output, + new TypeToken<Map<String, Object>>() {}.getType()); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/PluginManagerRestApiServlet.java b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginManagerRestApiServlet.java new file mode 100644 index 0000000..bb94616 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginManagerRestApiServlet.java
@@ -0,0 +1,32 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager; + +import com.google.gerrit.httpd.restapi.RestApiServlet; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.util.Providers; + +@Singleton +public class PluginManagerRestApiServlet extends RestApiServlet { + private static final long serialVersionUID = 1L; + + @Inject + PluginManagerRestApiServlet( + Globals globals, + AvailablePluginsCollection members) { + super(globals, Providers.of(members)); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/PluginsCentralLoader.java b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginsCentralLoader.java new file mode 100644 index 0000000..33943af --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/PluginsCentralLoader.java
@@ -0,0 +1,40 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager; + +import com.google.gerrit.common.Version; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import com.googlesource.gerrit.plugins.manager.repository.PluginInfo; +import com.googlesource.gerrit.plugins.manager.repository.PluginsRepository; + +import java.io.IOException; +import java.util.List; + +@Singleton +public class PluginsCentralLoader { + + private final PluginsRepository repository; + + @Inject + public PluginsCentralLoader(PluginsRepository repository) { + this.repository = repository; + } + + public List<PluginInfo> availablePlugins() throws IOException { + return repository.list(Version.getVersion()); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/TokenReplaceOutputStream.java b/src/main/java/com/googlesource/gerrit/plugins/manager/TokenReplaceOutputStream.java index 9810318..2c5976b 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/TokenReplaceOutputStream.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/TokenReplaceOutputStream.java
@@ -30,9 +30,9 @@ private final ByteArrayOutputStream outBuff; - private HttpServletResponse resp; + private final HttpServletResponse resp; - private int outLen; + private final int outLen; public TokenReplaceOutputStream(HttpServletResponse resp, int contentLength, byte[] token, byte[] replace) { @@ -78,7 +78,6 @@ ServletOutputStream out = resp.getOutputStream(); out.write(convertedData.toByteArray()); out.flush(); - } @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/WebModule.java b/src/main/java/com/googlesource/gerrit/plugins/manager/WebModule.java index 9a32518..c9994f1 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/WebModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/WebModule.java
@@ -13,18 +13,20 @@ // limitations under the License. package com.googlesource.gerrit.plugins.manager; -import com.google.gerrit.extensions.restapi.RestApiModule; import com.google.inject.servlet.ServletModule; +import com.googlesource.gerrit.plugins.manager.repository.JenkinsCiPluginsRepository; +import com.googlesource.gerrit.plugins.manager.repository.PluginsRepository; + public class WebModule extends ServletModule { @Override protected void configureServlets() { - install(new RestApiModule() { - @Override - protected void configure() { - filterRegex(".*\\.js").through(XAuthFilter.class); - } - }); + bind(AvailablePluginsCollection.class); + bind(PluginsRepository.class).to(JenkinsCiPluginsRepository.class); + + serve("/available*").with(PluginManagerRestApiServlet.class); + + filterRegex(".*\\.js").through(XAuthFilter.class); } }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java b/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java index 68874be..33f05b3 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/XAuthFilter.java
@@ -59,14 +59,14 @@ final String gerritAuth = webSession.get().getXGerritAuth(); if (gerritAuth != null) { - log.info("Injecting X-Gerrit-Auth for {}", httpReq.getRequestURI()); + log.debug("Injecting X-Gerrit-Auth for {}", httpReq.getRequestURI()); httpResp = new HttpServletResponseWrapper(httpResp) { private int origContentLength; @Override public void setHeader(String name, String value) { - log.info("{}: {}", name, value); + log.debug("{}: {}", name, value); if (name.equalsIgnoreCase("Content-Length")) { origContentLength = Integer.parseInt(value); } else {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartGson.java b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartGson.java new file mode 100644 index 0000000..400505d --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartGson.java
@@ -0,0 +1,55 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.gson; + +import com.google.gerrit.server.OutputFormat; +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; + +public class SmartGson { + + private final Gson gson; + + public SmartGson() { + this.gson = OutputFormat.JSON.newGson(); + } + + public SmartJson get(String url) throws IOException { + return SmartJson.of(gson.fromJson(getReader(url), JsonObject.class)); + } + + public <T> T get(String url, Class<T> classOfT) throws IOException { + try (Reader reader = getReader(url)) { + return gson.fromJson(reader, classOfT); + } + } + + private InputStreamReader getReader(String url) throws IOException { + URL ciUrl; + try { + ciUrl = new URL(url); + } catch (MalformedURLException e) { + throw new IllegalArgumentException( + "Internal error: Gerrit CI URL seems to be malformed", e); + } + return new InputStreamReader(ciUrl.openStream()); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartJson.java b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartJson.java new file mode 100644 index 0000000..7766796 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/gson/SmartJson.java
@@ -0,0 +1,66 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.gson; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.gson.JsonElement; + +public class SmartJson { + + private final JsonElement jsonElem; + + private SmartJson(JsonElement elem) { + this.jsonElem = elem; + } + + public static SmartJson of(JsonElement fromJson) { + return new SmartJson(fromJson); + } + + public Optional<String> getOptionalString(String fieldName) { + return getOptional(fieldName).transform(new Function<SmartJson, String>() { + @Override + public String apply(SmartJson elem) { + if (!elem.jsonElem.isJsonPrimitive()) { + throw new IllegalArgumentException("cannot convert " + elem.jsonElem + + " into a String"); + } + return elem.jsonElem.getAsString(); + } + }); + } + + public String getString(String fieldName) { + return getOptionalString(fieldName).or(""); + } + + public Optional<SmartJson> getOptional(String fieldName) { + if (jsonElem != null && jsonElem.getAsJsonObject().get(fieldName) != null) { + return Optional.of(SmartJson + .of(jsonElem.getAsJsonObject().get(fieldName))); + } else { + return Optional.absent(); + } + } + + public SmartJson get(String fieldName) { + return getOptional(fieldName).get(); + } + + public JsonElement get() { + return jsonElem; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/JenkinsCiPluginsRepository.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/JenkinsCiPluginsRepository.java new file mode 100644 index 0000000..d1620b3 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/JenkinsCiPluginsRepository.java
@@ -0,0 +1,150 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.repository; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.gerrit.extensions.annotations.PluginName; +import com.google.gerrit.server.config.PluginConfig; +import com.google.gerrit.server.config.PluginConfigFactory; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import com.googlesource.gerrit.plugins.manager.GerritVersionBranch; +import com.googlesource.gerrit.plugins.manager.gson.SmartGson; +import com.googlesource.gerrit.plugins.manager.gson.SmartJson; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Singleton +public class JenkinsCiPluginsRepository implements PluginsRepository { + + private static final Logger log = LoggerFactory + .getLogger(JenkinsCiPluginsRepository.class); + + private static final String DEFAULT_GERRIT_CI_URL = + "https://gerrit-ci.gerritforge.com"; + + private static final Optional<PluginInfo> noPluginInfo = Optional.absent(); + + private final PluginConfig config; + + static class View { + String name; + Job[] jobs; + } + + static class Job { + String name; + String url; + String color; + } + + private final Provider<SmartGson> gsonProvider; + + @Inject + public JenkinsCiPluginsRepository(Provider<SmartGson> gsonProvider, + PluginConfigFactory configFactory, @PluginName String pluginName) { + this.gsonProvider = gsonProvider; + this.config = configFactory.getFromGerritConfig(pluginName); + } + + @Override + public List<PluginInfo> list(String gerritVersion) throws IOException { + SmartGson gson = gsonProvider.get(); + String viewName = "Plugins-" + GerritVersionBranch.getBranch(gerritVersion); + List<PluginInfo> plugins = new ArrayList<>(); + + try { + Job[] jobs = + gson.get(getJenkinsUrl() + "/view/" + viewName + "/api/json", + View.class).jobs; + + for (Job job : jobs) { + if (job.color.equals("blue")) { + Optional<PluginInfo> pluginInfo = getPluginInfo(gson, job.url); + if (pluginInfo.isPresent()) { + plugins.add(pluginInfo.get()); + } + } + } + } catch (FileNotFoundException e) { + log.warn("No plugins available for Gerrit version " + gerritVersion, e); + } + + return plugins; + } + + private String getJenkinsUrl() { + return config.getString("jenkinsUrl", DEFAULT_GERRIT_CI_URL); + } + + private Optional<PluginInfo> getPluginInfo(final SmartGson gson, String url) + throws IOException { + SmartJson jobDetails = gson.get(url + "/api/json"); + Optional<SmartJson> lastSuccessfulBuild = + jobDetails.getOptional("lastSuccessfulBuild"); + + return lastSuccessfulBuild.transform( + new Function<SmartJson, Optional<PluginInfo>>() { + @Override + public Optional<PluginInfo> apply(SmartJson build) { + String buildUrl = build.getString("url"); + try { + return getPluginArtifactInfo(gson, buildUrl); + } catch (IOException e) { + log.warn("Cannot retrieve plugin artifact info from {}", buildUrl); + return noPluginInfo; + } + } + }).or(noPluginInfo); + } + + private Optional<PluginInfo> getPluginArtifactInfo(SmartGson gson, String url) + throws IOException { + SmartJson buildExecution = gson.get(url + "/api/json"); + JsonArray artifacts = + buildExecution.get("artifacts").get().getAsJsonArray(); + if (artifacts.size() == 0) { + return Optional.absent(); + } + + SmartJson artifactJson = SmartJson.of(artifacts.get(0)); + String pluginFileName = artifactJson.getString("fileName"); + + String pluginVersion = ""; + for (JsonElement elem : buildExecution.get("actions").get() + .getAsJsonArray()) { + SmartJson elemJson = SmartJson.of(elem); + Optional<SmartJson> lastBuildRevision = + elemJson.getOptional("lastBuiltRevision"); + + if (lastBuildRevision.isPresent()) { + pluginVersion = lastBuildRevision.get().getString("SHA1"); + } + } + + return Optional.of(new PluginInfo(pluginFileName, pluginVersion)); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginInfo.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginInfo.java new file mode 100644 index 0000000..03e4d8a --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginInfo.java
@@ -0,0 +1,29 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.repository; + +import com.google.gerrit.extensions.restapi.Url; + +public class PluginInfo { + public final String id; + public final String name; + public final String version; + + PluginInfo(String name, String version) { + this.id = Url.encode(name); + this.name = name; + this.version = version; + } +} \ No newline at end of file
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepository.java b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepository.java new file mode 100644 index 0000000..236fc28 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manager/repository/PluginsRepository.java
@@ -0,0 +1,23 @@ +// Copyright (C) 2015 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.googlesource.gerrit.plugins.manager.repository; + +import java.io.IOException; +import java.util.List; + +public interface PluginsRepository { + + List<PluginInfo> list(String gerritVersion) throws IOException; +}
diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 9d16466..40d710d 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html
@@ -11,7 +11,13 @@ <h1>Installed plugins</h1> <ul> <li ng-repeat="(key, prop) in plugins.list"> - {{key}} + {{key}} - {{prop.version}} + </li> + </ul> + <h1>Available plugins</h1> + <ul> + <li ng-repeat="(key, prop) in plugins.available"> + {{key}} - {{prop.version}} </li> </ul> </div>
diff --git a/src/main/resources/static/js/plugin-manager.js b/src/main/resources/static/js/plugin-manager.js index ad01b06..d3e455a 100644 --- a/src/main/resources/static/js/plugin-manager.js +++ b/src/main/resources/static/js/plugin-manager.js
@@ -19,12 +19,18 @@ plugins.list = {}; + plugins.available = {}; + $http.get('/plugins/?all', plugins.httpConfig).then( function successCallback(response) { plugins.list = response.data; }, function errorCallback(response) { - // called asynchronously if an error occurs - // or server returns response with an error status. + }); + + $http.get('/plugins/plugin-manager/available', plugins.httpConfig).then( + function successCallback(response) { + plugins.available = response.data; + }, function errorCallback(response) { }); });