Commit 55ca354a authored by Maxym Borodenko's avatar Maxym Borodenko Committed by Matija Obreza

1) Request's Accept header should be application/json - done.

2) Server should respond in JSON with error message that can be displayed to the user - done.

3) Frontend should allow user to close the error message and continue working - done.
parent be50a9d4
Pipeline #2303 canceled with stages
in 19 seconds
/**
* Copyright 2017 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.exception;
/**
* @author Maxym Borodenko
*/
public class MaxPageLimitException extends RuntimeException {
private static final long serialVersionUID = -8667874524883066239L;
private Integer maxPageLimit;
public MaxPageLimitException(final Integer maxPageLimit) {
this.maxPageLimit = maxPageLimit;
}
@Override
public String getMessage() {
return "Genesys does not support requests for data beyond " + maxPageLimit + "th page.";
}
public Integer getMaxPageLimit() {
return maxPageLimit;
}
}
......@@ -28,6 +28,7 @@ import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.exception.MaxPageLimitException;
import org.genesys2.server.model.elastic.AccessionDetails;
import org.genesys2.server.model.genesys.Accession;
import org.genesys2.server.model.genesys.Method;
......@@ -166,7 +167,7 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
transformFiltersIfNeed(filters);
if (pageable.getPageNumber() > PAGINATION_MAXPAGE_LIMIT) {
throw new RuntimeException("Genesys does not support requests for data beyond " + PAGINATION_MAXPAGE_LIMIT + "th page.");
throw new MaxPageLimitException(PAGINATION_MAXPAGE_LIMIT);
}
final DirectMysqlQuery directQuery = new DirectMysqlQuery("accession", "a");
......
......@@ -45,6 +45,7 @@ import com.jhlabs.image.MapColorsFilter;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.utils.URIBuilder;
import org.genesys2.server.exception.MaxPageLimitException;
import org.genesys2.server.model.elastic.AccessionDetails;
import org.genesys2.server.model.filters.GenesysFilter;
import org.genesys2.server.model.filters.I18nListFilter;
......@@ -70,6 +71,7 @@ import org.genesys2.server.service.impl.FilterHandler.AppliedFilter;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilters;
import org.genesys2.server.service.impl.GenesysFilterServiceImpl.LabelValue;
import org.genesys2.server.service.impl.SearchException;
import org.genesys2.server.servlet.model.ErrorResponse;
import org.genesys2.spring.ResourceNotFoundException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -79,7 +81,9 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.facet.result.Term;
import org.springframework.data.elasticsearch.core.facet.result.TermResult;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
......@@ -430,14 +434,21 @@ public class ExplorerController extends BaseController implements InitializingBe
return availableFilters;
}
@RequestMapping(value = "/explore/json", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Page<?> getFilteredJsonData(@RequestParam(value = "page", required = false, defaultValue = "1") int page,
@RequestParam(value = "filter", required = true, defaultValue = "{}") String jsonFilter,
@RequestParam(value = "results", required = true, defaultValue = "50") int results) throws IOException, SearchException {
AppliedFilters appliedFilters = mapper.readValue(jsonFilter, AppliedFilters.class);
return filterService.listAccessionDetails(appliedFilters, new PageRequest(page - 1, Integer.min(results, maxPageSize), new Sort("seqNo")));
}
@RequestMapping(value = "/explore/json", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity getFilteredJsonData(@RequestParam(value = "page", required = false, defaultValue = "1") int page,
@RequestParam(value = "filter", required = true, defaultValue = "{}") String jsonFilter,
@RequestParam(value = "results", required = true, defaultValue = "50") int results) throws IOException, SearchException {
AppliedFilters appliedFilters = mapper.readValue(jsonFilter, AppliedFilters.class);
try {
return new ResponseEntity<Page>(filterService.listAccessionDetails(appliedFilters, new PageRequest(page - 1, Integer.min(results, maxPageSize), new Sort("seqNo"))), HttpStatus.OK);
} catch (MaxPageLimitException ex) {
ErrorResponse error = new ErrorResponse();
error.setErrorCode(HttpStatus.BAD_REQUEST.value());
error.setMessage(ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
}
@RequestMapping(value = "/explore/booleanSuggestions", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
......
/**
* Copyright 2017 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.servlet.model;
/**
* @author Maxym Borodenko
*/
public class ErrorResponse {
private Integer errorCode;
private String message;
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(final Integer errorCode) {
this.errorCode = errorCode;
}
public String getMessage() {
return message;
}
public void setMessage(final String message) {
this.message = message;
}
}
......@@ -142,6 +142,7 @@ document.addEventListener('DOMContentLoaded', function() {
var popup = document.getElementById('error-loading-popup-id');
if(event.target === popup) {
popup.style.display = 'none';
document.getElementById('loading-popup-id').style.display = 'none';
}
});
......
......@@ -2624,7 +2624,7 @@ h2.short {
}
.error-loading-popup-content {
p {
padding: 10px 20px 0 0;
padding: 0px 20px 0 0;
}
}
......
......@@ -37,13 +37,6 @@
</div>
</div>
<div id="error-loading-popup-id" class="error-loading-popup">
<div class="error-loading-popup-content">
<span class="close" id="error-loading-popup-close"><span class="glyphicon glyphicon-remove"></span></span>
<p><spring:message code="prompt.loading-data-failed" /></p>
</div>
</div>
<div id="dialog" class="row"></div>
<div class="">
<!--Filters-->
......@@ -154,8 +147,10 @@
<span><spring:message code="maps.view-map"/></span>
</a>
</div>
<!--Pagination-->
<filters:pagination pagedData="${pagedData}" jsonFilter="${jsonFilter}"/>
<div class="top-pagination-wrapper">
<!--Pagination-->
<filters:pagination pagedData="${pagedData}" jsonFilter="${jsonFilter}"/>
</div>
</div>
</div>
<div id="content-area" class="col-lg-10 col-md-9 col-sm-9 col-xs-12 explore-table">
......@@ -296,6 +291,8 @@
var page = ${pagedData.number};
var totalPages = ${pagedData.totalPages};
var results = ${pagedData.size};
var cropNames = [];
......@@ -346,7 +343,7 @@
}
}
});
$('body').on('input', '.panel-group .input-group .form-control', function () {
var emptyFilterVal = true;
$.each($(this).parent().parent().find('.filtval'), function () {
......@@ -413,7 +410,7 @@
var needToApply = true;
$.each($(parentEl).find('.filtval'), function () {
needToApply = false;
});
});
if(needToApply) {
applyFilters();
}
......@@ -439,7 +436,7 @@
var needToApply = true;
$.each($(parentEl).find('.filtval'), function () {
needToApply = false;
});
});
if(needToApply) {
applyFilters();
}
......@@ -453,9 +450,9 @@
$("body").on("click", ".applyBtn", function () {
if( !$(this).hasClass('disabled') ) {
$(this).parent().parent().find("button.filter-auto, button.filter-range").trigger("click");
applyFilters();
BrowseUtil.applySuggestions(jsonData, messages);
$(this).parent().parent().find("button.filter-auto, button.filter-range").trigger("click");
applyFilters();
BrowseUtil.applySuggestions(jsonData, messages);
}
});
......@@ -609,20 +606,66 @@
jsonData[key] = [];
});
$.each([$(".firstPage"), $(".previousPage"), $(".nextPage"), $(".lastPage")], function () {
$(this).find("a").on("click", function (e) {
$("body").on("click", ".firstPage", function(e) {
e.preventDefault();
applyFiltersWithPageNumber(1);
var element_to_scroll_to = $('#table-top')[0];
element_to_scroll_to.scrollIntoView();
});
$("body").on("click", ".nextPage", function(e) {
e.preventDefault();
page = (page ) === 501 ? page : page + 1;
applyFiltersWithPageNumber(page + 1);
var element_to_scroll_to = $('#table-top')[0];
element_to_scroll_to.scrollIntoView();
});
$("body").on("click", ".previousPage", function(e) {
e.preventDefault();
page = page === 0 ? 0 : (page - 1);
applyFiltersWithPageNumber(page + 1);
var element_to_scroll_to = $('#table-top')[0];
element_to_scroll_to.scrollIntoView();
});
$("body").on("click", ".lastPage", function(e) {
e.preventDefault();
applyFiltersWithPageNumber(totalPages);
var element_to_scroll_to = $('#table-top')[0];
element_to_scroll_to.scrollIntoView();
});
$("body").on("click", ".close", function(e) {
e.preventDefault();
$('#error-loading-popup-id').hide();
$('#loading-popup-id').hide();
});
$(".top-pagination-wrapper #paginationInputField").on("keypress", function(e) {
if(e.which === 13) {
e.preventDefault();
applyFilters($(e.target).attr("href"));
applyFiltersWithPageNumber($(this).val());
var tableToScrollTo = $('#table-top')[0];
tableToScrollTo.scrollIntoView();
});
}
});
$("nav.text-center").find("form").on("submit", function (e) {
$(".bottom-pagination-wrapper #paginationInputField").on("keypress", function(e) {
if(e.which === 13) {
e.preventDefault();
applyFiltersWithPageNumber($(this).val());
var tableToScrollTo = $('#table-top')[0];
tableToScrollTo.scrollIntoView();
}
});
$("body").on("input paste", "#paginationInputField", function (e) {
e.preventDefault();
applyFilters("?" + $(this).serialize());
var tableToScrollTo = $('#table-top')[0];
tableToScrollTo.scrollIntoView();
if (/\D/g.test(this.value)) {
// Filter non-digits from input value.
this.value = this.value.replace(/\D/g, '');
}
});
$(".results-per-page").on("change", function () {
......@@ -638,7 +681,7 @@
$(this).collapse("show");
if(!$(this).is('#panel_additional_collapse')) {
$(this).children().children().find('.applyBtn').removeClass('disabled');
}
}
}
</c:forEach>
});
......@@ -798,6 +841,10 @@
window.history.pushState(requestUrl, '', displayUrl);
$.ajax({
beforeSend: function(xhrObj){
xhrObj.setRequestHeader("Content-Type","application/json");
xhrObj.setRequestHeader("Accept","application/json");
},
url: requestUrl,
method: 'get',
success: function (response) {
......@@ -808,8 +855,12 @@
});
},
error: function () {
var message = $.trim('<spring:message code="prompt.loading-data-failed" />');
buildErrorPopup(message);
document.getElementById('error-loading-popup-id').style.display = "block";
}
}).done(function(res) {
totalPages = res.totalPages;
});
}
......@@ -836,6 +887,46 @@
refreshTickedAccessions();
}
function applyFiltersWithPageNumber (page) {
document.getElementById('loading-popup-id').style.display = "block";
var filter = JSON.stringify(jsonData);
var requestUrl = '//' + location.host + location.pathname + "/json" + ((page !== undefined ? ("?page=" + page + "&") : "?page=1&") + "filter=" + encodeURIComponent(filter));
requestUrl += "&results=" + results;
var displayUrl = requestUrl.replace('/json', '');
window.history.pushState(requestUrl, '', displayUrl);
$.ajax({
beforeSend: function(xhrObj){
xhrObj.setRequestHeader("Content-Type","application/json");
xhrObj.setRequestHeader("Accept","application/json");
},
url: requestUrl,
method: 'get',
success: function (response) {
renderData(response);
document.getElementById('loading-popup-id').style.display = "none";
$.each($("select.column-changer"), function () {
$(this).change();
});
},
error: function (error) {
buildErrorPopup(error.responseJSON.message);
document.getElementById('error-loading-popup-id').style.display = "block";
}
}).done(function(res) {
page = res.number;
totalPages = res.totalPages;
});
}
function buildErrorPopup(message) {
$('.container-fluid')
.append($('<div id="error-loading-popup-id"/>').addClass('error-loading-popup')
.append($('<div/>').addClass('error-loading-popup-content')
.append($('<span/>').addClass('close')
.append($('<span/>').addClass('glyphicon glyphicon-remove')))
.append($('<p/>').append(message))));
}
function refreshTickedAccessions() {
$.ajax({
url : '/sel/json/selection',
......
......@@ -34,7 +34,7 @@
</li>
<li>
<span class="pagination-input">
<input class="form-control" style="display: inline; max-width: 5em; text-align: center"
<input class="form-control" id="paginationInputField" style="display: inline; max-width: 5em; text-align: center"
type="text" name="page" value="${pagedData.number + 1}"/>
</span>
</li>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment