diff --git a/src/mokka/src/main/java/pl/hycom/mokka/emulator/mock/MockConfigurationManager.java b/src/mokka/src/main/java/pl/hycom/mokka/emulator/mock/MockConfigurationManager.java index 8d3491c..fc55e27 100644 --- a/src/mokka/src/main/java/pl/hycom/mokka/emulator/mock/MockConfigurationManager.java +++ b/src/mokka/src/main/java/pl/hycom/mokka/emulator/mock/MockConfigurationManager.java @@ -26,6 +26,7 @@ import pl.hycom.mokka.security.UserRepository; import pl.hycom.mokka.security.model.AuditedRevisionEntity; import pl.hycom.mokka.security.model.User; +import pl.hycom.mokka.util.ArithmeticUtils; import pl.hycom.mokka.util.query.MockSearch; import pl.hycom.mokka.util.query.Q; import pl.hycom.mokka.util.query.QManager; @@ -223,7 +224,7 @@ public WrappedMockConfiguration getMockConfigurations(HttpServletRequest req) { mockSearch.setStartingIndex(Integer.parseInt(req.getParameter("from")) * mocksPerPage); } - // start perPage & startFromc + // start perPage & startFrom if (StringUtils.isNumeric(req.getParameter("perPage"))) { mockSearch.setMaxResults(Integer.parseInt(req.getParameter("perPage")) * mocksPerPage); } else { @@ -261,12 +262,9 @@ public WrappedMockConfiguration getMockConfigurations(HttpServletRequest req) { } wrappedMockConfiguration.mocks = mockSearch.find(); - - if (wrappedMockConfiguration.mocks.size() >= mocksPerPage + 1) { - wrappedMockConfiguration.hasNext = true; - } else { - wrappedMockConfiguration.hasNext = false; - } + Long allMockCount = mockSearch.countAllPossibleResults(); + wrappedMockConfiguration.pageCount = ArithmeticUtils.divideAndCeil(allMockCount, mocksPerPage); + mockSearch.clearParameterMap(); return wrappedMockConfiguration; } diff --git a/src/mokka/src/main/java/pl/hycom/mokka/emulator/mock/model/WrappedMockConfiguration.java b/src/mokka/src/main/java/pl/hycom/mokka/emulator/mock/model/WrappedMockConfiguration.java index 00b914f..fc25d8f 100644 --- a/src/mokka/src/main/java/pl/hycom/mokka/emulator/mock/model/WrappedMockConfiguration.java +++ b/src/mokka/src/main/java/pl/hycom/mokka/emulator/mock/model/WrappedMockConfiguration.java @@ -16,5 +16,5 @@ public class WrappedMockConfiguration implements Serializable { @JsonView(View.General.class) @TrackChanges - public boolean hasNext = false; + public Long pageCount; } diff --git a/src/mokka/src/main/java/pl/hycom/mokka/util/ArithmeticUtils.java b/src/mokka/src/main/java/pl/hycom/mokka/util/ArithmeticUtils.java new file mode 100644 index 0000000..4fc9d8b --- /dev/null +++ b/src/mokka/src/main/java/pl/hycom/mokka/util/ArithmeticUtils.java @@ -0,0 +1,27 @@ +package pl.hycom.mokka.util; + +/** + * Helper class for arithmetic related operations. + * + * @author Kamil Adamiec (kamil.adamiec@hycom.pl) + */ +public class ArithmeticUtils { + + private ArithmeticUtils() { + // NOP + } + + /** + * Method which is workaround for sonar issue with boxing Math.ceil result, + * to non-primitive type, for purpose of converting floating point result to integer type. + * Method uses modulo operations to make sure, that result of integer division will be always rounded upwards. + * + * @param dividend Dividend + * @param divisor Divisor + * @return Rounded result + */ + public static Long divideAndCeil(long dividend, long divisor) { + long remainder = dividend % divisor; + return (dividend + (remainder == 0 || remainder > (divisor / 2) ? remainder : (divisor - remainder))) / divisor; + } +} diff --git a/src/mokka/src/main/java/pl/hycom/mokka/util/query/MockSearch.java b/src/mokka/src/main/java/pl/hycom/mokka/util/query/MockSearch.java index 5bd505b..978ee2b 100644 --- a/src/mokka/src/main/java/pl/hycom/mokka/util/query/MockSearch.java +++ b/src/mokka/src/main/java/pl/hycom/mokka/util/query/MockSearch.java @@ -11,148 +11,157 @@ import java.util.List; import java.util.Map; - /** - * @author Tomasz Wozniak (tomasz.wozniak@hycom.pl) - */ - @Service - @Transactional - public class MockSearch { - - public static final String ID = "id"; - public static final String NAME = "name"; - public static final String PATH = "path"; - public static final String DESCRIPTION = "description"; - public static final String PATTERN = "pattern"; - public static final String ENABLED = "enabled"; - private static final String PERCENT = "%"; - private static final String WHERE = "where "; - - @PersistenceContext - private EntityManager em; - - private Integer startingIndex; - private Integer maxResults; - private Map parameterMap = new HashMap<>(); - - public List find(){ - Query query; - StringBuilder base = new StringBuilder(); - - if(parameterMap.size() == 0){ - base.append("select m from MockConfig m "); - } - else{ - base.append("select m from MockConfig m where "); - } +/** + * @author Tomasz Wozniak (tomasz.wozniak@hycom.pl) + */ +@Service +@Transactional +public class MockSearch { + + public static final String ID = "id"; + public static final String NAME = "name"; + public static final String PATH = "path"; + public static final String DESCRIPTION = "description"; + public static final String PATTERN = "pattern"; + public static final String ENABLED = "enabled"; + private static final String PERCENT = "%"; + private static final String WHERE = "where "; + + @PersistenceContext + private EntityManager em; + + private Integer startingIndex; + private Integer maxResults; + private Map parameterMap = new HashMap<>(); + + public List find(){ + Query query; + StringBuilder base = new StringBuilder(); + + if(parameterMap.size() == 0){ + base.append("select m from MockConfig m "); + } + else{ + base.append("select m from MockConfig m where "); + appendParametersToBaseQuery(base); + } - if(parameterMap.containsKey(ID)) { - base.append("m.id = :id "); - } + base.append("order by m ASC"); - if(parameterMap.containsKey(NAME)) { - if(base.toString().endsWith(WHERE)) { - base.append("lower(m.name) like lower(:name) "); - } else { - base.append("and lower(m.name) like lower(:name) "); - } - } + query = em.createQuery(base.toString(), MockConfiguration.class); + setQueryParameters(query); - if(parameterMap.containsKey(PATH)) { - if(base.toString().endsWith(WHERE)) { - base.append("lower(m.path) like lower(:path) "); - }else { - base.append("and lower(m.path) like lower(:path) "); - } - } + if (getStartingIndex() != null) { + query.setFirstResult(getStartingIndex()); + } - if(parameterMap.containsKey(PATTERN)){ - if(base.toString().endsWith(WHERE)) { - base.append("lower(m.pattern) like lower(:pattern) "); - } - else { - base.append("and lower(m.pattern) like lower(:pattern) "); - } - } + if (getMaxResults() != null) { + query.setMaxResults(getMaxResults()+1); + } + return query.getResultList(); + } - if(parameterMap.containsKey(DESCRIPTION)){ - if(base.toString().endsWith(WHERE)){ - base.append("lower(m.description) like lower(:description) "); - } - else { - base.append("and lower(m.description) like lower(:description) "); - } - } + public Long countAllPossibleResults() { + Query query; + StringBuilder base = new StringBuilder(); - if(parameterMap.containsKey(ENABLED)){ - if(base.toString().endsWith(WHERE)){ - base.append("m.enabled ="); - } - else { - base.append("and m.enabled ="); - } - - if("true".equalsIgnoreCase(parameterMap.get(ENABLED))){ - base.append("1 "); - } - else { - base.append("0 "); - } - } + if(parameterMap.size() == 0) { + base.append("select count(m) from MockConfig m"); + } else { + base.append("select count(m) from MockConfig m where "); + appendParametersToBaseQuery(base); + } - base.append("order by m ASC"); + query = em.createQuery(base.toString(), Long.class); + setQueryParameters(query); + return (Long) query.getSingleResult(); + } - query = em.createQuery(base.toString(), MockConfiguration.class); + private void appendParametersToBaseQuery(StringBuilder base) { - if(parameterMap.containsKey(ID)) { - query.setParameter("id", Long.valueOf(parameterMap.get(ID))); - } + if(parameterMap.containsKey(ID)) { + base.append("m.id = :id "); + } - if(parameterMap.containsKey(NAME)) { - query.setParameter("name", PERCENT + parameterMap.get(NAME) + PERCENT); - } + if(parameterMap.containsKey(NAME)) { + appendCondition(base, "lower(m.name) like lower(:name) "); + } - if(parameterMap.containsKey(PATH)) { - query.setParameter("path", PERCENT + parameterMap.get(PATH) + PERCENT); - } + if(parameterMap.containsKey(PATH)) { + appendCondition(base, "lower(m.path) like lower(:path) "); + } - if(parameterMap.containsKey(DESCRIPTION)){ - query.setParameter("description", PERCENT + parameterMap.get(DESCRIPTION) + PERCENT); - } + if(parameterMap.containsKey(PATTERN)){ + appendCondition(base, "lower(m.pattern) like lower(:pattern) "); + } - if(parameterMap.containsKey(PATTERN)){ - query.setParameter("pattern", PERCENT + parameterMap.get(PATTERN) + PERCENT); - } + if(parameterMap.containsKey(DESCRIPTION)){ + appendCondition(base, "lower(m.description) like lower(:description) "); + } - if (getStartingIndex() != null) { - query.setFirstResult(getStartingIndex()); - } + if(parameterMap.containsKey(ENABLED)){ + appendCondition(base, "m.enabled ="); - if (getMaxResults() != null) { - query.setMaxResults(getMaxResults()+1); + if("true".equalsIgnoreCase(parameterMap.get(ENABLED))){ + base.append("1 "); + } else { + base.append("0 "); } + } + } - parameterMap.clear(); - return query.getResultList(); + private void appendCondition(StringBuilder base, String conditionString) { + if(base.toString().endsWith(WHERE)) { + base.append("and "); } + base.append(conditionString); + } + + private void setQueryParameters(Query query) { - public void add(String parameter, String value){ - parameterMap.put(parameter,value); + if(parameterMap.containsKey(ID)) { + query.setParameter("id", Long.valueOf(parameterMap.get(ID))); } - public Integer getStartingIndex() { - return startingIndex; + if(parameterMap.containsKey(NAME)) { + query.setParameter("name", PERCENT + parameterMap.get(NAME) + PERCENT); } - public void setStartingIndex(Integer startingIndex) { - this.startingIndex = startingIndex; + if(parameterMap.containsKey(PATH)) { + query.setParameter("path", PERCENT + parameterMap.get(PATH) + PERCENT); } - public Integer getMaxResults() { - return maxResults; + if(parameterMap.containsKey(DESCRIPTION)){ + query.setParameter("description", PERCENT + parameterMap.get(DESCRIPTION) + PERCENT); } - public void setMaxResults(Integer maxResults) { - this.maxResults = maxResults; + if(parameterMap.containsKey(PATTERN)){ + query.setParameter("pattern", PERCENT + parameterMap.get(PATTERN) + PERCENT); } } + public void clearParameterMap() { + parameterMap.clear(); + } + + public void add(String parameter, String value){ + parameterMap.put(parameter,value); + } + + public Integer getStartingIndex() { + return startingIndex; + } + + public void setStartingIndex(Integer startingIndex) { + this.startingIndex = startingIndex; + } + + public Integer getMaxResults() { + return maxResults; + } + + public void setMaxResults(Integer maxResults) { + this.maxResults = maxResults; + } +} + diff --git a/src/mokka/src/main/resources/static/js/configuration/controller.js b/src/mokka/src/main/resources/static/js/configuration/controller.js index 3b9a055..60723d2 100644 --- a/src/mokka/src/main/resources/static/js/configuration/controller.js +++ b/src/mokka/src/main/resources/static/js/configuration/controller.js @@ -9,7 +9,9 @@ app.controller('ConfigurationController', function($rootScope, $scope, $mdToast, self.search.enabled = true; self.activeSearch = {}; self.pagination = 0; - self.paginationHasNext = false; + self.pageCount = 1; + self.pageIndexes = [1]; + self.maxPageIndexes = 9; self.loading = false; self.hasNext=false; // self.mocksPerPage = 10; @@ -42,7 +44,7 @@ app.controller('ConfigurationController', function($rootScope, $scope, $mdToast, file.upload = Upload.upload({ url: '/configurations/import', - data: {file: file} + data: {'file': file} }); file.upload.then(function (response) { @@ -219,11 +221,12 @@ app.controller('ConfigurationController', function($rootScope, $scope, $mdToast, self.showConfiguration(d.mocks[0]); d.mocks[0].editMode = true; } - self.paginationHasNext = d.hasNext; - self.mocks = d.mocks; - self.loading = false; - $location.hash(''); - $anchorScroll(); + self.pageCount = d.pageCount; + self.updatePageIndexes(); + self.mocks = d.mocks; + self.loading = false; + $location.hash(''); + $anchorScroll(); }); }; @@ -318,4 +321,44 @@ app.controller('ConfigurationController', function($rootScope, $scope, $mdToast, $mdToast.show($mdToast.simple().position('bottom right start').textContent('Change restored')); }); }; + + self.hasNextPage = function () { + return self.pagination < (self.pageCount - 1); + } + + self.goToPage = function(index) { + self.pagination = index - 1; + self.mocks = []; + self.fetchMocks(); + } + + self.getCurrentPageNumber = function() { + return self.pagination + 1; + } + + self.updatePageIndexes = function() { + const maxPagesAhead = (self.maxPageIndexes - 1) / 2; + const maxPagesBehind = (self.maxPageIndexes - 1) / 2; + + let currentPage = self.getCurrentPageNumber(); + let pagesAhead = self.pageCount - currentPage; + pagesAhead = pagesAhead > maxPagesAhead ? maxPagesAhead : pagesAhead; + let pagesBehind = currentPage - 1; + pagesBehind = pagesBehind > maxPagesBehind ? maxPagesBehind : pagesBehind; + + if(pagesAhead < maxPagesAhead) { + pagesBehind += maxPagesAhead - pagesAhead; + } + if(pagesBehind < maxPagesBehind) { + pagesAhead += maxPagesBehind - pagesBehind; + } + + let startIndex = currentPage - pagesBehind; + startIndex = startIndex < 1 ? 1 : startIndex; + let endIndex = currentPage + pagesAhead; + self.pageIndexes = []; + for(let i=0; startIndex <= endIndex && startIndex <= self.pageCount; i++, startIndex++) { + self.pageIndexes[i] = startIndex; + } + } }); diff --git a/src/mokka/src/main/resources/templates/configuration/page.html b/src/mokka/src/main/resources/templates/configuration/page.html index dff76f5..6f3864a 100644 --- a/src/mokka/src/main/resources/templates/configuration/page.html +++ b/src/mokka/src/main/resources/templates/configuration/page.html @@ -299,8 +299,17 @@
Field: {{ key }} +
+ + Page {{ pageIndex }} +
+ {{ pageIndex }} +
+
+
+ ng-disabled="!ctrl.hasNextPage()"> Next page diff --git a/src/mokka/src/test/java/pl/hycom/mokka/util/ArithmeticUtilsUnitTest.java b/src/mokka/src/test/java/pl/hycom/mokka/util/ArithmeticUtilsUnitTest.java new file mode 100644 index 0000000..bd4cca2 --- /dev/null +++ b/src/mokka/src/test/java/pl/hycom/mokka/util/ArithmeticUtilsUnitTest.java @@ -0,0 +1,16 @@ +package pl.hycom.mokka.util; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ArithmeticUtilsUnitTest { + + @Test + public void divideAndCeilTest() { + + Assertions.assertEquals(4, ArithmeticUtils.divideAndCeil(15, 4)); + Assertions.assertEquals(4, ArithmeticUtils.divideAndCeil(16, 4)); + Assertions.assertEquals(5, ArithmeticUtils.divideAndCeil(17, 4)); + Assertions.assertEquals(5, ArithmeticUtils.divideAndCeil(18, 4)); + } +}