You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by GitBox <gi...@apache.org> on 2022/05/03 10:29:16 UTC

[GitHub] [fineract] galovics commented on a diff in pull request #2310: FINERACT-1609: Added Pagination to reports

galovics commented on code in PR #2310:
URL: https://github.com/apache/fineract/pull/2310#discussion_r863541735


##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java:
##########
@@ -220,6 +220,16 @@ public int getRoundingMode() {
         return defaultValue;
     }
 
+    @Override
+    public Integer reportsPaginationNumberOfItemsPerPage() {
+        final String propertyName = "reports-pagination-number-of-items-per-page";

Review Comment:
   I know this is kind of the standard but would you mind starting to extract these into a constant?



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -162,9 +181,33 @@ public GenericResultsetData retrieveGenericResultset(final String name, final St
         final long startTime = System.currentTimeMillis();
         LOG.info("STARTING REPORT: {}   Type: {}", name, type);
 
-        final String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        final GenericResultsetData result;
+        Boolean paginationAllowed = BooleanUtil.parseBoolean(queryParams.get(ReportingConstants.isPaginationAllowed));
 
-        final GenericResultsetData result = this.genericDataService.fillGenericResultSet(sql);
+        if (paginationAllowed) {
+            final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+
+            final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors);
+            baseDataValidator.reset().parameter(pageNo).value(queryParams.get(pageNo)).notNull().throwValidationErrors();
+
+            baseDataValidator.reset().parameter(paginationOrderBy).value(queryParams.get(paginationOrderBy)).ignoreIfNull()
+                    .matchesRegularExpression(ORDER_BY_REGEX_PATTERN).throwValidationErrors();
+            Integer pageSize = this.configurationDomainService.reportsPaginationNumberOfItemsPerPage();
+
+            Page<GenericResultsetData> reportData = this.paginationHelper.fetchPage(this.jdbcTemplate, sql, null, new ReportMapper(sql));
+            Integer pageNo = Integer.parseInt(queryParams.get(ReportingConstants.pageNo));
+
+            pageNo = pageNo * pageSize;
+            sql = sql + " order by " + queryParams.get(paginationOrderBy) + " limit " + pageSize + " offset " + pageNo;

Review Comment:
   This is a PostgreSQL specific SQL. That's why we have the DatabaseSpecificSQLGenerator#limit method available, to generate this.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java:
##########
@@ -1315,7 +1315,7 @@ public GenericResultsetData retrieveDataTableGenericResultSet(final String dataT
 
         final List<ResultsetRowData> result = fillDatatableResultSetDataRows(sql);
 
-        return new GenericResultsetData(columnHeaders, result);
+        return new GenericResultsetData(columnHeaders, result, null, null);

Review Comment:
   Smelly usage.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtest/stretchyreports/StretchyReportHelper.java:
##########
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtest.stretchyreports;
+
+import com.google.gson.Gson;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportHelper {
+
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+
+    private String nameOfReport = "report_test";
+    private String reportType = "Table";
+    private String reportSQL = null;
+    private String useReport = null;
+    List<String> reportParameters = Collections.<String>emptyList();
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportHelper.class);
+    private static final String CREATE_REPORT_URL = "/fineract-provider/api/v1/reports?" + Utils.TENANT_IDENTIFIER;
+
+    public StretchyReportHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
+        this.requestSpec = requestSpec;
+        this.responseSpec = responseSpec;
+    }
+
+    public String build() {

Review Comment:
   build what?



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java:
##########
@@ -99,8 +99,8 @@ public static void verifyAllDefaultGlobalConfigurations(final RequestSpecificati
         ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
 
         // There are currently 37 global configurations.

Review Comment:
   Delete the comment please or at least update it. Worst comment is the misleading comment.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -69,14 +79,23 @@ public class ReadReportingServiceImpl implements ReadReportingService {
     private final PlatformSecurityContext context;
     private final GenericDataService genericDataService;
     private final SqlInjectionPreventerService sqlInjectionPreventerService;
+    private final DatabaseSpecificSQLGenerator sqlGenerator;
+    private final ConfigurationDomainService configurationDomainService;
+    private final PaginationHelper paginationHelper;
+    private static final String ORDER_BY_REGEX_PATTERN = "^[0-9]*$";
 
     @Autowired
     public ReadReportingServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
-            final GenericDataService genericDataService, SqlInjectionPreventerService sqlInjectionPreventerService) {
+            final GenericDataService genericDataService, SqlInjectionPreventerService sqlInjectionPreventerService,

Review Comment:
   Why not turning it into a Lombok constructor?



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -69,14 +79,23 @@ public class ReadReportingServiceImpl implements ReadReportingService {
     private final PlatformSecurityContext context;
     private final GenericDataService genericDataService;
     private final SqlInjectionPreventerService sqlInjectionPreventerService;
+    private final DatabaseSpecificSQLGenerator sqlGenerator;
+    private final ConfigurationDomainService configurationDomainService;
+    private final PaginationHelper paginationHelper;
+    private static final String ORDER_BY_REGEX_PATTERN = "^[0-9]*$";

Review Comment:
   Please put the static constant to the top.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/GenericResultsetData.java:
##########
@@ -27,10 +27,15 @@ public final class GenericResultsetData {
 
     private final List<ResultsetColumnHeaderData> columnHeaders;
     private final List<ResultsetRowData> data;
+    private Integer totalItems;

Review Comment:
   Why not final?



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -162,9 +181,33 @@ public GenericResultsetData retrieveGenericResultset(final String name, final St
         final long startTime = System.currentTimeMillis();
         LOG.info("STARTING REPORT: {}   Type: {}", name, type);
 
-        final String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        final GenericResultsetData result;
+        Boolean paginationAllowed = BooleanUtil.parseBoolean(queryParams.get(ReportingConstants.isPaginationAllowed));
 
-        final GenericResultsetData result = this.genericDataService.fillGenericResultSet(sql);
+        if (paginationAllowed) {
+            final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+
+            final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors);
+            baseDataValidator.reset().parameter(pageNo).value(queryParams.get(pageNo)).notNull().throwValidationErrors();
+
+            baseDataValidator.reset().parameter(paginationOrderBy).value(queryParams.get(paginationOrderBy)).ignoreIfNull()
+                    .matchesRegularExpression(ORDER_BY_REGEX_PATTERN).throwValidationErrors();
+            Integer pageSize = this.configurationDomainService.reportsPaginationNumberOfItemsPerPage();
+
+            Page<GenericResultsetData> reportData = this.paginationHelper.fetchPage(this.jdbcTemplate, sql, null, new ReportMapper(sql));
+            Integer pageNo = Integer.parseInt(queryParams.get(ReportingConstants.pageNo));
+
+            pageNo = pageNo * pageSize;
+            sql = sql + " order by " + queryParams.get(paginationOrderBy) + " limit " + pageSize + " offset " + pageNo;

Review Comment:
   Also, this is a potential SQL injection problem. Use query parameters instead of concatenating the string.
   Also, where you can't use it, make sure to escape it (SqlInjectionPreventerService)



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/GenericResultsetData.java:
##########
@@ -41,6 +46,14 @@ public List<ResultsetRowData> getData() {
         return this.data;
     }
 
+    public void setReportSize(Integer totalItems) {

Review Comment:
   Lombok setters + getters pls.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/GenericResultsetData.java:
##########
@@ -27,10 +27,15 @@ public final class GenericResultsetData {
 
     private final List<ResultsetColumnHeaderData> columnHeaders;
     private final List<ResultsetRowData> data;
+    private Integer totalItems;

Review Comment:
   Why the wrapper Integer class instead of primitive int?



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -162,9 +181,33 @@ public GenericResultsetData retrieveGenericResultset(final String name, final St
         final long startTime = System.currentTimeMillis();
         LOG.info("STARTING REPORT: {}   Type: {}", name, type);
 
-        final String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        final GenericResultsetData result;
+        Boolean paginationAllowed = BooleanUtil.parseBoolean(queryParams.get(ReportingConstants.isPaginationAllowed));
 
-        final GenericResultsetData result = this.genericDataService.fillGenericResultSet(sql);
+        if (paginationAllowed) {
+            final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+
+            final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors);
+            baseDataValidator.reset().parameter(pageNo).value(queryParams.get(pageNo)).notNull().throwValidationErrors();
+
+            baseDataValidator.reset().parameter(paginationOrderBy).value(queryParams.get(paginationOrderBy)).ignoreIfNull()
+                    .matchesRegularExpression(ORDER_BY_REGEX_PATTERN).throwValidationErrors();
+            Integer pageSize = this.configurationDomainService.reportsPaginationNumberOfItemsPerPage();
+
+            Page<GenericResultsetData> reportData = this.paginationHelper.fetchPage(this.jdbcTemplate, sql, null, new ReportMapper(sql));
+            Integer pageNo = Integer.parseInt(queryParams.get(ReportingConstants.pageNo));
+
+            pageNo = pageNo * pageSize;
+            sql = sql + " order by " + queryParams.get(paginationOrderBy) + " limit " + pageSize + " offset " + pageNo;
+
+            result = this.genericDataService.fillGenericResultSet(sql);
+            result.setRecordsPerPage(pageSize);

Review Comment:
   I don't like setting these after constructing the result. 
   By allowing setters to be called, we essentially allow screwing up the invariant of the class.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/GenericDataServiceImpl.java:
##########
@@ -88,7 +88,7 @@ public GenericResultsetData fillGenericResultSet(final String sql) {
                 resultsetDataRows.add(resultsetDataRow);
             }
 
-            return new GenericResultsetData(columnHeaders, resultsetDataRows);
+            return new GenericResultsetData(columnHeaders, resultsetDataRows, null, null);

Review Comment:
   This is definitely misleading. 
   Either you're mixing up the responsibility of a GenericResultsetData by putting those 2 new attributes there while not using it everywhere.
   Or you just need to provide those 2 numbers here as well



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -37,8 +40,15 @@
 import java.util.Map;
 import java.util.Set;
 import javax.ws.rs.core.StreamingOutput;
+import liquibase.util.BooleanUtil;

Review Comment:
   Please don't use Liquibase internal classes.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -216,6 +258,21 @@ private String getSql(final String name, final String type) {
         throw new ReportNotFoundException(encodedName);
     }
 
+    private static final class ReportMapper implements RowMapper<GenericResultsetData> {
+
+        private final String schema;
+
+        ReportMapper(String sql) {
+
+            this.schema = sql;
+        }
+
+        @Override
+        public GenericResultsetData mapRow(ResultSet rs, int rowNum) throws SQLException {

Review Comment:
   Ehm. This is another smell of counting the rows the wrong way. This definitely has to go.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -162,9 +181,33 @@ public GenericResultsetData retrieveGenericResultset(final String name, final St
         final long startTime = System.currentTimeMillis();
         LOG.info("STARTING REPORT: {}   Type: {}", name, type);
 
-        final String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        final GenericResultsetData result;
+        Boolean paginationAllowed = BooleanUtil.parseBoolean(queryParams.get(ReportingConstants.isPaginationAllowed));
 
-        final GenericResultsetData result = this.genericDataService.fillGenericResultSet(sql);
+        if (paginationAllowed) {
+            final List<ApiParameterError> dataValidationErrors = new ArrayList<>();

Review Comment:
   Can we extract the whole paginationAllowed branch into a separate method?
   Let's try to keep everything as simple as possible.
   One branch for paginating the result, and one for non-paginated result.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/GenericResultsetData.java:
##########
@@ -27,10 +27,15 @@ public final class GenericResultsetData {
 
     private final List<ResultsetColumnHeaderData> columnHeaders;
     private final List<ResultsetRowData> data;
+    private Integer totalItems;
+    private Integer recordsPerPage;
 
-    public GenericResultsetData(final List<ResultsetColumnHeaderData> columnHeaders, final List<ResultsetRowData> resultsetDataRows) {
+    public GenericResultsetData(final List<ResultsetColumnHeaderData> columnHeaders, final List<ResultsetRowData> resultsetDataRows,

Review Comment:
   Please turn this constructor into a Lombok constructor.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtest/stretchyreports/StretchyReportHelper.java:
##########
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtest.stretchyreports;
+
+import com.google.gson.Gson;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportHelper {
+
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+
+    private String nameOfReport = "report_test";
+    private String reportType = "Table";
+    private String reportSQL = null;
+    private String useReport = null;
+    List<String> reportParameters = Collections.<String>emptyList();
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportHelper.class);

Review Comment:
   Lombok logger pls.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -162,9 +181,33 @@ public GenericResultsetData retrieveGenericResultset(final String name, final St
         final long startTime = System.currentTimeMillis();
         LOG.info("STARTING REPORT: {}   Type: {}", name, type);
 
-        final String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        final GenericResultsetData result;
+        Boolean paginationAllowed = BooleanUtil.parseBoolean(queryParams.get(ReportingConstants.isPaginationAllowed));

Review Comment:
   Why the wrapper Boolean variable type? Simply go with primitive boolean.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java:
##########
@@ -1337,7 +1337,7 @@ private GenericResultsetData retrieveDataTableGenericResultSetForUpdate(final St
 
         final List<ResultsetRowData> result = fillDatatableResultSetDataRows(sql);
 
-        return new GenericResultsetData(columnHeaders, result);
+        return new GenericResultsetData(columnHeaders, result, null, null);

Review Comment:
   Smelly usage.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportingConstants.java:
##########
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.infrastructure.dataqueries.service;
+
+@SuppressWarnings({ "HideUtilityClassConstructor" })
+public class ReportingConstants {
+
+    public static final String pageNo = "pageNo";

Review Comment:
   Can you please follow the proper Java naming convention here for constants?
   Should be PAGE_NO, IS_PAGINATION_ALLOWED, etc.



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java:
##########
@@ -39,6 +43,21 @@ default Map<String, String> getReportParams(final MultivaluedMap<String, String>
                 SQLInjectionValidator.validateSQLInput(pValue);
                 reportParams.put(pKey, pValue);
             }
+            if (k.equalsIgnoreCase(isPaginationAllowed)) {

Review Comment:
   Why 3 different branches? Why not a single one with OR logical checks?



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java:
##########
@@ -162,9 +181,33 @@ public GenericResultsetData retrieveGenericResultset(final String name, final St
         final long startTime = System.currentTimeMillis();
         LOG.info("STARTING REPORT: {}   Type: {}", name, type);
 
-        final String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport);
+        final GenericResultsetData result;
+        Boolean paginationAllowed = BooleanUtil.parseBoolean(queryParams.get(ReportingConstants.isPaginationAllowed));
 
-        final GenericResultsetData result = this.genericDataService.fillGenericResultSet(sql);
+        if (paginationAllowed) {
+            final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+
+            final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors);
+            baseDataValidator.reset().parameter(pageNo).value(queryParams.get(pageNo)).notNull().throwValidationErrors();
+
+            baseDataValidator.reset().parameter(paginationOrderBy).value(queryParams.get(paginationOrderBy)).ignoreIfNull()
+                    .matchesRegularExpression(ORDER_BY_REGEX_PATTERN).throwValidationErrors();
+            Integer pageSize = this.configurationDomainService.reportsPaginationNumberOfItemsPerPage();
+
+            Page<GenericResultsetData> reportData = this.paginationHelper.fetchPage(this.jdbcTemplate, sql, null, new ReportMapper(sql));

Review Comment:
   Just as a note. This will retrieve the data itself plus it will run an extra query to count the resultset.
   This is definitely wasting time since we're never using the dataset but only the count.
   



##########
integration-tests/src/test/java/org/apache/fineract/integrationtest/stretchyreports/StretchyReportHelper.java:
##########
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtest.stretchyreports;
+
+import com.google.gson.Gson;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportHelper {
+
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+
+    private String nameOfReport = "report_test";
+    private String reportType = "Table";
+    private String reportSQL = null;
+    private String useReport = null;
+    List<String> reportParameters = Collections.<String>emptyList();
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportHelper.class);
+    private static final String CREATE_REPORT_URL = "/fineract-provider/api/v1/reports?" + Utils.TENANT_IDENTIFIER;
+
+    public StretchyReportHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {

Review Comment:
   Whats the reason you're passing these as constructor parameters? You are not using it since every method on the helper gets its own specifications in parameters.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/StretchyReportTest.java:
##########
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import org.apache.fineract.integrationtest.stretchyreports.StretchyReportHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportTest.class);
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+    private StretchyReportHelper stretchyReportHelper;
+    private static final String STRETCHY_GET_REPORT_URL = "/fineract-provider/api/v1/reports";
+    private static final String STRETCHY_REPORT_URL = "/fineract-provider/api/v1/runreports";
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testReportPagination() {
+        this.stretchyReportHelper = new StretchyReportHelper(this.requestSpec, this.responseSpec);
+
+        final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(400).build();
+        StretchyReportHelper validationErrorHelper = new StretchyReportHelper(this.requestSpec, errorResponse);
+        for (int i = 0; i < 20; i++) {
+            final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+            ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
+        }
+        String url = STRETCHY_REPORT_URL + "/" + "Client Listing" + "?" + Utils.TENANT_IDENTIFIER + "&R_officeId=1";

Review Comment:
   Why constructing the URL here? Isn't it the job of the Helper you created?



##########
integration-tests/src/test/java/org/apache/fineract/integrationtest/stretchyreports/StretchyReportHelper.java:
##########
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtest.stretchyreports;
+
+import com.google.gson.Gson;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportHelper {
+
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+
+    private String nameOfReport = "report_test";
+    private String reportType = "Table";
+    private String reportSQL = null;
+    private String useReport = null;
+    List<String> reportParameters = Collections.<String>emptyList();
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportHelper.class);
+    private static final String CREATE_REPORT_URL = "/fineract-provider/api/v1/reports?" + Utils.TENANT_IDENTIFIER;
+
+    public StretchyReportHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
+        this.requestSpec = requestSpec;
+        this.responseSpec = responseSpec;
+    }
+
+    public String build() {
+        final HashMap<String, Object> map = new HashMap<>();
+
+        map.put("reportName", this.nameOfReport);
+        map.put("reportSql", this.reportSQL);
+        map.put("reportType", this.reportType);
+        map.put("useReport", this.useReport);
+        map.put("reportParameters", this.reportParameters);
+
+        String reportCreateJson = new Gson().toJson(map);
+        LOG.info("{}", reportCreateJson);
+        return reportCreateJson;
+    }
+
+    public LinkedHashMap getStretchyReportDetail(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,

Review Comment:
   No Map return type please. Use the fineract-client for typesafe APIs like I did in ClientHelper#getClient



##########
integration-tests/src/test/java/org/apache/fineract/integrationtest/stretchyreports/StretchyReportHelper.java:
##########
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtest.stretchyreports;
+
+import com.google.gson.Gson;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportHelper {
+
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+
+    private String nameOfReport = "report_test";
+    private String reportType = "Table";
+    private String reportSQL = null;
+    private String useReport = null;
+    List<String> reportParameters = Collections.<String>emptyList();
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportHelper.class);
+    private static final String CREATE_REPORT_URL = "/fineract-provider/api/v1/reports?" + Utils.TENANT_IDENTIFIER;
+
+    public StretchyReportHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
+        this.requestSpec = requestSpec;
+        this.responseSpec = responseSpec;
+    }
+
+    public String build() {
+        final HashMap<String, Object> map = new HashMap<>();
+
+        map.put("reportName", this.nameOfReport);
+        map.put("reportSql", this.reportSQL);
+        map.put("reportType", this.reportType);
+        map.put("useReport", this.useReport);
+        map.put("reportParameters", this.reportParameters);
+
+        String reportCreateJson = new Gson().toJson(map);
+        LOG.info("{}", reportCreateJson);
+        return reportCreateJson;
+    }
+
+    public LinkedHashMap getStretchyReportDetail(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+            final String url, final String jsonReturn) {
+
+        final LinkedHashMap response = Utils.performServerGet(requestSpec, responseSpec, url, jsonReturn);
+        return response;
+    }
+
+    public Object getStretchyReportDataDetail(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,

Review Comment:
   Same as above.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/StretchyReportTest.java:
##########
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import org.apache.fineract.integrationtest.stretchyreports.StretchyReportHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportTest.class);
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+    private StretchyReportHelper stretchyReportHelper;
+    private static final String STRETCHY_GET_REPORT_URL = "/fineract-provider/api/v1/reports";
+    private static final String STRETCHY_REPORT_URL = "/fineract-provider/api/v1/runreports";
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testReportPagination() {
+        this.stretchyReportHelper = new StretchyReportHelper(this.requestSpec, this.responseSpec);
+
+        final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(400).build();
+        StretchyReportHelper validationErrorHelper = new StretchyReportHelper(this.requestSpec, errorResponse);
+        for (int i = 0; i < 20; i++) {
+            final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+            ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
+        }
+        String url = STRETCHY_REPORT_URL + "/" + "Client Listing" + "?" + Utils.TENANT_IDENTIFIER + "&R_officeId=1";
+        LinkedHashMap reportData = this.stretchyReportHelper.getStretchyReportDetail(this.requestSpec, this.responseSpec, url, "");
+        ArrayList<String> rdata = (ArrayList<String>) reportData.get("data");
+        Integer reportDataSize = rdata.size();
+        Assertions.assertNotNull(reportDataSize);
+
+        Boolean isPaginationAllowed = true;
+        if (isPaginationAllowed) {
+            Integer pageNo = 0;
+            Integer orderBy = 1;
+            Integer pageCount = 0;
+            Integer pageContent = Math.toIntExact(
+                    GlobalConfigurationHelper.getGlobalConfigurationByName(requestSpec, responseSpec, "page-content-limit").getValue());

Review Comment:
   This is the wrong parameter.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/StretchyReportTest.java:
##########
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import org.apache.fineract.integrationtest.stretchyreports.StretchyReportHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportTest.class);
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+    private StretchyReportHelper stretchyReportHelper;
+    private static final String STRETCHY_GET_REPORT_URL = "/fineract-provider/api/v1/reports";
+    private static final String STRETCHY_REPORT_URL = "/fineract-provider/api/v1/runreports";
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testReportPagination() {
+        this.stretchyReportHelper = new StretchyReportHelper(this.requestSpec, this.responseSpec);
+
+        final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(400).build();
+        StretchyReportHelper validationErrorHelper = new StretchyReportHelper(this.requestSpec, errorResponse);
+        for (int i = 0; i < 20; i++) {
+            final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+            ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
+        }
+        String url = STRETCHY_REPORT_URL + "/" + "Client Listing" + "?" + Utils.TENANT_IDENTIFIER + "&R_officeId=1";
+        LinkedHashMap reportData = this.stretchyReportHelper.getStretchyReportDetail(this.requestSpec, this.responseSpec, url, "");
+        ArrayList<String> rdata = (ArrayList<String>) reportData.get("data");
+        Integer reportDataSize = rdata.size();
+        Assertions.assertNotNull(reportDataSize);
+
+        Boolean isPaginationAllowed = true;
+        if (isPaginationAllowed) {

Review Comment:
   Isn't this always evaluating to true?



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/StretchyReportTest.java:
##########
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import org.apache.fineract.integrationtest.stretchyreports.StretchyReportHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportTest.class);
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+    private StretchyReportHelper stretchyReportHelper;
+    private static final String STRETCHY_GET_REPORT_URL = "/fineract-provider/api/v1/reports";
+    private static final String STRETCHY_REPORT_URL = "/fineract-provider/api/v1/runreports";
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testReportPagination() {
+        this.stretchyReportHelper = new StretchyReportHelper(this.requestSpec, this.responseSpec);
+
+        final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(400).build();
+        StretchyReportHelper validationErrorHelper = new StretchyReportHelper(this.requestSpec, errorResponse);
+        for (int i = 0; i < 20; i++) {
+            final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+            ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
+        }
+        String url = STRETCHY_REPORT_URL + "/" + "Client Listing" + "?" + Utils.TENANT_IDENTIFIER + "&R_officeId=1";
+        LinkedHashMap reportData = this.stretchyReportHelper.getStretchyReportDetail(this.requestSpec, this.responseSpec, url, "");
+        ArrayList<String> rdata = (ArrayList<String>) reportData.get("data");
+        Integer reportDataSize = rdata.size();
+        Assertions.assertNotNull(reportDataSize);
+
+        Boolean isPaginationAllowed = true;
+        if (isPaginationAllowed) {
+            Integer pageNo = 0;
+            Integer orderBy = 1;
+            Integer pageCount = 0;
+            Integer pageContent = Math.toIntExact(
+                    GlobalConfigurationHelper.getGlobalConfigurationByName(requestSpec, responseSpec, "page-content-limit").getValue());
+            pageCount = reportDataSize / pageContent;
+            if (reportDataSize % pageContent != 0) {
+                pageCount++;
+            }
+            Integer resultant_report_with_limit_size = 0;

Review Comment:
   This is not conforming with the Java naming guideline.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/StretchyReportTest.java:
##########
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import org.apache.fineract.integrationtest.stretchyreports.StretchyReportHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StretchyReportTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(StretchyReportTest.class);
+    private RequestSpecification requestSpec;
+    private ResponseSpecification responseSpec;
+    private StretchyReportHelper stretchyReportHelper;
+    private static final String STRETCHY_GET_REPORT_URL = "/fineract-provider/api/v1/reports";
+    private static final String STRETCHY_REPORT_URL = "/fineract-provider/api/v1/runreports";
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void testReportPagination() {
+        this.stretchyReportHelper = new StretchyReportHelper(this.requestSpec, this.responseSpec);
+
+        final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(400).build();
+        StretchyReportHelper validationErrorHelper = new StretchyReportHelper(this.requestSpec, errorResponse);
+        for (int i = 0; i < 20; i++) {
+            final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+            ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
+        }
+        String url = STRETCHY_REPORT_URL + "/" + "Client Listing" + "?" + Utils.TENANT_IDENTIFIER + "&R_officeId=1";
+        LinkedHashMap reportData = this.stretchyReportHelper.getStretchyReportDetail(this.requestSpec, this.responseSpec, url, "");
+        ArrayList<String> rdata = (ArrayList<String>) reportData.get("data");
+        Integer reportDataSize = rdata.size();
+        Assertions.assertNotNull(reportDataSize);
+
+        Boolean isPaginationAllowed = true;
+        if (isPaginationAllowed) {
+            Integer pageNo = 0;
+            Integer orderBy = 1;
+            Integer pageCount = 0;
+            Integer pageContent = Math.toIntExact(
+                    GlobalConfigurationHelper.getGlobalConfigurationByName(requestSpec, responseSpec, "page-content-limit").getValue());
+            pageCount = reportDataSize / pageContent;

Review Comment:
   This logic is very much not trivial. Do you mind explaining whats going on here?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org