refactor plugin endpoints

This commit is contained in:
Eduard Heimbuch
2019-07-31 10:27:52 +02:00
parent 598a4e6f32
commit a9744d8df1
12 changed files with 210 additions and 107 deletions

View File

@@ -282,16 +282,6 @@ public class PluginInformation
return version; return version;
} }
/**
* Method description
*
*
* @return
*/
public Map<String, Object> getLinks() {
return links;
}
/** /**
* Method description * Method description
* *
@@ -384,17 +374,6 @@ public class PluginInformation
this.version = version; this.version = version;
} }
/**
* Method description
*
*
* @param links
*/
public void setLinks(Map<String, Object> links) {
this.links = links;
}
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */ /** Field description */
@@ -418,7 +397,4 @@ public class PluginInformation
/** Field description */ /** Field description */
private String version; private String version;
/** Field description */
private Map<String, Object> links;
} }

View File

@@ -0,0 +1,106 @@
package sonia.scm.api.v2.resources;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import sonia.scm.plugin.Plugin;
import sonia.scm.plugin.PluginInformation;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.PluginManager;
import sonia.scm.plugin.PluginPermissions;
import sonia.scm.plugin.PluginState;
import sonia.scm.plugin.PluginWrapper;
import sonia.scm.web.VndMediaType;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound;
public class AvailablePluginResource {
private final PluginDtoCollectionMapper collectionMapper;
private PluginDtoMapper dtoMapper;
private final PluginManager pluginManager;
@Inject
public AvailablePluginResource(PluginDtoCollectionMapper collectionMapper, PluginDtoMapper dtoMapper, PluginManager pluginManager) {
this.collectionMapper = collectionMapper;
this.dtoMapper = dtoMapper;
this.pluginManager = pluginManager;
}
/**
* Returns a collection of available plugins.
*
* @return collection of available plugins.
*/
@GET
@Path("")
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(CollectionDto.class)
@Produces(VndMediaType.PLUGIN_COLLECTION)
public Response getAvailablePlugins() {
PluginPermissions.read().check();
Collection<PluginInformation> plugins = pluginManager.getAvailable()
.stream()
.filter(plugin -> plugin.getState().equals(PluginState.AVAILABLE))
.collect(Collectors.toList());
return Response.ok(collectionMapper.map(plugins)).build();
}
/**
* Returns available plugin.
*
* @return available plugin.
*/
@GET
@Path("/{name}/{version}")
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(CollectionDto.class)
@Produces(VndMediaType.PLUGIN)
public Response getAvailablePlugin(@PathParam("name") String name, @PathParam("version") String version) {
PluginPermissions.read().check();
Optional<PluginInformation> plugin = pluginManager.getAvailable()
.stream()
.filter(p -> p.getId().equals(name + ":" + version))
.findFirst();
return Response.ok(dtoMapper.map(plugin.get())).build();
}
/**
* Returns 200 when plugin installation is successful triggered.
*
* @return HTTP Status.
*/
@POST
@Path("/{name}/{version}/install")
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(CollectionDto.class)
@Produces(VndMediaType.PLUGIN)
public Response installPlugin(@PathParam("name") String name, @PathParam("version") String version) {
PluginPermissions.manage().check();
pluginManager.install(name + ":" + version);
return Response.ok().build();
}
}

View File

@@ -42,7 +42,8 @@ public class IndexDtoGenerator extends HalAppenderMapper {
link("logout", resourceLinks.authentication().logout()) link("logout", resourceLinks.authentication().logout())
); );
if (PluginPermissions.read().isPermitted()) { if (PluginPermissions.read().isPermitted()) {
builder.single(link("plugins", resourceLinks.pluginCollection().self())); builder.single(link("installedPlugins", resourceLinks.installedPluginCollection().self()));
builder.single(link("availablePlugins", resourceLinks.availablePluginCollection().self()));
} }
if (UserPermissions.list().isPermitted()) { if (UserPermissions.list().isPermitted()) {
builder.single(link("users", resourceLinks.userCollection().self())); builder.single(link("users", resourceLinks.userCollection().self()));

View File

@@ -15,6 +15,7 @@ import sonia.scm.web.VndMediaType;
import javax.inject.Inject; import javax.inject.Inject;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
@@ -28,7 +29,7 @@ import java.util.stream.Collectors;
import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.ContextEntry.ContextBuilder.entity;
import static sonia.scm.NotFoundException.notFound; import static sonia.scm.NotFoundException.notFound;
public class PluginResource { public class InstalledPluginResource {
private final PluginLoader pluginLoader; private final PluginLoader pluginLoader;
private final PluginDtoCollectionMapper collectionMapper; private final PluginDtoCollectionMapper collectionMapper;
@@ -36,7 +37,7 @@ public class PluginResource {
private final PluginManager pluginManager; private final PluginManager pluginManager;
@Inject @Inject
public PluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginManager pluginManager) { public InstalledPluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginManager pluginManager) {
this.pluginLoader = pluginLoader; this.pluginLoader = pluginLoader;
this.collectionMapper = collectionMapper; this.collectionMapper = collectionMapper;
this.mapper = mapper; this.mapper = mapper;
@@ -70,7 +71,7 @@ public class PluginResource {
* @return installed plugin with specified id * @return installed plugin with specified id
*/ */
@GET @GET
@Path("{id}") @Path("/{id}")
@StatusCodes({ @StatusCodes({
@ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 404, condition = "not found"), @ResponseCode(code = 404, condition = "not found"),
@@ -91,27 +92,4 @@ public class PluginResource {
throw notFound(entity(Plugin.class, id)); throw notFound(entity(Plugin.class, id));
} }
} }
/**
* Returns a collection of available plugins.
*
* @return collection of available plugins.
*/
@GET
@Path("/available")
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(CollectionDto.class)
@Produces(VndMediaType.PLUGIN_COLLECTION)
public Response getAvailablePlugins() {
PluginPermissions.read().check();
Collection<PluginInformation> plugins = pluginManager.getAvailable()
.stream()
.filter(plugin -> plugin.getState().equals(PluginState.AVAILABLE))
.collect(Collectors.toList());
return Response.ok(collectionMapper.map(plugins)).build();
}
} }

View File

@@ -27,16 +27,24 @@ public class PluginDtoCollectionMapper {
public HalRepresentation map(List<PluginWrapper> plugins) { public HalRepresentation map(List<PluginWrapper> plugins) {
List<PluginDto> dtos = plugins.stream().map(mapper::map).collect(toList()); List<PluginDto> dtos = plugins.stream().map(mapper::map).collect(toList());
return new HalRepresentation(createLinks(), embedDtos(dtos)); return new HalRepresentation(createInstalledPluginsLinks(), embedDtos(dtos));
} }
public HalRepresentation map(Collection<PluginInformation> plugins) { public HalRepresentation map(Collection<PluginInformation> plugins) {
List<PluginDto> dtos = plugins.stream().map(mapper::map).collect(toList()); List<PluginDto> dtos = plugins.stream().map(mapper::map).collect(toList());
return new HalRepresentation(createLinks(), embedDtos(dtos)); return new HalRepresentation(createAvailablePluginsLinks(), embedDtos(dtos));
} }
private Links createLinks() { private Links createInstalledPluginsLinks() {
String baseUrl = resourceLinks.pluginCollection().self(); String baseUrl = resourceLinks.installedPluginCollection().self();
Links.Builder linksBuilder = linkingTo()
.with(Links.linkingTo().self(baseUrl).build());
return linksBuilder.build();
}
private Links createAvailablePluginsLinks() {
String baseUrl = resourceLinks.availablePluginCollection().self();
Links.Builder linksBuilder = linkingTo() Links.Builder linksBuilder = linkingTo()
.with(Links.linkingTo().self(baseUrl).build()); .with(Links.linkingTo().self(baseUrl).build());

View File

@@ -2,11 +2,11 @@ package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Links; import de.otto.edison.hal.Links;
import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginInformation;
import sonia.scm.plugin.PluginState;
import sonia.scm.plugin.PluginWrapper; import sonia.scm.plugin.PluginWrapper;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.Map; import static de.otto.edison.hal.Link.*;
import static de.otto.edison.hal.Links.linkingTo; import static de.otto.edison.hal.Links.linkingTo;
public class PluginDtoMapper { public class PluginDtoMapper {
@@ -23,13 +23,18 @@ public class PluginDtoMapper {
} }
public PluginDto map(PluginInformation pluginInformation) { public PluginDto map(PluginInformation pluginInformation) {
Links.Builder linksBuilder = linkingTo() Links.Builder linksBuilder;
.self(resourceLinks.plugin() if (pluginInformation.getState() != null && pluginInformation.getState().equals(PluginState.AVAILABLE)) {
.self(pluginInformation.getName())); linksBuilder = linkingTo()
.self(resourceLinks.availablePlugin()
.self(pluginInformation.getName(), pluginInformation.getVersion()));
for (Object link : pluginInformation.getLinks().values()) { linksBuilder.single(link("install", resourceLinks.availablePlugin().install(pluginInformation.getName(), pluginInformation.getVersion())));
System.out.println("Link is = " + link.toString()); }
linksBuilder.item(((Map<String, Object>) link).values().iterator().next().toString()); else {
linksBuilder = linkingTo()
.self(resourceLinks.installedPlugin()
.self(pluginInformation.getName()));
} }
PluginDto pluginDto = new PluginDto(linksBuilder.build()); PluginDto pluginDto = new PluginDto(linksBuilder.build());

View File

@@ -4,18 +4,23 @@ import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
import javax.ws.rs.Path; import javax.ws.rs.Path;
@Path("v2/") @Path("v2/plugins")
public class PluginRootResource { public class PluginRootResource {
private Provider<PluginResource> pluginResourceProvider; private Provider<InstalledPluginResource> installedPluginResourceProvider;
private Provider<AvailablePluginResource> availablePluginResourceProvider;
@Inject @Inject
public PluginRootResource(Provider<PluginResource> pluginResourceProvider) { public PluginRootResource(Provider<InstalledPluginResource> installedPluginResourceProvider, Provider<AvailablePluginResource> availablePluginResourceProvider) {
this.pluginResourceProvider = pluginResourceProvider; this.installedPluginResourceProvider = installedPluginResourceProvider;
this.availablePluginResourceProvider = availablePluginResourceProvider;
} }
@Path("plugins") @Path("/installed")
public PluginResource plugins() { public InstalledPluginResource installedPlugins() {
return pluginResourceProvider.get(); return installedPluginResourceProvider.get();
} }
@Path("/available")
public AvailablePluginResource availablePlugins() { return availablePluginResourceProvider.get(); }
} }

View File

@@ -651,35 +651,71 @@ class ResourceLinks {
} }
} }
public PluginLinks plugin() { public InstalledPluginLinks installedPlugin() {
return new PluginLinks(scmPathInfoStore.get()); return new InstalledPluginLinks(scmPathInfoStore.get());
} }
static class PluginLinks { static class InstalledPluginLinks {
private final LinkBuilder pluginLinkBuilder; private final LinkBuilder installedPluginLinkBuilder;
PluginLinks(ScmPathInfo pathInfo) { InstalledPluginLinks(ScmPathInfo pathInfo) {
pluginLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, PluginResource.class); installedPluginLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, InstalledPluginResource.class);
} }
String self(String id) { String self(String id) {
return pluginLinkBuilder.method("plugins").parameters().method("getInstalledPlugin").parameters(id).href(); return installedPluginLinkBuilder.method("installedPlugins").parameters().method("getInstalledPlugin").parameters(id).href();
} }
} }
public PluginCollectionLinks pluginCollection() { public InstalledPluginCollectionLinks installedPluginCollection() {
return new PluginCollectionLinks(scmPathInfoStore.get()); return new InstalledPluginCollectionLinks(scmPathInfoStore.get());
} }
static class PluginCollectionLinks { static class InstalledPluginCollectionLinks {
private final LinkBuilder pluginCollectionLinkBuilder; private final LinkBuilder installedPluginCollectionLinkBuilder;
PluginCollectionLinks(ScmPathInfo pathInfo) { InstalledPluginCollectionLinks(ScmPathInfo pathInfo) {
pluginCollectionLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, PluginResource.class); installedPluginCollectionLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, InstalledPluginResource.class);
} }
String self() { String self() {
return pluginCollectionLinkBuilder.method("plugins").parameters().method("getInstalledPlugins").parameters().href(); return installedPluginCollectionLinkBuilder.method("installedPlugins").parameters().method("getInstalledPlugins").parameters().href();
}
}
public AvailablePluginLinks availablePlugin() {
return new AvailablePluginLinks(scmPathInfoStore.get());
}
static class AvailablePluginLinks {
private final LinkBuilder availablePluginLinkBuilder;
AvailablePluginLinks(ScmPathInfo pathInfo) {
availablePluginLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, AvailablePluginResource.class);
}
String self(String name, String version) {
return availablePluginLinkBuilder.method("availablePlugins").parameters().method("getAvailablePlugin").parameters(name, version).href();
}
String install(String name, String version) {
return availablePluginLinkBuilder.method("availablePlugins").parameters().method("installPlugin").parameters(name, version).href();
}
}
public AvailablePluginCollectionLinks availablePluginCollection() {
return new AvailablePluginCollectionLinks(scmPathInfoStore.get());
}
static class AvailablePluginCollectionLinks {
private final LinkBuilder availablePluginCollectionLinkBuilder;
AvailablePluginCollectionLinks(ScmPathInfo pathInfo) {
availablePluginCollectionLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, AvailablePluginResource.class);
}
String self() {
return availablePluginCollectionLinkBuilder.method("availablePlugins").parameters().method("getAvailablePlugins").parameters().href();
} }
} }

View File

@@ -186,8 +186,6 @@ public class DefaultPluginManager implements PluginManager
PluginCenter center = getPluginCenter(); PluginCenter center = getPluginCenter();
// pluginHandler.install(id);
for (PluginInformation plugin : center.getPlugins()) for (PluginInformation plugin : center.getPlugins())
{ {
String pluginId = plugin.getId(); String pluginId = plugin.getId();
@@ -593,10 +591,10 @@ public class DefaultPluginManager implements PluginManager
*/ */
private PluginCenter getPluginCenter() private PluginCenter getPluginCenter()
{ {
PluginCenter center = null; // cache.get(PluginCenter.class.getName()); PluginCenter center = cache.get(PluginCenter.class.getName());
// if (center == null) if (center == null)
// { {
synchronized (DefaultPluginManager.class) synchronized (DefaultPluginManager.class)
{ {
String pluginUrl = configuration.getPluginUrl(); String pluginUrl = configuration.getPluginUrl();
@@ -636,7 +634,7 @@ public class DefaultPluginManager implements PluginManager
} }
} }
} }
// } }
return center; return center;
} }

View File

@@ -57,7 +57,7 @@ public final class PluginCenterDto implements Serializable {
private Dependency dependencies; private Dependency dependencies;
@XmlElement(name = "_links") @XmlElement(name = "_links")
private Map<String, Object> links; private Map<String, Link> links;
} }
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
@@ -77,18 +77,10 @@ public final class PluginCenterDto implements Serializable {
private String name; private String name;
} }
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "_links")
@Getter
static class Links {
private Link link;
private boolean templated;
}
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
@Getter @Getter
static class Link { static class Link {
private String url; private String href;
private boolean templated;
} }
} }

View File

@@ -24,10 +24,6 @@ public class PluginCenterDtoMapper {
pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), Collections.singletonList(condition.getOs()), condition.getArch())); pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), Collections.singletonList(condition.getOs()), condition.getArch()));
} }
if (plugin.getLinks() != null) {
pluginInformation.setLinks(plugin.getLinks());
}
pluginInformationSet.add(pluginInformation); pluginInformationSet.add(pluginInformation);
} }
return pluginInformationSet; return pluginInformationSet;

View File

@@ -36,8 +36,10 @@ public class ResourceLinksMock {
when(resourceLinks.modifications()).thenReturn(new ResourceLinks.ModificationsLinks(uriInfo)); when(resourceLinks.modifications()).thenReturn(new ResourceLinks.ModificationsLinks(uriInfo));
when(resourceLinks.repositoryType()).thenReturn(new ResourceLinks.RepositoryTypeLinks(uriInfo)); when(resourceLinks.repositoryType()).thenReturn(new ResourceLinks.RepositoryTypeLinks(uriInfo));
when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo)); when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo));
when(resourceLinks.pluginCollection()).thenReturn(new ResourceLinks.PluginCollectionLinks(uriInfo)); when(resourceLinks.installedPluginCollection()).thenReturn(new ResourceLinks.InstalledPluginCollectionLinks(uriInfo));
when(resourceLinks.plugin()).thenReturn(new ResourceLinks.PluginLinks(uriInfo)); when(resourceLinks.availablePluginCollection()).thenReturn(new ResourceLinks.AvailablePluginCollectionLinks(uriInfo));
when(resourceLinks.installedPlugin()).thenReturn(new ResourceLinks.InstalledPluginLinks(uriInfo));
when(resourceLinks.availablePlugin()).thenReturn(new ResourceLinks.AvailablePluginLinks(uriInfo));
when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo)); when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo));
when(resourceLinks.uiPlugin()).thenReturn(new ResourceLinks.UIPluginLinks(uriInfo)); when(resourceLinks.uiPlugin()).thenReturn(new ResourceLinks.UIPluginLinks(uriInfo));
when(resourceLinks.authentication()).thenReturn(new ResourceLinks.AuthenticationLinks(uriInfo)); when(resourceLinks.authentication()).thenReturn(new ResourceLinks.AuthenticationLinks(uriInfo));