Move new users resource to new package

This commit is contained in:
René Pfeuffer
2018-06-01 08:56:11 +02:00
parent c7e59a4fbd
commit 9ac4c6744a
14 changed files with 24 additions and 20 deletions

View File

@@ -558,7 +558,7 @@ public abstract class AbstractManagerResource<T extends ModelObject,
*
* @return
*/
Collection<T> fetchItems(String sortby, boolean desc, int start,
protected Collection<T> fetchItems(String sortby, boolean desc, int start,
int limit)
{
AssertUtil.assertPositive(start);

View File

@@ -1,17 +0,0 @@
package sonia.scm.api.rest.resources;
import lombok.Data;
import javax.xml.bind.annotation.XmlRootElement;
import java.net.URI;
@Data
@XmlRootElement
public class Link {
private URI href;
public Link(URI href) {
this.href = href;
}
}

View File

@@ -1,111 +0,0 @@
package sonia.scm.api.rest.resources;
import com.google.common.collect.ImmutableList;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.Arrays;
/**
* This class is used to create links for JAX-RS resources. Create a new instance specifying all resource classes used
* to process the request. Than for each of these classes call <code>builder.method(...).parameters(...)</code> for each
* of these classes consecutively. The builder itself is immutable, so that each instance is reusable and you get a new
* builder for each method.
*
* <pre>
* LinkBuilder builder = new LinkBuilder(uriInfo, MainResource.class, SubResource.class);
* Link link = builder
* .method("sub")
* .parameters("param")
* .method("x")
* .parameters("param_1", "param_2")
* .create();
* </pre>
*/
class LinkBuilder {
private final UriInfo uriInfo;
private final Class[] classes;
private final ImmutableList<Call> calls;
LinkBuilder(UriInfo uriInfo, Class... classes) {
this(uriInfo, classes, ImmutableList.of());
}
private LinkBuilder(UriInfo uriInfo, Class[] classes, ImmutableList<Call> calls) {
this.uriInfo = uriInfo;
this.classes = classes;
this.calls = calls;
}
public Parameters method(String method) {
if (calls.size() >= classes.length) {
throw new IllegalStateException("no more classes for methods");
}
return new Parameters(method);
}
public Link create() {
if (calls.size() < classes.length) {
throw new IllegalStateException("not enough methods for all classes");
}
URI baseUri = uriInfo.getBaseUri();
URI relativeUri = createRelativeUri();
URI absoluteUri = baseUri.resolve(relativeUri);
return new Link(absoluteUri);
}
private LinkBuilder add(String method, String[] parameters) {
return new LinkBuilder(uriInfo, classes, appendNewCall(method, parameters));
}
private ImmutableList<Call> appendNewCall(String method, String[] parameters) {
return ImmutableList.<Call> builder().addAll(calls).add(createNewCall(method, parameters)).build();
}
private Call createNewCall(String method, String[] parameters) {
return new Call(LinkBuilder.this.classes[calls.size()], method, parameters);
}
private URI createRelativeUri() {
UriBuilder uriBuilder = userUriBuilder();
calls.forEach(call -> uriBuilder.path(call.clazz, call.method));
String[] concatenatedParameters = calls
.stream()
.map(call -> call.parameters)
.flatMap(Arrays::stream)
.toArray(String[]::new);
return uriBuilder.build((Object[]) concatenatedParameters);
}
private UriBuilder userUriBuilder() {
return UriBuilder.fromResource(classes[0]);
}
class Parameters {
private final String method;
private Parameters(String method) {
this.method = method;
}
public LinkBuilder parameters(String... parameters) {
return LinkBuilder.this.add(method, parameters);
}
}
private static class Call {
private final Class clazz;
private final String method;
private final String[] parameters;
private Call(Class clazz, String method, String[] parameters) {
this.clazz = clazz;
this.method = method;
this.parameters = parameters;
}
}
}

View File

@@ -1,12 +0,0 @@
package sonia.scm.api.rest.resources;
import com.google.inject.AbstractModule;
import org.mapstruct.factory.Mappers;
public class MapperModule extends AbstractModule {
@Override
protected void configure() {
bind(UserDto2UserMapper.class).to(Mappers.getMapper(UserDto2UserMapper.class).getClass());
bind(User2UserDtoMapper.class).to(Mappers.getMapper(User2UserDtoMapper.class).getClass());
}
}

View File

@@ -1,38 +0,0 @@
package sonia.scm.api.rest.resources;
import org.apache.shiro.SecurityUtils;
import org.mapstruct.AfterMapping;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import sonia.scm.security.Role;
import sonia.scm.user.User;
import javax.ws.rs.core.UriInfo;
import java.util.HashMap;
import java.util.Map;
@Mapper
public abstract class User2UserDtoMapper {
public abstract UserDto userToUserDto(User user, @Context UriInfo uriInfo);
@AfterMapping
void removePassword(@MappingTarget UserDto target) {
target.setPassword(UserResource.DUMMY_PASSWORT);
}
@AfterMapping
void appendLinks(@MappingTarget UserDto target, @Context UriInfo uriInfo) {
LinkBuilder userLinkBuilder = new LinkBuilder(uriInfo, UserNewResource.class, UserSubResource.class);
LinkBuilder collectionLinkBuilder = new LinkBuilder(uriInfo, UserNewResource.class, UserCollectionResource.class);
Map<String, Link> links = new HashMap<>();
links.put("self", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("get").parameters().create());
if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) {
links.put("delete", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("delete").parameters().create());
links.put("update", userLinkBuilder.method("getUserSubResource").parameters(target.getName()).method("update").parameters().create());
links.put("create", collectionLinkBuilder.method("getUserCollectionResource").parameters().method("create").parameters().create());
}
target.setLinks(links);
}
}

View File

@@ -1,94 +0,0 @@
package sonia.scm.api.rest.resources;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@Singleton
public class UserCollectionResource extends AbstractManagerResource<User, UserException> {
private final UserDto2UserMapper dtoToUserMapper;
private final User2UserDtoMapper userToDtoMapper;
@Inject
public UserCollectionResource(UserManager manager, UserDto2UserMapper dtoToUserMapper, User2UserDtoMapper userToDtoMapper) {
super(manager);
this.dtoToUserMapper = dtoToUserMapper;
this.userToDtoMapper = userToDtoMapper;
}
/**
* Returns all users. <strong>Note:</strong> This method requires admin privileges.
*
* @param request the current request
* @param start the start value for paging
* @param limit the limit value for paging
* @param sortby sort parameter
* @param desc sort direction desc or aesc
* @return
*/
@GET
@Path("")
@TypeHint(User[].class)
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"),
@ResponseCode(code = 500, condition = "internal server error")
})
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response getAll(@Context Request request, @Context UriInfo uriInfo, @DefaultValue("0")
@QueryParam("start") int start, @DefaultValue("-1")
@QueryParam("limit") int limit, @QueryParam("sortby") String sortby,
@DefaultValue("false")
@QueryParam("desc") boolean desc) {
Collection<User> items = fetchItems(sortby, desc, start, limit);
List<UserDto> collect = items.stream().map(user -> userToDtoMapper.userToUserDto(user, uriInfo)).collect(Collectors.toList());
return Response.ok(new GenericEntity<Collection<UserDto>>(collect) {}).build();
}
@POST
@Path("")
@StatusCodes({
@ResponseCode(code = 201, condition = "create success", additionalHeaders = {
@ResponseHeader(name = "Location", description = "uri to the created group")
}),
@ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(TypeHint.NO_CONTENT.class)
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response create(@Context UriInfo uriInfo, UserDto userDto) throws IOException, UserException {
User user = dtoToUserMapper.userDtoToUser(userDto, "");
manager.create(user);
LinkBuilder builder = new LinkBuilder(uriInfo, UserNewResource.class, UserSubResource.class);
return Response.created(builder.method("getUserSubResource").parameters(user.getName()).method("get").parameters().create().getHref()).build();
}
@Override
protected GenericEntity<Collection<User>> createGenericEntity(Collection<User> items) {
throw new UnsupportedOperationException();
}
@Override
protected String getId(User item) {
return item.getName();
}
@Override
protected String getPathPart() {
throw new UnsupportedOperationException();
}
}

View File

@@ -1,22 +0,0 @@
package sonia.scm.api.rest.resources;
import lombok.Data;
import javax.xml.bind.annotation.XmlElement;
import java.util.Map;
@Data
public class UserDto {
private boolean active;
private boolean admin;
private Long creationDate;
private String displayName;
private Long lastModified;
private String mail;
private String name;
private String password;
private String type;
@XmlElement(name = "_links")
private Map<String, Link> links;
}

View File

@@ -1,35 +0,0 @@
package sonia.scm.api.rest.resources;
import com.google.inject.Inject;
import org.apache.shiro.authc.credential.PasswordService;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import sonia.scm.user.User;
import static sonia.scm.api.rest.resources.UserResource.DUMMY_PASSWORT;
@Mapper
public abstract class UserDto2UserMapper {
@Inject
private PasswordService passwordService;
@Mapping(source = "password", target = "password", qualifiedByName = "encrypt")
public abstract User userDtoToUser(UserDto userDto, @Context String originalPassword);
@Named("encrypt")
String encrypt(String password, @Context String originalPassword) {
if (DUMMY_PASSWORT.equals(password))
{
return originalPassword;
}
else
{
return passwordService.encryptPassword(password);
}
}
}

View File

@@ -1,30 +0,0 @@
package sonia.scm.api.rest.resources;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import javax.ws.rs.Path;
@Singleton
@Path("usersnew")
public class UserNewResource {
private final UserCollectionResource userCollectionResource;
private final UserSubResource userSubResource;
@Inject
public UserNewResource(UserCollectionResource userCollectionResource, UserSubResource userSubResource) {
this.userCollectionResource = userCollectionResource;
this.userSubResource = userSubResource;
}
@Path("")
public UserCollectionResource getUserCollectionResource() {
return userCollectionResource;
}
@Path("{id}")
public UserSubResource getUserSubResource() {
return userSubResource;
}
}

View File

@@ -1,96 +0,0 @@
package sonia.scm.api.rest.resources;
import com.google.inject.Inject;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import com.webcohesion.enunciate.metadata.rs.TypeHint;
import org.apache.shiro.SecurityUtils;
import sonia.scm.security.Role;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.util.Collection;
public class UserSubResource extends AbstractManagerResource<User, UserException> {
private final UserDto2UserMapper dtoToUserMapper;
private final User2UserDtoMapper userToDtoMapper;
@Inject
public UserSubResource(UserDto2UserMapper dtoToUserMapper, User2UserDtoMapper userToDtoMapper, UserManager manager) {
super(manager);
this.dtoToUserMapper = dtoToUserMapper;
this.userToDtoMapper = userToDtoMapper;
}
@GET
@Path("")
@TypeHint(UserDto.class)
@StatusCodes({
@ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"),
@ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"),
@ResponseCode(code = 500, condition = "internal server error")
})
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response get(@Context Request request, @Context UriInfo uriInfo, @PathParam("id") String id)
{
if (SecurityUtils.getSubject().hasRole(Role.ADMIN))
{
User user = manager.get(id);
UserDto userDto = userToDtoMapper.userToUserDto(user, uriInfo);
return Response.ok(userDto).build();
}
else
{
return Response.status(Response.Status.FORBIDDEN).build();
}
}
@PUT
@Path("")
@StatusCodes({
@ResponseCode(code = 204, condition = "update success"),
@ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(TypeHint.NO_CONTENT.class)
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response update(@Context UriInfo uriInfo,
@PathParam("id") String name, UserDto userDto)
{
String originalPassword = manager.get(name).getPassword();
User user = dtoToUserMapper.userDtoToUser(userDto, originalPassword);
return update(name, user);
}
@DELETE
@Path("")
@StatusCodes({
@ResponseCode(code = 204, condition = "delete success"),
@ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"),
@ResponseCode(code = 500, condition = "internal server error")
})
@TypeHint(TypeHint.NO_CONTENT.class)
public Response delete(@PathParam("id") String name)
{
return super.delete(name);
}
@Override
protected GenericEntity<Collection<User>> createGenericEntity(Collection<User> items) {
throw new UnsupportedOperationException();
}
@Override
protected String getId(User item) {
return item.getName();
}
@Override
protected String getPathPart() {
throw new UnsupportedOperationException();
}
}