fineract-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From my...@apache.org
Subject [fineract-cn-reporting] 03/24: added first report
Date Mon, 22 Jan 2018 15:31:00 GMT
This is an automated email from the ASF dual-hosted git repository.

myrle pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract-cn-reporting.git

commit dc8dc1f2522d106a86a804e296af1802ed285587
Author: mgeiss <mgeiss@mifos.org>
AuthorDate: Thu Jul 6 11:04:00 2017 +0200

    added first report
---
 .../reporting/api/v1/PermittableGroupIds.java      |   3 +-
 .../reporting/api/v1/client/ReportManager.java     |  14 ++
 .../mifos/reporting/api/v1/domain/ReportPage.java  |  19 +-
 .../io/mifos/reporting/api/v1/domain/Value.java    |  10 +-
 service/build.gradle                               |   3 +-
 .../reporting/service/ReportingConfiguration.java  |   7 +-
 .../provider/ReportSpecificationProvider.java      |  12 ++
 .../CustomerListReportSpecification.java           | 197 +++++++++++++++++++--
 .../service/rest/ReportingRestController.java      |  24 ++-
 .../reporting/service/spi/CriteriaBuilder.java     |  83 +++++++++
 .../reporting/service/spi/ReportSpecification.java |   2 +-
 11 files changed, 327 insertions(+), 47 deletions(-)

diff --git a/api/src/main/java/io/mifos/reporting/api/v1/PermittableGroupIds.java b/api/src/main/java/io/mifos/reporting/api/v1/PermittableGroupIds.java
index d0a3bed..8e28149 100644
--- a/api/src/main/java/io/mifos/reporting/api/v1/PermittableGroupIds.java
+++ b/api/src/main/java/io/mifos/reporting/api/v1/PermittableGroupIds.java
@@ -17,6 +17,5 @@ package io.mifos.reporting.api.v1;
 
 @SuppressWarnings("unused")
 public interface PermittableGroupIds {
-  String SAMPLE_MANAGEMENT = "reporting__v1__samples";
-  String SELF_MANAGEMENT = "reporting__v1__self";
+  String REPORT_MANAGEMENT = "reporting__v1__general";
 }
diff --git a/api/src/main/java/io/mifos/reporting/api/v1/client/ReportManager.java b/api/src/main/java/io/mifos/reporting/api/v1/client/ReportManager.java
index 50a1b6c..950ba6b 100644
--- a/api/src/main/java/io/mifos/reporting/api/v1/client/ReportManager.java
+++ b/api/src/main/java/io/mifos/reporting/api/v1/client/ReportManager.java
@@ -18,12 +18,15 @@ package io.mifos.reporting.api.v1.client;
 import io.mifos.core.api.annotation.ThrowsException;
 import io.mifos.core.api.annotation.ThrowsExceptions;
 import io.mifos.core.api.util.CustomFeignClientsConfiguration;
+import io.mifos.core.lang.ServiceException;
+import io.mifos.reporting.api.v1.PermittableGroupIds;
 import io.mifos.reporting.api.v1.domain.ReportDefinition;
 import io.mifos.reporting.api.v1.domain.ReportPage;
 import io.mifos.reporting.api.v1.domain.ReportRequest;
 import org.springframework.cloud.netflix.feign.FeignClient;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -69,4 +72,15 @@ public interface ReportManager {
                             @RequestBody final ReportRequest reportRequest,
                             @RequestParam(value = "pageIndex", required = false) final Integer
pageIndex,
                             @RequestParam(value = "size", required = false) final Integer
size);
+
+  @RequestMapping(
+      value = "categories/{category}/definitions/{identifier}",
+      method = RequestMethod.GET,
+      produces = MediaType.ALL_VALUE,
+      consumes = MediaType.APPLICATION_JSON_VALUE)
+  @ThrowsExceptions({
+      @ThrowsException(status = HttpStatus.NOT_FOUND, exception = ReportNotFoundException.class)
+  })
+  ReportDefinition findReportDefinition(@PathVariable("category") final String category,
+                                        @PathVariable("identifier") final String identifier);
 }
diff --git a/api/src/main/java/io/mifos/reporting/api/v1/domain/ReportPage.java b/api/src/main/java/io/mifos/reporting/api/v1/domain/ReportPage.java
index 90db8f7..2bd088f 100644
--- a/api/src/main/java/io/mifos/reporting/api/v1/domain/ReportPage.java
+++ b/api/src/main/java/io/mifos/reporting/api/v1/domain/ReportPage.java
@@ -26,8 +26,7 @@ public class ReportPage {
   private Header header;
   private List<Row> rows;
   private Footer footer;
-  private Integer totalPages;
-  private Long totalElements;
+  private boolean hasMore;
 
   public ReportPage() {
     super();
@@ -89,19 +88,11 @@ public class ReportPage {
     this.footer = footer;
   }
 
-  public Integer getTotalPages() {
-    return this.totalPages;
+  public void setHasMore(final boolean hasMore) {
+    this.hasMore = hasMore;
   }
 
-  public void setTotalPages(final Integer totalPages) {
-    this.totalPages = totalPages;
-  }
-
-  public Long getTotalElements() {
-    return this.totalElements;
-  }
-
-  public void setTotalElements(final Long totalElements) {
-    this.totalElements = totalElements;
+  public boolean isHasMore() {
+    return hasMore;
   }
 }
diff --git a/api/src/main/java/io/mifos/reporting/api/v1/domain/Value.java b/api/src/main/java/io/mifos/reporting/api/v1/domain/Value.java
index a94be09..6887781 100644
--- a/api/src/main/java/io/mifos/reporting/api/v1/domain/Value.java
+++ b/api/src/main/java/io/mifos/reporting/api/v1/domain/Value.java
@@ -17,19 +17,19 @@ package io.mifos.reporting.api.v1.domain;
 
 public class Value {
 
-  private String value;
+  private String[] values;
   private Type type;
 
   public Value() {
     super();
   }
 
-  public String getValue() {
-    return this.value;
+  public String[] getValues() {
+    return this.values;
   }
 
-  public void setValue(final String value) {
-    this.value = value;
+  public void setValues(final String[] values) {
+    this.values = values;
   }
 
   public Type getType() {
diff --git a/service/build.gradle b/service/build.gradle
index 9b9708a..7f74364 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -38,7 +38,8 @@ dependencies {
             [group: 'io.mifos.core', name: 'cassandra', version: versions.frameworkcassandra],
             [group: 'io.mifos.core', name: 'mariadb', version: versions.frameworkmariadb],
             [group: 'io.mifos.core', name: 'command', version: versions.frameworkcommand],
-            [group: 'org.hibernate', name: 'hibernate-validator', version: versions.validator]
+            [group: 'org.hibernate', name: 'hibernate-validator', version: versions.validator],
+            [group: 'org.owasp.esapi', name: 'esapi', version: '2.1.0.1']
     )
 }
 
diff --git a/service/src/main/java/io/mifos/reporting/service/ReportingConfiguration.java
b/service/src/main/java/io/mifos/reporting/service/ReportingConfiguration.java
index f0ab979..6bf4fed 100644
--- a/service/src/main/java/io/mifos/reporting/service/ReportingConfiguration.java
+++ b/service/src/main/java/io/mifos/reporting/service/ReportingConfiguration.java
@@ -27,7 +27,6 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
 import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
@@ -42,11 +41,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
 @EnableServiceException
 @ComponentScan({
     "io.mifos.reporting.service.rest",
-    "io.mifos.reporting.service.internal",
-    "io.mifos.reporting.service.repository"
-})
-@EnableJpaRepositories({
-    "io.mifos.reporting.service.repository"
+    "io.mifos.reporting.service.internal"
 })
 public class ReportingConfiguration extends WebMvcConfigurerAdapter {
 
diff --git a/service/src/main/java/io/mifos/reporting/service/internal/provider/ReportSpecificationProvider.java
b/service/src/main/java/io/mifos/reporting/service/internal/provider/ReportSpecificationProvider.java
index 7ef4871..3c9b646 100644
--- a/service/src/main/java/io/mifos/reporting/service/internal/provider/ReportSpecificationProvider.java
+++ b/service/src/main/java/io/mifos/reporting/service/internal/provider/ReportSpecificationProvider.java
@@ -92,4 +92,16 @@ public class ReportSpecificationProvider implements ApplicationContextAware
{
   private String buildKeyForSpecificationCache(final String category, final String identifier)
{
     return category + "~" + identifier;
   }
+
+  public Optional<ReportDefinition> findReportDefinition(final String category, final
String identifier) {
+    final List<ReportDefinition> reportDefinitions = this.reportCategoryCache.get(category);
+    if (reportDefinitions != null) {
+      return reportDefinitions
+          .stream()
+          .filter(reportDefinition -> reportDefinition.getIdentifier().equals(identifier))
+          .findAny();
+    } else {
+      return Optional.empty();
+    }
+  }
 }
diff --git a/service/src/main/java/io/mifos/reporting/service/internal/specification/CustomerListReportSpecification.java
b/service/src/main/java/io/mifos/reporting/service/internal/specification/CustomerListReportSpecification.java
index b166b53..f56657f 100644
--- a/service/src/main/java/io/mifos/reporting/service/internal/specification/CustomerListReportSpecification.java
+++ b/service/src/main/java/io/mifos/reporting/service/internal/specification/CustomerListReportSpecification.java
@@ -15,13 +15,19 @@
  */
 package io.mifos.reporting.service.internal.specification;
 
+import io.mifos.core.api.util.UserContextHolder;
+import io.mifos.core.lang.DateConverter;
 import io.mifos.reporting.api.v1.domain.DisplayableField;
+import io.mifos.reporting.api.v1.domain.Header;
 import io.mifos.reporting.api.v1.domain.QueryParameter;
 import io.mifos.reporting.api.v1.domain.ReportDefinition;
 import io.mifos.reporting.api.v1.domain.ReportPage;
 import io.mifos.reporting.api.v1.domain.ReportRequest;
+import io.mifos.reporting.api.v1.domain.Row;
 import io.mifos.reporting.api.v1.domain.Type;
+import io.mifos.reporting.api.v1.domain.Value;
 import io.mifos.reporting.service.ServiceConstants;
+import io.mifos.reporting.service.spi.CriteriaBuilder;
 import io.mifos.reporting.service.spi.DisplayableFieldBuilder;
 import io.mifos.reporting.service.spi.QueryParameterBuilder;
 import io.mifos.reporting.service.spi.Report;
@@ -30,6 +36,11 @@ import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.text.DecimalFormat;
+import java.time.Clock;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -40,12 +51,17 @@ import java.util.stream.Collectors;
 public class CustomerListReportSpecification implements ReportSpecification {
 
   private final Logger logger;
-  private final HashMap<String, String> columnMapping = new HashMap<>();
+  private final EntityManager entityManager;
+  private final HashMap<String, String> customerColumnMapping = new HashMap<>();
+  private final HashMap<String, String> addressColumnMapping = new HashMap<>();
+  private final HashMap<String, String> accountColumnMapping = new HashMap<>();
 
   @Autowired
-  public CustomerListReportSpecification(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger
logger) {
+  public CustomerListReportSpecification(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger
logger,
+                                         final EntityManager entityManager) {
     super();
     this.logger = logger;
+    this.entityManager = entityManager;
     this.initializeMapping();
   }
 
@@ -61,21 +77,40 @@ public class CustomerListReportSpecification implements ReportSpecification
{
   }
 
   @Override
-  public ReportPage generateReport(final ReportRequest reportRequest) {
-    return null;
+  public ReportPage generateReport(final ReportRequest reportRequest, final int pageIndex,
final int size) {
+    final ReportDefinition reportDefinition = this.getReportDefinition();
+    this.logger.info("Generating report {0}.", reportDefinition.getIdentifier());
+
+    final ReportPage reportPage = new ReportPage();
+    reportPage.setName(reportDefinition.getName());
+    reportPage.setDescription(reportDefinition.getDescription());
+    reportPage.setHeader(this.createHeader(reportRequest.getDisplayableFields()));
+
+    final Query customerQuery = this.entityManager.createNativeQuery(this.buildCustomerQuery(reportRequest,
pageIndex, size));
+    final List<?> customerResultList =  customerQuery.getResultList();
+    reportPage.setRows(this.buildRows(reportRequest, customerResultList));
+
+    reportPage.setHasMore(
+        !this.entityManager.createNativeQuery(this.buildCustomerQuery(reportRequest, pageIndex
+ 1, size))
+            .getResultList().isEmpty()
+    );
+
+    reportPage.setGeneratedBy(UserContextHolder.checkedGetUser());
+    reportPage.setGeneratedOn(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
+    return reportPage;
   }
 
   @Override
   public void validate(final ReportRequest reportRequest) throws IllegalArgumentException
{
     final ArrayList<String> unknownFields =  new ArrayList<>();
     reportRequest.getQueryParameters().forEach(queryParameter -> {
-      if (!this.columnMapping.keySet().contains(queryParameter.getName())) {
+      if (!this.customerColumnMapping.keySet().contains(queryParameter.getName())) {
         unknownFields.add(queryParameter.getName());
       }
     });
 
     reportRequest.getDisplayableFields().forEach(displayableField -> {
-      if (!this.columnMapping.keySet().contains(displayableField.getName())) {
+      if (!this.customerColumnMapping.keySet().contains(displayableField.getName())) {
         unknownFields.add(displayableField.getName());
       }
     });
@@ -88,15 +123,71 @@ public class CustomerListReportSpecification implements ReportSpecification
{
   }
 
   private void initializeMapping() {
-    this.columnMapping.put("Date Range", "createdBy");
-    this.columnMapping.put("State", "currentState");
-    this.columnMapping.put("Customer", "identifier");
-    this.columnMapping.put("Account number", "identifier");
-    this.columnMapping.put("First name", "givenName");
-    this.columnMapping.put("Middle Name", "middleName");
-    this.columnMapping.put("Last Name", "surname");
-    this.columnMapping.put("Balance", "balance");
-    this.columnMapping.put("Address", "address");
+    this.customerColumnMapping.put("Date Range", "cst.created_on");
+    this.customerColumnMapping.put("State", "cst.current_state");
+    this.customerColumnMapping.put("Customer", "cst.identifier");
+    this.customerColumnMapping.put("First name", "cst.given_name");
+    this.customerColumnMapping.put("Middle Name", "cst.middle_name");
+    this.customerColumnMapping.put("Last Name", "cst.surname");
+
+    this.accountColumnMapping.put("Account number", "acc.identifier, acc.balance");
+
+    this.addressColumnMapping.put("Address", "CONCAT(adr.street, ', ', adr.postal_code, ',
', adr.city)");
+  }
+
+  private Header createHeader(final List<DisplayableField> displayableFields) {
+    final Header header = new Header();
+    header.setColumnNames(
+        displayableFields
+            .stream()
+            .map(DisplayableField::getName)
+            .collect(Collectors.toList())
+    );
+    return header;
+  }
+
+  private List<Row> buildRows(final ReportRequest reportRequest, final List<?>
customerResultList) {
+    final ArrayList<Row> rows = new ArrayList<>();
+
+    customerResultList.forEach(result -> {
+      final Row row = new Row();
+      row.setValues(new ArrayList<>());
+      final Object[] resultValues = (Object[]) result;
+      for (final Object resultValue : resultValues) {
+        final Value value = new Value();
+        value.setValues(new String[]{resultValue.toString()});
+        row.getValues().add(value);
+      }
+
+      final DecimalFormat decimalFormat = new DecimalFormat(".##");
+      final Query accountQuery = this.entityManager.createNativeQuery(this.buildAccountQuery(reportRequest,
resultValues[0].toString()));
+      final List<?> accountResultList = accountQuery.getResultList();
+      final ArrayList<String> values = new ArrayList<>();
+      accountResultList.forEach(accountResult -> {
+        if (accountResult instanceof Object[]) {
+          final Object[] accountResultValues = (Object[]) accountResult;
+          final String accountValue = accountResultValues[0].toString() + " (" +
+              decimalFormat.format(Double.valueOf(accountResultValues[1].toString())) + ")";
+          values.add(accountValue);
+        }
+      });
+      final Value accountValue = new Value();
+      accountValue.setValues(values.toArray(new String[values.size()]));
+      row.getValues().add(accountValue);
+
+      final String addressQueryString = this.buildAddressQuery(reportRequest, resultValues[0].toString());
+      if (addressQueryString != null) {
+        final Query addressQuery = this.entityManager.createNativeQuery(addressQueryString);
+        final List<?> resultList = addressQuery.getResultList();
+        final Value addressValue = new Value();
+        addressValue.setValues(new String[]{resultList.get(0).toString()});
+        row.getValues().add(addressValue);
+      }
+
+      rows.add(row);
+    });
+
+    return rows;
   }
 
   private List<QueryParameter> buildQueryParameters() {
@@ -113,8 +204,82 @@ public class CustomerListReportSpecification implements ReportSpecification
{
         DisplayableFieldBuilder.create("Middle name", Type.TEXT).build(),
         DisplayableFieldBuilder.create("Last name", Type.TEXT).build(),
         DisplayableFieldBuilder.create("Account number", Type.TEXT).mandatory().build(),
-        DisplayableFieldBuilder.create("Balance", Type.NUMBER).build(),
         DisplayableFieldBuilder.create("Address", Type.TEXT).build()
     );
   }
+
+  private String buildCustomerQuery(final ReportRequest reportRequest, int pageIndex, int
size) {
+    final StringBuilder query = new StringBuilder("SELECT ");
+
+    final List<DisplayableField> displayableFields = reportRequest.getDisplayableFields();
+    final ArrayList<String> columns = new ArrayList<>();
+    displayableFields.forEach(displayableField -> {
+      final String column = this.customerColumnMapping.get(displayableField.getName());
+      if (column != null) {
+        columns.add(column);
+      }
+    });
+
+    query.append(columns.stream().collect(Collectors.joining(", ")))
+        .append(" FROM ")
+        .append("maat_customers cst ");
+
+    final List<QueryParameter> queryParameters = reportRequest.getQueryParameters();
+    if (!queryParameters.isEmpty()) {
+      query.append(" WHERE ");
+      final ArrayList<String> criteria = new ArrayList<>();
+      queryParameters.forEach(queryParameter -> criteria.add(
+          CriteriaBuilder.buildCriteria(this.customerColumnMapping.get(queryParameter.getName()),
queryParameter))
+      );
+      query.append(criteria.stream().collect(Collectors.joining(" AND ")));
+    }
+    query.append(" ORDER BY cst.identifier");
+
+    query.append(" LIMIT ");
+    query.append(size);
+    if (pageIndex > 0) {
+      query.append(" OFFSET ");
+      query.append(size * pageIndex);
+    }
+
+    return query.toString();
+  }
+
+  private String buildAccountQuery(final ReportRequest reportRequest, final String customerIdentifier)
{
+    final List<DisplayableField> displayableFields = reportRequest.getDisplayableFields();
+    final ArrayList<String> columns = new ArrayList<>();
+    displayableFields.forEach(displayableField -> {
+      final String column = this.accountColumnMapping.get(displayableField.getName());
+      if (column != null) {
+        columns.add(column);
+      }
+    });
+
+    return "SELECT " + columns.stream().collect(Collectors.joining(", ")) + " " +
+        "FROM thoth_accounts acc " +
+            "LEFT JOIN maat_customers cst on acc.holders = cst.identifier " +
+        "WHERE cst.identifier ='" + customerIdentifier + "' " +
+        "ORDER BY acc.identifier";
+  }
+
+  private String buildAddressQuery(final ReportRequest reportRequest, final String customerIdentifier)
{
+
+    final List<DisplayableField> displayableFields = reportRequest.getDisplayableFields();
+    final ArrayList<String> columns = new ArrayList<>();
+    displayableFields.forEach(displayableField -> {
+      final String column = this.addressColumnMapping.get(displayableField.getName());
+      if (column != null) {
+        columns.add(column);
+      }
+    });
+
+    if (!columns.isEmpty()) {
+      return "SELECT " + columns.stream().collect(Collectors.joining(", ")) + " " +
+          "FROM thoth_accounts acc " +
+              "LEFT JOIN maat_customers cst on acc.holders = cst.identifier " +
+          "WHERE cst.identifier ='" + customerIdentifier + "' " +
+          "ORDER BY acc.identifier";
+    }
+    return null;
+  }
 }
diff --git a/service/src/main/java/io/mifos/reporting/service/rest/ReportingRestController.java
b/service/src/main/java/io/mifos/reporting/service/rest/ReportingRestController.java
index 16307c5..a23e355 100644
--- a/service/src/main/java/io/mifos/reporting/service/rest/ReportingRestController.java
+++ b/service/src/main/java/io/mifos/reporting/service/rest/ReportingRestController.java
@@ -18,6 +18,7 @@ package io.mifos.reporting.service.rest;
 import io.mifos.anubis.annotation.AcceptedTokenType;
 import io.mifos.anubis.annotation.Permittable;
 import io.mifos.core.lang.ServiceException;
+import io.mifos.reporting.api.v1.PermittableGroupIds;
 import io.mifos.reporting.api.v1.domain.ReportDefinition;
 import io.mifos.reporting.api.v1.domain.ReportPage;
 import io.mifos.reporting.api.v1.domain.ReportRequest;
@@ -66,9 +67,10 @@ public class ReportingRestController {
   public
   @ResponseBody
   ResponseEntity<Void> initialize() {
-    return ResponseEntity.accepted().build();
+    return ResponseEntity.ok().build();
   }
 
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.REPORT_MANAGEMENT)
   @RequestMapping(
       value = "/categories",
       method = RequestMethod.GET,
@@ -80,6 +82,7 @@ public class ReportingRestController {
     return ResponseEntity.ok(this.reportSpecificationProvider.getAvailableCategories());
   }
 
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.REPORT_MANAGEMENT)
   @RequestMapping(
       value = "categories/{category}",
       method = RequestMethod.GET,
@@ -90,6 +93,7 @@ public class ReportingRestController {
     return ResponseEntity.ok(this.reportSpecificationProvider.getAvailableReports(category));
   }
 
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.REPORT_MANAGEMENT)
   @RequestMapping(
       value = "/categories/{category}/reports/{identifier}",
       method = RequestMethod.POST,
@@ -114,9 +118,25 @@ public class ReportingRestController {
         throw ServiceException.badRequest(iaex.getMessage());
       }
 
-      return ResponseEntity.ok(reportSpecification.generateReport(reportRequest));
+      return ResponseEntity.ok(reportSpecification.generateReport(reportRequest, pageIndex,
size));
     } else {
       throw ServiceException.notFound("Report {0} not found.", identifier);
     }
   }
+
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.REPORT_MANAGEMENT)
+  @RequestMapping(
+      value = "categories/{category}/definitions/{identifier}",
+      method = RequestMethod.GET,
+      produces = MediaType.APPLICATION_JSON_VALUE,
+      consumes = MediaType.ALL_VALUE)
+  public
+  ResponseEntity<ReportDefinition> findReportDefinition(
+      @PathVariable("category") final String category,
+      @PathVariable("identifier") final String identifier) {
+    return ResponseEntity.ok(
+        this.reportSpecificationProvider.findReportDefinition(category, identifier)
+            .orElseThrow(() -> ServiceException.notFound("Report definition {0} not found.",
identifier))
+    );
+  }
 }
diff --git a/service/src/main/java/io/mifos/reporting/service/spi/CriteriaBuilder.java b/service/src/main/java/io/mifos/reporting/service/spi/CriteriaBuilder.java
new file mode 100644
index 0000000..a0dc841
--- /dev/null
+++ b/service/src/main/java/io/mifos/reporting/service/spi/CriteriaBuilder.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * 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 io.mifos.reporting.service.spi;
+
+import io.mifos.reporting.api.v1.domain.QueryParameter;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Encoder;
+import org.owasp.esapi.codecs.MySQLCodec;
+import org.springframework.util.StringUtils;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class CriteriaBuilder {
+
+  // https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet
+  private static final Encoder ENCODER = ESAPI.encoder();
+  private static final MySQLCodec MY_SQL_CODEC = new MySQLCodec(MySQLCodec.Mode.ANSI);
+
+  private CriteriaBuilder() {
+    super();
+  }
+
+  public static String buildCriteria(final String field, final QueryParameter queryParameter)
{
+    final StringBuilder criteria = new StringBuilder(field);
+
+    switch (queryParameter.getOperator()) {
+      case EQUALS:
+        criteria.append(" = '");
+        criteria.append(CriteriaBuilder.ENCODER.encodeForSQL(CriteriaBuilder.MY_SQL_CODEC,
queryParameter.getValue()));
+        criteria.append("'");
+        break;
+      case LIKE:
+        criteria.append(" LIKE '%");
+        criteria.append(CriteriaBuilder.ENCODER.encodeForSQL(CriteriaBuilder.MY_SQL_CODEC,
queryParameter.getValue()));
+        criteria.append("%'");
+        break;
+      case GREATER:
+        criteria.append(" > '");
+        criteria.append(CriteriaBuilder.ENCODER.encodeForSQL(CriteriaBuilder.MY_SQL_CODEC,
queryParameter.getValue()));
+        criteria.append("'");
+        break;
+      case LESSER:
+        criteria.append(" < '");
+        criteria.append(CriteriaBuilder.ENCODER.encodeForSQL(CriteriaBuilder.MY_SQL_CODEC,
queryParameter.getValue()));
+        criteria.append("'");
+        break;
+      case IN:
+        criteria.append(" in (");
+        final Set<String> strings = StringUtils.commaDelimitedListToSet(queryParameter.getValue());
+        criteria.append(
+            strings
+                .stream()
+                .map(s -> "'" + CriteriaBuilder.ENCODER.encodeForSQL(CriteriaBuilder.MY_SQL_CODEC,
s) + "'")
+                .collect(Collectors.joining(","))
+        );
+        break;
+      case BETWEEN:
+        final String[] splitString = queryParameter.getValue().split("\\.\\.");
+        criteria.append(" BETWEEN '");
+        criteria.append(CriteriaBuilder.ENCODER.encodeForSQL(CriteriaBuilder.MY_SQL_CODEC,
splitString[0]));
+        criteria.append("' AND '");
+        criteria.append(CriteriaBuilder.ENCODER.encodeForSQL(CriteriaBuilder.MY_SQL_CODEC,
splitString[1]));
+        criteria.append("'");
+        break;
+    }
+
+    return criteria.toString();
+  }
+}
diff --git a/service/src/main/java/io/mifos/reporting/service/spi/ReportSpecification.java
b/service/src/main/java/io/mifos/reporting/service/spi/ReportSpecification.java
index 3502b94..0508411 100644
--- a/service/src/main/java/io/mifos/reporting/service/spi/ReportSpecification.java
+++ b/service/src/main/java/io/mifos/reporting/service/spi/ReportSpecification.java
@@ -23,7 +23,7 @@ public interface ReportSpecification {
 
   ReportDefinition getReportDefinition();
 
-  ReportPage generateReport(final ReportRequest reportRequest);
+  ReportPage generateReport(final ReportRequest reportRequest, int pageIndex, int size);
 
   void validate(final ReportRequest reportRequest) throws IllegalArgumentException;
 }

-- 
To stop receiving notification emails like this one, please contact
myrle@apache.org.

Mime
View raw message