use hateoas style resource for ui plugins

This commit is contained in:
Sebastian Sdorra
2018-08-27 12:59:26 +02:00
parent 4f775fe7ca
commit 56b629fa9d
15 changed files with 414 additions and 42 deletions

View File

@@ -26,6 +26,10 @@ public class MapperModule extends AbstractModule {
bind(BranchToBranchDtoMapper.class).to(Mappers.getMapper(BranchToBranchDtoMapper.class).getClass());
// no mapstruct required
bind(UIPluginDtoMapper.class);
bind(UIPluginDtoCollectionMapper.class);
bind(UriInfoStore.class).in(ServletScopes.REQUEST);
}
}

View File

@@ -314,4 +314,37 @@ class ResourceLinks {
return permissionLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("permissions").parameters().method("getPermissionCollectionResource").parameters().method("getAll").parameters().href();
}
}
public UIPluginLinks uiPlugin() {
return new UIPluginLinks(uriInfoStore.get());
}
static class UIPluginLinks {
private final LinkBuilder uiPluginLinkBuilder;
UIPluginLinks(UriInfo uriInfo) {
uiPluginLinkBuilder = new LinkBuilder(uriInfo, UIRootResource.class, UIPluginResource.class);
}
String self(String id) {
return uiPluginLinkBuilder.method("plugins").parameters().method("getInstalledPlugin").parameters(id).href();
}
}
public UIPluginCollectionLinks uiPluginCollection() {
return new UIPluginCollectionLinks(uriInfoStore.get());
}
static class UIPluginCollectionLinks {
private final LinkBuilder uiPluginCollectionLinkBuilder;
UIPluginCollectionLinks(UriInfo uriInfo) {
uiPluginCollectionLinkBuilder = new LinkBuilder(uriInfo, UIRootResource.class, UIPluginResource.class);
}
String self() {
return uiPluginCollectionLinkBuilder.method("plugins").parameters().method("getInstalledPlugins").parameters().href();
}
}
}

View File

@@ -0,0 +1,46 @@
package sonia.scm.api.v2.resources;
import com.google.inject.Inject;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.HalRepresentation;
import de.otto.edison.hal.Links;
import sonia.scm.plugin.PluginWrapper;
import java.util.Collection;
import java.util.List;
import static de.otto.edison.hal.Embedded.embeddedBuilder;
import static de.otto.edison.hal.Links.linkingTo;
import static java.util.stream.Collectors.toList;
public class UIPluginDtoCollectionMapper {
private final ResourceLinks resourceLinks;
private final UIPluginDtoMapper mapper;
@Inject
public UIPluginDtoCollectionMapper(ResourceLinks resourceLinks, UIPluginDtoMapper mapper) {
this.resourceLinks = resourceLinks;
this.mapper = mapper;
}
public HalRepresentation map(Collection<PluginWrapper> plugins) {
List<UIPluginDto> dtos = plugins.stream().map(mapper::map).collect(toList());
return new HalRepresentation(createLinks(), embedDtos(dtos));
}
private Links createLinks() {
String baseUrl = resourceLinks.uiPluginCollection().self();
Links.Builder linksBuilder = linkingTo()
.with(Links.linkingTo().self(baseUrl).build());
return linksBuilder.build();
}
private Embedded embedDtos(List<UIPluginDto> dtos) {
return embeddedBuilder()
.with("plugins", dtos)
.build();
}
}

View File

@@ -0,0 +1,34 @@
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.Links;
import sonia.scm.plugin.PluginWrapper;
import javax.inject.Inject;
import static de.otto.edison.hal.Links.linkingTo;
public class UIPluginDtoMapper {
private ResourceLinks resourceLinks;
@Inject
public UIPluginDtoMapper(ResourceLinks resourceLinks) {
this.resourceLinks = resourceLinks;
}
public UIPluginDto map(PluginWrapper plugin) {
UIPluginDto dto = new UIPluginDto(
plugin.getPlugin().getInformation().getName(),
plugin.getPlugin().getResources().getScriptResources()
);
Links.Builder linksBuilder = linkingTo()
.self(resourceLinks.uiPlugin()
.self(plugin.getId()));
dto.add(linksBuilder.build());
return dto;
}
}

View File

@@ -0,0 +1,91 @@
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 de.otto.edison.hal.HalRepresentation;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.PluginWrapper;
import sonia.scm.web.VndMediaType;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class UIPluginResource {
private final PluginLoader pluginLoader;
private final UIPluginDtoCollectionMapper collectionMapper;
private final UIPluginDtoMapper mapper;
@Inject
public UIPluginResource(PluginLoader pluginLoader, UIPluginDtoCollectionMapper collectionMapper, UIPluginDtoMapper mapper) {
this.pluginLoader = pluginLoader;
this.collectionMapper = collectionMapper;
this.mapper = mapper;
}
/**
* Returns a collection of installed plugins and their ui bundles.
*
* @return collection of installed plugins.
*/
@GET
@Path("")
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(CollectionDto.class)
@Produces(VndMediaType.UI_PLUGIN_COLLECTION)
public Response getInstalledPlugins() {
List<PluginWrapper> plugins = pluginLoader.getInstalledPlugins()
.stream()
.filter(this::filter)
.collect(Collectors.toList());
return Response.ok(collectionMapper.map(plugins)).build();
}
/**
* Returns the installed plugin with the given id.
*
* @param id id of plugin
*
* @return installed plugin with specified id
*/
@GET
@Path("{id}")
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 404, condition = "not found"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(UIPluginDto.class)
@Produces(VndMediaType.UI_PLUGIN)
public Response getInstalledPlugin(@PathParam("id") String id) {
Optional<UIPluginDto> uiPluginDto = pluginLoader.getInstalledPlugins()
.stream()
.filter(this::filter)
.filter(plugin -> id.equals(plugin.getId()))
.map(mapper::map)
.findFirst();
if (uiPluginDto.isPresent()) {
return Response.ok(uiPluginDto.get()).build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
private boolean filter(PluginWrapper plugin) {
return plugin.getPlugin().getResources() != null;
}
}

View File

@@ -1,46 +1,22 @@
package sonia.scm.api.v2.resources;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.plugin.PluginWrapper;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.inject.Provider;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;
import java.util.stream.Collectors;
@Path("v2/ui")
public class UIRootResource {
private final PluginLoader pluginLoader;
private Provider<UIPluginResource> uiPluginResourceProvider;
@Inject
public UIRootResource(PluginLoader pluginLoader) {
this.pluginLoader = pluginLoader;
public UIRootResource(Provider<UIPluginResource> uiPluginResourceProvider) {
this.uiPluginResourceProvider = uiPluginResourceProvider;
}
@GET
@Path("plugins")
@Produces(MediaType.APPLICATION_JSON)
public List<UIPluginDto> getInstalledPlugins() {
return pluginLoader.getInstalledPlugins()
.stream()
.filter(this::filter)
.map(this::map)
.collect(Collectors.toList());
}
private boolean filter(PluginWrapper plugin) {
return plugin.getPlugin().getResources() != null;
}
private UIPluginDto map(PluginWrapper plugin) {
return new UIPluginDto(
plugin.getPlugin().getInformation().getName(),
plugin.getPlugin().getResources().getScriptResources()
);
public UIPluginResource plugins() {
return uiPluginResourceProvider.get();
}
}