RESTful API for Categories

Former-commit-id: 3b6f069895
restapi
Jens Pelzetter 2020-06-07 11:15:48 +02:00
parent 813ff58daa
commit 264736422f
6 changed files with 362 additions and 66 deletions

View File

@ -21,10 +21,14 @@ package org.libreccm.api.admin.categorization;
import org.libreccm.api.admin.categorization.dto.CategorizationData;
import org.libreccm.api.admin.categorization.dto.CategoryData;
import org.libreccm.api.dto.ListView;
import org.libreccm.categorization.Categorization;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryManager;
import org.libreccm.categorization.CategoryRepository;
import org.libreccm.categorization.Domain;
import org.libreccm.categorization.ObjectNotAssignedToCategoryException;
import org.libreccm.core.CcmObject;
import org.libreccm.core.CcmObjectRepository;
import org.libreccm.core.CoreConstants;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.RequiresPrivilege;
@ -71,6 +75,9 @@ public class CategoriesApi {
@Inject
private CategorizationApiRepository repository;
@Inject
private CcmObjectRepository ccmObjectRepository;
@GET
@Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}")
@Produces(MediaType.APPLICATION_JSON)
@ -496,10 +503,23 @@ public class CategoriesApi {
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public ListView<CategorizationData> getObjectsInCategory(
@PathParam("domainIdentifier") final String domainIdentifierParam,
@PathParam("path") final String categoryPathTokens
@PathParam("domainIdentifier") final String domainIdentifier,
@PathParam("path") final String categoryPath,
@QueryParam("limit") final int limit,
@QueryParam("offset") final int offset
) {
throw new UnsupportedOperationException();
final Domain domain = repository.findDomain(domainIdentifier);
final Category category = findCategory(domain, categoryPath);
return new ListView<>(
categoryRepository
.findObjectsInCategoryAsStream(category, limit, offset)
.map(CategorizationData::new)
.collect(Collectors.toList()),
categoryRepository.countObjectsInCategory(category),
limit,
offset
);
}
@GET
@ -509,88 +529,253 @@ public class CategoriesApi {
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public ListView<CategorizationData> getCategoryObjectsInCategory(
@PathParam("categoryId") final long categoryId
@PathParam("categoryId") final long categoryId,
@QueryParam("limit") final int limit,
@QueryParam("offset") final int offset
) {
throw new UnsupportedOperationException();
final Category category = findCategory(categoryId);
return new ListView<>(
categoryRepository
.findObjectsInCategoryAsStream(category, limit, offset)
.map(CategorizationData::new)
.collect(Collectors.toList()),
categoryRepository.countObjectsInCategory(category),
limit,
offset
);
}
@GET
@Path("/UUID-{categoryId}/objects")
@Path("/UUID-{uuid}/objects")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public ListView<CategorizationData> getObjectsInCategory(
@PathParam("categoryId") final String categoryUuid
@PathParam("uuid") final String uuid,
@QueryParam("limit") final int limit,
@QueryParam("offset") final int offset
) {
throw new UnsupportedOperationException();
final Category category = findCategory(uuid);
return new ListView<>(
categoryRepository
.findObjectsInCategoryAsStream(category, limit, offset)
.map(CategorizationData::new)
.collect(Collectors.toList()),
categoryRepository.countObjectsInCategory(category),
limit,
offset
);
}
@PUT
@Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/objects/{objectIdentifier}")
@Produces(MediaType.APPLICATION_JSON)
@POST
@Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/objects/")
@Consumes(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public CategoryData addObjectToCategory(
@PathParam("domainIdentifier") final String domainIdentifierParam,
@PathParam("path") final String categoryPathTokens,
@PathParam("objectIdentifier") final String objectIdentifierParam
public Response addObjectToCategory(
@PathParam("domainIdentifier") final String domainIdentifier,
@PathParam("path") final String categoryPath,
final CategorizationData categorizationData
) {
throw new UnsupportedOperationException();
if (categorizationData == null) {
throw new WebApplicationException(
"Missing CategorizationData.", Response.Status.BAD_REQUEST
);
}
final Domain domain = repository.findDomain(domainIdentifier);
final Category category = findCategory(domain, categoryPath);
final CcmObject object = findObject(categorizationData.getUuid());
final Categorization categorization;
if (categorizationData.getType() == null) {
categorization = categoryManager.addObjectToCategory(
object, category
);
} else {
categorization = categoryManager.addObjectToCategory(
object, category, categorizationData.getType()
);
}
@PUT
return Response.created(
uriInfo
.getBaseUriBuilder()
.path(domain.getDomainKey())
.path(categoryPath)
.path("objects")
.path(categorization.getUuid())
.build()
).build();
}
@POST
@Path("/ID-{categoryId}/objects/{objectIdentifier}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public CategoryData addObjectToCategory(
public Response addObjectToCategory(
@PathParam("categoryId") final long categoryId,
@PathParam("objectIdentifier") final String objectIdentifier
final CategorizationData categorizationData
) {
throw new UnsupportedOperationException();
if (categorizationData == null) {
throw new WebApplicationException(
"Missing CategorizationData.", Response.Status.BAD_REQUEST
);
}
@PUT
@Path("/UUID-{categoryId}/objects/objectIdentifier")
@Produces(MediaType.APPLICATION_JSON)
final Category category = findCategory(categoryId);
final CcmObject object = findObject(categorizationData.getUuid());
final Categorization categorization;
if (categorizationData.getType() == null) {
categorization = categoryManager.addObjectToCategory(
object, category
);
} else {
categorization = categoryManager.addObjectToCategory(
object, category, categorizationData.getType()
);
}
return Response.created(
uriInfo
.getBaseUriBuilder()
.path(String.format("ID-%d", category.getObjectId()))
.path("objects")
.path(categorization.getUuid())
.build()
).build();
}
@POST
@Path("/UUID-{categoryUuid}/objects/objectIdentifier")
@Consumes(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public CategoryData addObjectsToCategory(
@PathParam("categoryId") final String categoryUuid,
@PathParam("objectIdentifier") final String objectIdentifier
public Response addObjectsToCategory(
@PathParam("categoryUuid") final String uuid,
final CategorizationData categorizationData
) {
throw new UnsupportedOperationException();
if (categorizationData == null) {
throw new WebApplicationException(
"Missing CategorizationData.", Response.Status.BAD_REQUEST
);
}
final Category category = findCategory(uuid);
final CcmObject object = findObject(categorizationData.getUuid());
final Categorization categorization;
if (categorizationData.getType() == null) {
categorization = categoryManager.addObjectToCategory(
object, category
);
} else {
categorization = categoryManager.addObjectToCategory(
object, category, categorizationData.getType()
);
}
return Response.created(
uriInfo
.getBaseUriBuilder()
.path(String.format("UUID-%s", category.getUuid()))
.path("objects")
.path(categorization.getUuid())
.build()
).build();
}
@DELETE
@Path("/{domainIdentifier}/{path:^[\\w\\-/]+$}/objects/{objectIdentifier}")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public CategoryData removeObjectFromCategory(
@PathParam("domainIdentifier") final String domainIdentifierParam,
@PathParam("path") final String categoryPathTokens,
@PathParam("objectIdentifier") final String objectIdentifierParam
public Response removeObjectFromCategory(
@PathParam("domainIdentifier") final String domainIdentifier,
@PathParam("path") final String categoryPath,
@PathParam("objectIdentifier") final String categorizationUuid
) {
throw new UnsupportedOperationException();
final Domain domain = repository.findDomain(domainIdentifier);
final Category category = findCategory(domain, categoryPath);
final Categorization categorization = categoryRepository
.findObjectInCategory(category, categorizationUuid)
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No Categorization %s in Category %s of Domain %s.",
categorizationUuid,
categoryPath,
domain.getDomainKey()
),
Response.Status.NOT_FOUND
)
);
try {
categoryManager.removeObjectFromCategory(
categorization.getOwner(), category
);
return Response.ok(
String.format(
"Categorization %s of Category %s of Domain %s "
+ "deleted successfully.",
categorization.getUuid(),
categoryPath,
domain.getDomainKey()
)
).build();
} catch (ObjectNotAssignedToCategoryException ex) {
throw new WebApplicationException(
Response.Status.INTERNAL_SERVER_ERROR
);
}
}
@DELETE
@Path("/ID-{categoryId}/objects/{objectIdentifier}")
@Produces(MediaType.APPLICATION_JSON)
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public CategoryData removeObjectFromCategory(
public Response removeObjectFromCategory(
@PathParam("categoryId") final long categoryId,
@PathParam("objectIdentifier") final String objectIdentifier
@PathParam("objectIdentifier") final String categorizationUuid
) {
throw new UnsupportedOperationException();
final Category category = findCategory(categoryId);
final Categorization categorization = categoryRepository
.findObjectInCategory(category, categorizationUuid)
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No Categorization %s in Category %d.",
categorizationUuid, categoryId
),
Response.Status.NOT_FOUND
)
);
try {
categoryManager.removeObjectFromCategory(
categorization.getOwner(), category
);
return Response.ok(
String.format(
"Categorization %s of Category %d deleted successfully.",
categorization.getUuid(),
categoryId
)
).build();
} catch (ObjectNotAssignedToCategoryException ex) {
throw new WebApplicationException(
Response.Status.INTERNAL_SERVER_ERROR
);
}
}
@DELETE
@ -599,11 +784,39 @@ public class CategoriesApi {
@AuthorizationRequired
@RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
@Transactional(Transactional.TxType.REQUIRED)
public CategoryData removeObjectsFromCategory(
public Response removeObjectsFromCategory(
@PathParam("categoryId") final String categoryUuid,
@PathParam("objectIdentifier") final String objectIdentifier
@PathParam("objectIdentifier") final String categorizationUuid
) {
throw new UnsupportedOperationException();
final Category category = findCategory(categoryUuid);
final Categorization categorization = categoryRepository
.findObjectInCategory(category, categorizationUuid)
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No Categorization %s in Category %s.",
categorizationUuid, categorizationUuid
),
Response.Status.NOT_FOUND
)
);
try {
categoryManager.removeObjectFromCategory(
categorization.getOwner(), category
);
return Response.ok(
String.format(
"Categorization %s of Category %s deleted successfully.",
categorization.getUuid(),
categorizationUuid
)
).build();
} catch (ObjectNotAssignedToCategoryException ex) {
throw new WebApplicationException(
Response.Status.INTERNAL_SERVER_ERROR
);
}
}
private Category findCategory(final Domain domain, final String path) {
@ -647,4 +860,22 @@ public class CategoriesApi {
);
}
private CcmObject findObject(final String uuid) {
if (uuid == null) {
throw new WebApplicationException(
"No UUID for object", Response.Status.BAD_REQUEST
);
}
return ccmObjectRepository
.findObjectByUuid(uuid)
.orElseThrow(
() -> new WebApplicationException(
String.format(
"No CcmObject with UUID %s found.", uuid
)
)
);
}
}

View File

@ -20,7 +20,6 @@ package org.libreccm.api.admin.categorization.dto;
import org.libreccm.api.core.dto.CcmObjectId;
import org.libreccm.categorization.Categorization;
import org.libreccm.categorization.Category;
import java.util.Objects;

View File

@ -62,36 +62,37 @@ import javax.persistence.Table;
name = "Categorization.find",
query = "SELECT c FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.categorizedObject = :object")
,
+ "AND c.categorizedObject = :object"),
@NamedQuery(
name = "Categorization.findForCategory",
query = "SELECT c FROM Categorization c "
+ "WHERE c.uuid = :uuid "
+ "AND c.category = :category"
),
@NamedQuery(
name = "Categorization.isAssignedTo",
query = "SELECT (CASE WHEN COUNT(c) > 0 THEN true ELSE false END) "
+ "FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.categorizedObject = :object")
,
+ "AND c.categorizedObject = :object"),
@NamedQuery(
name = "Categorization.isAssignedToWithType",
query = "SELECT (CASE WHEN COUNT(c) > 0 THEN true ELSE false END) "
+ "FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.categorizedObject = :object "
+ "AND c.type = :type")
,
+ "AND c.type = :type"),
@NamedQuery(
name = "Categorization.findIndexObject",
query = "SELECT c.categorizedObject FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.indexObject = TRUE")
,
+ "AND c.indexObject = TRUE"),
@NamedQuery(
name = "Categorization.findIndexObjectCategorization",
query = "SELECT c FROM Categorization c "
+ "WHERE c.category = :category "
+ "AND c.indexObject = TRUE"
)
,
),
@NamedQuery(
name = "Categorization.hasIndexObject",
query = "SELECT (CASE WHEN COUNT(c.categorizedObject) > 0 THEN true "

View File

@ -95,6 +95,12 @@ import javax.xml.bind.annotation.XmlRootElement;
query = "SELECT (CASE WHEN COUNT(c) > 0 THEN true ELSE false END) "
+ "FROM Categorization c "
+ "WHERE c.categorizedObject = :object"),
@NamedQuery(
name = "Category.findObjects",
query = "SELECT c FROM Categorization c "
+ "WHERE c.category = :category "
+ "ORDER BY c.objectOrder"
),
@NamedQuery(
name = "Category.countObjects",
query = "SELECT COUNT(c) FROM Categorization c "

View File

@ -76,12 +76,12 @@ public class CategoryManager implements Serializable {
private PermissionChecker permissionChecker;
/**
* Assigns an category to an object.
*
* Please note: Because the association between {@link Category} and {@code
* Assigns an category to an object.Please note: Because the association
* between {@link Category} and {@code
* CcmObject} is a many-to-many association we use an association object to
* store the additional attributes of the association. The
* {@link Categorization} entity is completely managed by this class.
* store the additional attributes of the association.
*
* The {@link Categorization} entity is completely managed by this class.
*
* If either {@code object} or the {@code category} parameter are
* {@code null} an {@link IllegalArgumentException} is thrown because
@ -91,15 +91,18 @@ public class CategoryManager implements Serializable {
* {@code null}.
* @param category The category to which the object should be assigned. Can
* never be {@code null}.
*
* @return The new {@link Categorization} object for the category and the
* object.
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public void addObjectToCategory(
public Categorization addObjectToCategory(
final CcmObject object,
@RequiresPrivilege(PRIVILEGE_MANAGE_CATEGORY_OBJECTS)
final Category category) {
addObjectToCategory(object, category, null);
return addObjectToCategory(object, category, null);
}
/**
@ -119,10 +122,13 @@ public class CategoryManager implements Serializable {
* @param category The category to which the object should be assigned. Can
* never be {@code null}.
* @param type Type of the categorisation.
*
* @return The new {@link Categorization} object for the category and the
* object.
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public void addObjectToCategory(
public Categorization addObjectToCategory(
final CcmObject object,
@RequiresPrivilege(PRIVILEGE_MANAGE_CATEGORY_OBJECTS)
final Category category,
@ -161,6 +167,8 @@ public class CategoryManager implements Serializable {
shiro.getSystemUser().execute(() -> {
entityManager.persist(categorization);
});
return categorization;
}
/**

View File

@ -243,6 +243,57 @@ public class CategoryRepository extends AbstractEntityRepository<Long, Category>
).getSingleResult();
}
@Transactional(Transactional.TxType.REQUIRED)
public List<Categorization> findObjectsInCategory(
final Category category,
final int limit,
final int offset
) {
return getEntityManager()
.createNamedQuery("Category.findObjects", Categorization.class)
.setParameter("category", category)
.setMaxResults(limit)
.setFirstResult(offset)
.getResultList();
}
@Transactional(Transactional.TxType.REQUIRED)
public Stream<Categorization> findObjectsInCategoryAsStream(
final Category category,
final int limit,
final int offset
) {
return getEntityManager()
.createNamedQuery("Category.findObjects", Categorization.class)
.setParameter("category", category)
.setMaxResults(limit)
.setFirstResult(offset)
.getResultStream();
}
@Transactional(Transactional.TxType.REQUIRED)
public long countObjectsInCategory(final Category category) {
return getEntityManager()
.createNamedQuery("Category.countObjects", Long.class)
.setParameter("category", category)
.getSingleResult();
}
@Transactional(Transactional.TxType.REQUIRED)
public Optional<Categorization> findObjectInCategory(
final Category category, final String categorizationUuid
) {
return getEntityManager()
.createNamedQuery(
"Categorization.findForCategory",
Categorization.class
)
.setParameter("uuid", categorizationUuid)
.setParameter("category", category)
.getResultStream()
.findFirst();
}
private boolean filterCategoryByName(final Category category,
final String name) {
LOGGER.debug("#findByPath(Domain, String): c = {}",