Commit 9da179ed authored by Valentin Suhnjov's avatar Valentin Suhnjov

Merge branch 'release/v0.15.0' into master

parents 940c3bd2 94b70202
......@@ -5,7 +5,7 @@
<groupId>ee.eesti.riha</groupId>
<artifactId>rest</artifactId>
<version>0.14.2</version>
<version>0.15.0</version>
<packaging>war</packaging>
......
......@@ -81,9 +81,27 @@ DROP VIEW IF EXISTS riha.comment_type_issue_view;
CREATE OR REPLACE VIEW riha.comment_type_issue_view AS
SELECT
issue.*,
infosystem.json_content ->> 'short_name' AS infosystem_short_name
infosystem.json_content ->> 'short_name' infosystem_short_name,
infosystem.json_content ->> 'name' infosystem_full_name,
array_to_json(array_agg(event ORDER BY event.comment_id) FILTER (WHERE event.type = 'ISSUE_EVENT')) events,
row_to_json(last_comment) last_comment
FROM riha.comment issue
INNER JOIN riha.main_resource_view infosystem
ON (infosystem.json_content ->> 'uuid') = issue.infosystem_uuid :: TEXT
LEFT JOIN riha.comment event
ON issue.comment_id = event.comment_parent_id
LEFT JOIN (SELECT DISTINCT ON (comment_parent_id)
comment_id,
comment_parent_id,
creation_date,
author_name,
author_personal_code,
organization_name,
organization_code
FROM riha.comment
WHERE type = 'ISSUE_COMMENT'
ORDER BY comment_parent_id, creation_date DESC) last_comment
ON issue.comment_id = last_comment.comment_parent_id
WHERE issue.type = 'ISSUE'
GROUP BY issue.comment_id, infosystem_short_name, infosystem_full_name, last_comment.*
ORDER BY issue.comment_id;
\ No newline at end of file
--DROP VIEW riha.comment_type_issue_view;
CREATE OR REPLACE VIEW riha.comment_type_issue_view AS
SELECT
issue.*,
infosystem.json_content ->> 'short_name' infosystem_short_name,
infosystem.json_content ->> 'name' infosystem_full_name,
array_to_json(array_agg(event ORDER BY event.comment_id) FILTER (WHERE event.type = 'ISSUE_EVENT')) events
FROM riha.comment issue
INNER JOIN riha.main_resource_view infosystem
ON (infosystem.json_content ->> 'uuid') = issue.infosystem_uuid :: TEXT
LEFT JOIN riha.comment event
ON issue.comment_id = event.comment_parent_id
WHERE issue.type = 'ISSUE'
GROUP BY issue.comment_id, infosystem_short_name, infosystem_full_name
ORDER BY issue.comment_id;
\ No newline at end of file
--DROP VIEW riha.comment_type_issue_view;
CREATE OR REPLACE VIEW riha.comment_type_issue_view AS
SELECT
issue.*,
infosystem.json_content ->> 'short_name' infosystem_short_name,
infosystem.json_content ->> 'name' infosystem_full_name,
array_to_json(array_agg(event ORDER BY event.comment_id) FILTER (WHERE event.type = 'ISSUE_EVENT')) events,
row_to_json(last_comment) last_comment
FROM riha.comment issue
INNER JOIN riha.main_resource_view infosystem
ON (infosystem.json_content ->> 'uuid') = issue.infosystem_uuid :: TEXT
LEFT JOIN riha.comment event
ON issue.comment_id = event.comment_parent_id
LEFT JOIN (SELECT DISTINCT ON (comment_parent_id)
comment_id,
comment_parent_id,
creation_date,
author_name,
author_personal_code,
organization_name,
organization_code
FROM riha.comment
WHERE type = 'ISSUE_COMMENT'
ORDER BY comment_parent_id, creation_date DESC) last_comment
ON issue.comment_id = last_comment.comment_parent_id
WHERE issue.type = 'ISSUE'
GROUP BY issue.comment_id, infosystem_short_name, infosystem_full_name, last_comment.*
ORDER BY issue.comment_id;
\ No newline at end of file
package ee.eesti.riha.rest.dao;
import ee.eesti.riha.rest.dao.grid.CommentGrid;
import ee.eesti.riha.rest.util.PagedRequest;
import ee.eesti.riha.rest.util.PagedResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* Performs various persistence related operations on Comment entity.
*/
@Transactional
@Repository
public class CommentDAO {
@Autowired
private CommentGrid commentGrid;
public PagedResponse list(PagedRequest request) {
return commentGrid.query(request);
}
}
package ee.eesti.riha.rest.dao.grid;
import ee.eesti.riha.rest.model.Comment;
import ee.eesti.riha.rest.model.CommentTypeIssueViewModel;
import ee.eesti.riha.rest.model.readonly.Comment_type_issue_view;
import ee.eesti.riha.rest.util.FilterParameter;
import ee.eesti.riha.rest.util.PagedRequest;
import org.hibernate.criterion.*;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* Query grid to work specifically with entity of {@link Comment} type. Provides ways to query top level comments and
* additional customized actions. <p>Actions: <ul><li>{@link #ACTION_AUTHOR_OR_ORGANIZATION_CODE} will modify query and
* restrict {@link #PROPERTY_AUTHOR_PERSONAL_CODE} and {@link #PROPERTY_ORGANIZATION_CODE} in disjunction on both main
* query and will additionally query child comment entities for same disjunction existence</li></ul></p>
*/
@Repository
@Transactional
public class CommentGrid extends AbstractQueryGrid {
private static final String PROPERTY_AUTHOR_PERSONAL_CODE = "author_personal_code";
private static final String PROPERTY_ORGANIZATION_CODE = "organization_code";
private static final String ACTION_AUTHOR_OR_ORGANIZATION_CODE = "author-or-organization-code";
public CommentGrid() {
super(Comment_type_issue_view.class, "comment");
}
@Override
protected void setRestrictions(DetachedCriteria criteria, PagedRequest request) {
criteria.add(Restrictions.isNull("comment.comment_parent_id"));
if (request.containsFilter(ACTION_AUTHOR_OR_ORGANIZATION_CODE)) {
restrictForAuthorOrOrganizationCode(criteria, request);
} else {
super.setRestrictions(criteria, request);
}
}
@Override
protected void setTransformation(DetachedCriteria criteria, PagedRequest request) {
criteria.setResultTransformer(new AliasToBeanResultTransformer(CommentTypeIssueViewModel.class));
}
/**
* Restricts query to search for either {@link #PROPERTY_AUTHOR_PERSONAL_CODE} or {@link
* #PROPERTY_ORGANIZATION_CODE}. Additionally creates sub query to make same query on child entities. All other
* filter restrictions are applied to main query only.
*
* @param criteria criteria to add restrictions to
* @param request paged request
*/
private void restrictForAuthorOrOrganizationCode(DetachedCriteria criteria, PagedRequest request) {
Criterion mainCriterion = getMainCriterion(request);
if (mainCriterion != null) {
criteria.add(mainCriterion);
}
Criterion propagatedCriterion = getPropagatedCriterion(request);
if (propagatedCriterion != null) {
DetachedCriteria subCriteria = DetachedCriteria.forClass(Comment.class, "child")
.setProjection(Projections.id())
.add(Restrictions.eqProperty("child.comment_parent_id", "comment.comment_id"))
.add(propagatedCriterion);
criteria.add(Restrictions.disjunction(propagatedCriterion, Subqueries.exists(subCriteria)));
}
}
private Criterion getMainCriterion(PagedRequest request) {
List<Criterion> criterionList = new ArrayList<>();
for (String property : request.getFilterProperties()) {
if (!isPropagatedProperty(property)) {
Criterion criterion = createPropertyRestrictions(request.getFilter(property));
if (criterion != null) {
criterionList.add(criterion);
}
}
}
return conjunction(criterionList);
}
private Criterion getPropagatedCriterion(PagedRequest request) {
List<Criterion> criterionList = new ArrayList<>();
for (String property : request.getFilterProperties()) {
if (isPropagatedProperty(property)) {
Criterion criterion = createPropertyRestrictions(request.getFilter(property));
if (criterion != null) {
criterionList.add(criterion);
}
}
}
return disjunction(criterionList);
}
private boolean isPropagatedProperty(String property) {
return PROPERTY_AUTHOR_PERSONAL_CODE.equalsIgnoreCase(property)
|| PROPERTY_ORGANIZATION_CODE.equalsIgnoreCase(property);
}
@Override
protected Criterion createPropertyFilterRestriction(FilterParameter filter) {
if (PROPERTY_AUTHOR_PERSONAL_CODE.equalsIgnoreCase(filter.getProperty())) {
return Restrictions.ilike(PROPERTY_AUTHOR_PERSONAL_CODE, filter.getValue());
}
return super.createPropertyFilterRestriction(filter);
}
}
......@@ -3,7 +3,6 @@ package ee.eesti.riha.rest.dao.util;
import java.text.ParseException;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......
package ee.eesti.riha.rest.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonRawValue;
import ee.eesti.riha.rest.logic.Finals;
import java.util.Date;
import java.util.UUID;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CommentTypeIssueViewModel {
private Integer comment_id;
private Integer comment_parent_id;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = Finals.DATE_FORMAT)
private Date creation_date;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = Finals.DATE_FORMAT)
private Date modified_date;
private UUID infosystem_uuid;
private String comment;
private String author_name;
private String author_personal_code;
private String organization_name;
private String organization_code;
private String status;
private String type;
private String title;
private String sub_type;
private String resolution_type;
private String infosystem_short_name;
private String infosystem_full_name;
@JsonRawValue
private String events;
@JsonRawValue
private String last_comment;
public Integer getComment_id() {
return comment_id;
}
public void setComment_id(Integer comment_id) {
this.comment_id = comment_id;
}
public Integer getComment_parent_id() {
return comment_parent_id;
}
public void setComment_parent_id(Integer comment_parent_id) {
this.comment_parent_id = comment_parent_id;
}
public Date getCreation_date() {
return creation_date;
}
public void setCreation_date(Date creation_date) {
this.creation_date = creation_date;
}
public Date getModified_date() {
return modified_date;
}
public void setModified_date(Date modified_date) {
this.modified_date = modified_date;
}
public UUID getInfosystem_uuid() {
return infosystem_uuid;
}
public void setInfosystem_uuid(UUID infosystem_uuid) {
this.infosystem_uuid = infosystem_uuid;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getAuthor_name() {
return author_name;
}
public void setAuthor_name(String author_name) {
this.author_name = author_name;
}
public String getAuthor_personal_code() {
return author_personal_code;
}
public void setAuthor_personal_code(String author_personal_code) {
this.author_personal_code = author_personal_code;
}
public String getOrganization_name() {
return organization_name;
}
public void setOrganization_name(String organization_name) {
this.organization_name = organization_name;
}
public String getOrganization_code() {
return organization_code;
}
public void setOrganization_code(String organization_code) {
this.organization_code = organization_code;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSub_type() {
return sub_type;
}
public void setSub_type(String sub_type) {
this.sub_type = sub_type;
}
public String getResolution_type() {
return resolution_type;
}
public void setResolution_type(String resolution_type) {
this.resolution_type = resolution_type;
}
public String getInfosystem_short_name() {
return infosystem_short_name;
}
public void setInfosystem_short_name(String infosystem_short_name) {
this.infosystem_short_name = infosystem_short_name;
}
public String getInfosystem_full_name() {
return infosystem_full_name;
}
public void setInfosystem_full_name(String infosystem_full_name) {
this.infosystem_full_name = infosystem_full_name;
}
public String getEvents() {
return events;
}
public void setEvents(String events) {
this.events = events;
}
public String getLast_comment() {
return last_comment;
}
public void setLast_comment(String last_comment) {
this.last_comment = last_comment;
}
}
......@@ -2,6 +2,7 @@ package ee.eesti.riha.rest.model.readonly;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.google.gson.JsonObject;
import ee.eesti.riha.rest.logic.Finals;
import ee.eesti.riha.rest.model.BaseModel;
......@@ -66,11 +67,20 @@ public class Comment_type_issue_view implements BaseModel {
@Column(name = "sub_type")
private String sub_type;
@Column(name = "resolution_type")
private String resolution_type;
@Column(name = "infosystem_short_name")
private String infosystem_short_name;
@Column(name = "resolution_type")
private String resolution_type;
@Column(name = "infosystem_full_name")
private String infosystem_full_name;
@JsonRawValue
private String events;
@JsonRawValue
private String last_comment;
public Integer getComment_id() {
return comment_id;
......@@ -168,6 +178,14 @@ public class Comment_type_issue_view implements BaseModel {
throw new UnsupportedOperationException();
}
public String getResolution_type() {
return resolution_type;
}
public void setResolution_type(String resolution_type) {
throw new UnsupportedOperationException();
}
public String getInfosystem_short_name() {
return infosystem_short_name;
}
......@@ -176,6 +194,30 @@ public class Comment_type_issue_view implements BaseModel {
throw new UnsupportedOperationException();
}
public String getInfosystem_full_name() {
return infosystem_full_name;
}
public void setInfosystem_full_name(String infosystem_full_name) {
throw new UnsupportedOperationException();
}
public String getEvents() {
return events;
}
public void setEvents(String events) {
throw new UnsupportedOperationException();
}
public String getLast_comment() {
return last_comment;
}
public void setLast_comment(String last_comment) {
throw new UnsupportedOperationException();
}
@Override
public int callGetId() {
return comment_id;
......@@ -305,12 +347,4 @@ public class Comment_type_issue_view implements BaseModel {
public void setKind(String kind) {
throw new UnsupportedOperationException();
}
public String getResolution_type() {
return resolution_type;
}
public void setResolution_type(String resolution_type) {
this.resolution_type = resolution_type;
}
}
package ee.eesti.riha.rest.service;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
/**
* Comment management controller interface.
*/
@Produces(MediaType.APPLICATION_JSON + "; charset=UTF-8")
public interface CommentService {
/**
* Retrieves paged list of comments
*
* @return paged list of comments
*/
@Path(value = "/api/comment")
@GET
public Response list(@Context UriInfo uriInfo);
}
package ee.eesti.riha.rest.service.impl;
import ee.eesti.riha.rest.dao.CommentDAO;
import ee.eesti.riha.rest.service.CommentService;
import ee.eesti.riha.rest.util.PagedRequest;
import ee.eesti.riha.rest.util.PagedRequestArgumentResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
/**
* Implementation of {@link CommentService} interface.
*/
@Component
public class CommentServiceImpl implements CommentService {
@Autowired
private PagedRequestArgumentResolver pagedRequestArgumentResolver;
@Autowired
private CommentDAO commentDAO;
@Override
public Response list(UriInfo uriInfo) {
PagedRequest request = pagedRequestArgumentResolver.resolve(uriInfo.getQueryParameters());
return Response.ok(commentDAO.list(request)).build();
}
}
package ee.eesti.riha.rest.util;
/**
* Filter parameter.
*/
public class FilterParameter {
private String property;
private String value;
public FilterParameter(String property, String value) {
this.property = property;
this.value = value;
}
public String getProperty() {
return property;
}
public String getValue() {
return value;
}
public String getString() {
return value != null ? value : "";
}
@Override
public String toString() {
return "FilterParameter{" +
"property='" + property + '\'' +
", value='" + value + '\'' +
'}';
}
}
package ee.eesti.riha.rest.util;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Represent request with paging, sorting and filter information.
*/
public class PagedRequest {
private int pageNumber;
private int pageSize;
private MultiValueMap<String, FilterParameter> filters = new LinkedMultiValueMap<>();
private List<SortParameter> sort = new ArrayList<>();
public PagedRequest(int pageNumber, int pageSize, List<FilterParameter> filters, List<SortParameter> sort) {
Assert.isTrue(pageNumber >= 0, "pageNumber must be greater than or equal to zero");
Assert.isTrue(pageSize > 0, "pageSize must be greater than zero");
this.pageNumber = pageNumber;
this.pageSize = pageSize;
if (filters != null) {
for (FilterParameter filter : filters) {
this.filters.add(filter.getProperty(), filter);
}
}
if (sort != null) {
this.sort.addAll(sort);
}
}
public int getPageNumber() {
return pageNumber;
}
public int getPageSize() {
return pageSize;
}
public boolean containsFilter(String property) {
return filters.containsKey(property);
}
public Set<String> getFilterProperties() {
return filters.keySet();
}
public FilterParameter getFirstFilter(String property) {
return filters.getFirst(property);
}
public List<FilterParameter> getFilter(String property) {
return filters.get(property);
}
public List<SortParameter> getSort() {
return sort;
}
@Override
public String toString() {
return "PagedRequest{" +
"pageNumber=" + pageNumber +
", pageSize=" + pageSize +
", filters=" + filters +
", sort=" + sort +
'}';
}
}
package ee.eesti.riha.rest.util;
import org.springframework.stereotype.Component;
import javax.ws.rs.core.MultivaluedMap;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.lang3.ArrayUtils.isEmpty;