diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/request/ListQuery.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/request/ListQuery.java index f57edf76e04c..1cb560a3d897 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/request/ListQuery.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/request/ListQuery.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; @@ -31,6 +33,8 @@ import org.jetbrains.annotations.NotNull; public class ListQuery { + private static final Pattern PAGE_CLAUSE_PATTERN = Pattern.compile("(?i)\\bpage\\s+(\\d+)\\b"); + boolean allContent; Long max; Long page; @@ -53,6 +57,14 @@ public void setMax(Long max) { this.max = max; } + public Long getPage() { + return page; + } + + public void setPage(Long page) { + this.page = page; + } + public void setSearch(Map search) { this.search = search; } @@ -108,10 +120,13 @@ public static ListQuery fromRequest(HttpServletRequest request) { query.setFollow(follow); Map searchItems = getSearchMap(request.getParameter("search")); if (!searchItems.isEmpty()) { - try { - query.setMax(Long.parseLong(searchItems.get("page"))); - } catch (NumberFormatException e) { - // Ignore invalid page and keep default null value. + String pageValue = searchItems.get("page"); + if (StringUtils.isNotBlank(pageValue)) { + try { + query.setPage(Long.parseLong(pageValue)); + } catch (NumberFormatException e) { + // Ignore invalid page and keep default null value. + } } query.setSearch(searchItems); } @@ -119,39 +134,19 @@ public static ListQuery fromRequest(HttpServletRequest request) { return query; } - // Parse search clause. Only keep items which use simple '=' operator, and ignore others. For example: - // name=myvm and status=up --> {name=myvm, status=up} - // name=myvm and status!=down --> {name=myvm} (ignore status!=down because it uses '!=' operator) + // Parse search clause. For now, only extract the oVirt paging clause. + // Examples: + // page 3 --> {page=3} + // sortby name page 2 --> {page=2} @NotNull private static Map getSearchMap(String searchClause) { Map searchItems = new LinkedHashMap<>(); if (StringUtils.isBlank(searchClause)) { return searchItems; } - String[] terms = searchClause.trim().split("(?i)\\s+and\\s+"); - for (String term : terms) { - if (term == null) { - continue; - } - String trimmedTerm = term.trim(); - if (trimmedTerm.isEmpty()) { - continue; - } - - int eqIdx = trimmedTerm.indexOf('='); - if (eqIdx <= 0 || eqIdx != trimmedTerm.lastIndexOf('=')) { - continue; - } - char prev = trimmedTerm.charAt(eqIdx - 1); - if (prev == '!' || prev == '<' || prev == '>') { - continue; - } - - String key = trimmedTerm.substring(0, eqIdx).trim(); - String value = trimmedTerm.substring(eqIdx + 1).trim(); - if (!key.isEmpty() && !value.isEmpty()) { - searchItems.put(key, value); - } + Matcher matcher = PAGE_CLAUSE_PATTERN.matcher(searchClause); + if (matcher.find()) { + searchItems.put("page", matcher.group(1)); } return searchItems; } diff --git a/plugins/integrations/veeam-control-service/src/test/java/org/apache/cloudstack/veeam/api/request/ListQueryTest.java b/plugins/integrations/veeam-control-service/src/test/java/org/apache/cloudstack/veeam/api/request/ListQueryTest.java index 16fbd02669c5..a6d8bfef1d4b 100644 --- a/plugins/integrations/veeam-control-service/src/test/java/org/apache/cloudstack/veeam/api/request/ListQueryTest.java +++ b/plugins/integrations/veeam-control-service/src/test/java/org/apache/cloudstack/veeam/api/request/ListQueryTest.java @@ -67,18 +67,18 @@ public void testFromRequest_ParsesAllContentMaxAndFollow() { } @Test - public void testFromRequest_SearchParserIgnoresNonEqualsAndUsesPageValueAsMaxCurrentBehavior() { + public void testFromRequest_SearchParserExtractsPageOnly() { final HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getParameterMap()).thenReturn(Map.of("search", new String[]{"name=vm and page=3 and status!=down and x>=1"})); + when(request.getParameterMap()).thenReturn(Map.of("search", new String[]{"sortby name page 3"})); when(request.getParameter("all_content")).thenReturn(null); when(request.getParameter("max")).thenReturn(null); when(request.getParameter("follow")).thenReturn(null); - when(request.getParameter("search")).thenReturn("name=vm and page=3 and status!=down and x>=1"); + when(request.getParameter("search")).thenReturn("sortby name page 3"); final ListQuery query = ListQuery.fromRequest(request); - // Document existing behavior: when search contains page=..., max is set from it. - org.junit.Assert.assertEquals(Long.valueOf(3L), query.getLimit()); + // Only page key is extracted from search clause in oVirt format "page N" + org.junit.Assert.assertEquals(Long.valueOf(3L), query.getPage()); } @Test