Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: plugin installer #55

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
### Added
- Sonarcloud check
- Possibility to install a plugin via the frontpage

### Changed
- CoffeeNet Starter Parent to version `0.38.2`
Expand Down
23 changes: 12 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,6 @@
<artifactId>starter-monitoring</artifactId>
</dependency>

<!-- Spring Config -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-config-client-oauth2</artifactId>
<version>1.0.2.RELEASE</version>
</dependency>

<!-- Frontend -->
<dependency>
<groupId>rocks.coffeenet</groupId>
Expand All @@ -79,6 +68,12 @@
<version>0.8.0</version>
</dependency>

<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>github-api</artifactId>
<version>1.89</version>
</dependency>

<!-- Persistence -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -91,6 +86,12 @@
<artifactId>spring-boot-devtools</artifactId>
</dependency>

<!-- Configuration processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package rocks.coffeenet.frontpage.plugin.management;

/**
* @author Tobias Schneider
*/
class AvailablePluginAssets {

private final String name;
private final String url;

AvailablePluginAssets(String name, String url) {
this.name = name;
this.url = url;
}

public String getName() {
return name;
}

public String getUrl() {
return url;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package rocks.coffeenet.frontpage.plugin.management;

import java.util.List;

/**
* @author Tobias Schneider
*/
public class AvailablePlugins {

private final String name;
private final List<AvailablePluginAssets> versions;

public AvailablePlugins(String name, List<AvailablePluginAssets> versions) {
this.name = name;
this.versions = versions;
}

public String getName() {
return name;
}

public List<AvailablePluginAssets> getVersions() {
return versions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package rocks.coffeenet.frontpage.plugin.management;

import org.kohsuke.github.GitHubBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
* @author Tobias Schneider
*/
@Configuration
public class PluginConfiguration {

@Bean
PluginConfigurationProperties pluginConfigurationProperties() {

return new PluginConfigurationProperties();
}

@Bean
GitHubBuilder gitHubBuilder() {

return new GitHubBuilder();
}

@Bean
RestTemplate restTemplate(){

return new RestTemplate();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package rocks.coffeenet.frontpage.plugin.management;

import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

import java.util.List;

/**
* @author Tobias Schneider
*/
@Validated
@ConfigurationProperties("frontpage.plugins")
public class PluginConfigurationProperties {

@NotEmpty
private String directory = "./plugins";

private List<Resource> resource;

@Validated
public static class Resource {

@NotEmpty
private String repositoryName;

public String getRepositoryName() {
return repositoryName;
}

public void setRepositoryName(String repositoryName) {
this.repositoryName = repositoryName;
}
}

public String getDirectory() {
return directory;
}

public void setDirectory(String directory) {
this.directory = directory;
}

public List<Resource> getResource() {
return resource;
}

public void setResource(List<Resource> resource) {
this.resource = resource;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package rocks.coffeenet.frontpage.plugin.management;

/**
* @author Tobias Schneider
*/
public class PluginInstallationException extends RuntimeException {

PluginInstallationException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package rocks.coffeenet.frontpage.plugin.management;

/**
* @author Tobias Schneider
*/
public interface PluginInstallerService {

/**
* Installs a plugin into the defined directory of `frontpage.plugins.directory`
*
* @param url of the plugin to download and install
*
* @throws PluginInstallationException if an error occurred
*/
void installPlugin(String url);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package rocks.coffeenet.frontpage.plugin.management;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;
import java.util.Optional;

import static java.util.Collections.emptyList;

@Controller
public class PluginManagementController {

private final PluginManagementService pluginManagementService;
private final PluginInstallerService pluginInstallerService;

@Autowired
public PluginManagementController(PluginManagementService pluginManagementService, PluginInstallerService pluginInstallerService) {

this.pluginManagementService = pluginManagementService;
this.pluginInstallerService = pluginInstallerService;
}

@GetMapping("/plugins-install")
public String getPlugins(Model model) {

List<AvailablePlugins> availablePlugins = emptyList();

Optional<List<AvailablePlugins>> listOfAvailablePlugins = pluginManagementService.getListOfAvailablePlugins();
if (listOfAvailablePlugins.isPresent()) {
availablePlugins = listOfAvailablePlugins.get();
}

model.addAttribute("availablePlugins", availablePlugins);
return "management/plugins";
}

@PutMapping("/plugins-install")
public String installPlugin(@RequestParam String url) {

pluginInstallerService.installPlugin(url);

return "redirect:/plugins-install";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package rocks.coffeenet.frontpage.plugin.management;

/**
* @author Tobias Schneider
*/
public class PluginManagementException extends RuntimeException {

PluginManagementException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package rocks.coffeenet.frontpage.plugin.management;

import rocks.coffeenet.frontpage.plugin.management.PluginConfigurationProperties.Resource;
import org.kohsuke.github.GHRelease;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static java.lang.String.format;
import static java.util.stream.Collectors.toList;

/**
* @author Tobias Schneider
*/
@Service
public class PluginManagementService {

private final PluginConfigurationProperties pluginConfigurationProperties;
private final GitHubBuilder gitHubBuilder;

@Autowired
public PluginManagementService(PluginConfigurationProperties pluginConfigurationProperties, GitHubBuilder gitHubBuilder) {

this.pluginConfigurationProperties = pluginConfigurationProperties;
this.gitHubBuilder = gitHubBuilder;
}


Optional<List<AvailablePlugins>> getListOfAvailablePlugins() {

List<Resource> pluginResources = pluginConfigurationProperties.getResource();

if (pluginResources == null) {
return Optional.empty();
}

List<AvailablePlugins> availablePlugins = new ArrayList<>();

for (Resource pluginResource : pluginResources) {
GHRelease latestRelease = getLatestRelease(pluginResource);
List<AvailablePluginAssets> latestReleaseAssets = getAvailablePluginAssets(latestRelease);

availablePlugins.add(new AvailablePlugins(pluginResource.getRepositoryName(), latestReleaseAssets));
}

return Optional.of(availablePlugins);
}

private List<AvailablePluginAssets> getAvailablePluginAssets(GHRelease release) {
try {

if (release == null) {
return Collections.emptyList();
}

return release.getAssets()
.stream()
.map(ghAsset -> new AvailablePluginAssets(ghAsset.getName(), ghAsset.getBrowserDownloadUrl()))
.collect(toList());
} catch (IOException e) {
throw new PluginManagementException("");
}
}

private GHRelease getLatestRelease(Resource resource) {
try {
GitHub github = gitHubBuilder.build();
return github.getRepository(resource.getRepositoryName()).getLatestRelease();
} catch (IOException e) {
throw new PluginManagementException(format("Could not retrieve lists of release from %s", resource.getRepositoryName()));
}
}
}
Loading