This is an automated email from the ASF dual-hosted git repository.
ptuomola pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new b960616 Improve error handling if reporting service implementation is
missing [FINERACT-1173]
b960616 is described below
commit b96061613b2987af14af1723ca362f9868ad5b0b
Author: Michael Vorburger <[email protected]>
AuthorDate: Sun Oct 4 19:25:59 2020 +0200
Improve error handling if reporting service implementation is missing
[FINERACT-1173]
---
.../dataqueries/api/ReportsApiResource.java | 11 ++-
.../dataqueries/api/RunreportsApiResource.java | 110 +++------------------
.../service/DatatableReportingProcessService.java | 110 +++++++++++++++++++++
.../dataqueries/service/ReadReportingService.java | 25 +++--
.../service/ReadReportingServiceImpl.java | 82 +++++----------
.../service/ReportWritePlatformServiceImpl.java | 11 ++-
.../report/annotation/ReportService.java | 2 +-
.../provider/ReportingProcessServiceProvider.java | 60 ++++-------
.../report/service/ReportingProcessService.java | 17 ++++
.../ReportMailingJobWritePlatformServiceImpl.java | 2 +-
.../integrationtests/client/ReportsTest.java | 2 +-
11 files changed, 214 insertions(+), 218 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/ReportsApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/ReportsApiResource.java
index 990aef8..b4df23c 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/ReportsApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/ReportsApiResource.java
@@ -51,6 +51,7 @@ import
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSeria
import
org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
import
org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService;
+import
org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
@@ -71,17 +72,19 @@ public class ReportsApiResource {
private final ReadReportingService readReportingService;
private final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
+ private final ReportingProcessServiceProvider
reportingProcessServiceProvider;
@Autowired
public ReportsApiResource(final PlatformSecurityContext context, final
ReadReportingService readReportingService,
final ToApiJsonSerializer<ReportData> toApiJsonSerializer,
final PortfolioCommandSourceWritePlatformService
commandsSourceWritePlatformService,
- final ApiRequestParameterHelper apiRequestParameterHelper) {
+ final ApiRequestParameterHelper apiRequestParameterHelper,
ReportingProcessServiceProvider reportingProcessServiceProvider) {
this.context = context;
this.readReportingService = readReportingService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.commandsSourceWritePlatformService =
commandsSourceWritePlatformService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
+ this.reportingProcessServiceProvider = reportingProcessServiceProvider;
}
@GET
@@ -117,7 +120,8 @@ public class ReportsApiResource {
final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) {
-
result.appendedTemplate(this.readReportingService.getAllowedParameters(),
this.readReportingService.getAllowedReportTypes());
+
result.appendedTemplate(this.readReportingService.getAllowedParameters(),
+
this.reportingProcessServiceProvider.findAllReportingTypes());
}
return this.toApiJsonSerializer.serialize(settings, result,
this.responseDataParameters);
}
@@ -135,7 +139,8 @@ public class ReportsApiResource {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final ReportData result = new ReportData();
-
result.appendedTemplate(this.readReportingService.getAllowedParameters(),
this.readReportingService.getAllowedReportTypes());
+
result.appendedTemplate(this.readReportingService.getAllowedParameters(),
+ this.reportingProcessServiceProvider.findAllReportingTypes());
final ApiRequestJsonSerializationSettings settings =
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, result,
this.responseDataParameters);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java
index d257103..0f0c4c5 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java
@@ -25,10 +25,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
@@ -40,20 +36,14 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.ResponseBuilder;
-import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
-import
org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
-import
org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
-import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
-import
org.apache.fineract.infrastructure.dataqueries.service.GenericDataService;
+import
org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
import
org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService;
import
org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
import
org.apache.fineract.infrastructure.report.service.ReportingProcessService;
import
org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
-import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
@@ -65,20 +55,17 @@ import org.springframework.stereotype.Component;
@Tag(name = "Run Reports", description = "")
public class RunreportsApiResource {
+ public static final String IS_SELF_SERVICE_USER_REPORT_PARAMETER =
"isSelfServiceUserReport";
+
private final PlatformSecurityContext context;
- private final ToApiJsonSerializer<ReportData> toApiJsonSerializer;
private final ReadReportingService readExtraDataAndReportingService;
- private final GenericDataService genericDataService;
private final ReportingProcessServiceProvider
reportingProcessServiceProvider;
@Autowired
public RunreportsApiResource(final PlatformSecurityContext context, final
ReadReportingService readExtraDataAndReportingService,
- final GenericDataService genericDataService, final
ToApiJsonSerializer<ReportData> toApiJsonSerializer,
final ReportingProcessServiceProvider
reportingProcessServiceProvider) {
this.context = context;
this.readExtraDataAndReportingService =
readExtraDataAndReportingService;
- this.genericDataService = genericDataService;
- this.toApiJsonSerializer = toApiJsonSerializer;
this.reportingProcessServiceProvider = reportingProcessServiceProvider;
}
@@ -108,79 +95,26 @@ public class RunreportsApiResource {
@ApiResponse(responseCode = "200", description = "OK", content =
@Content(schema = @Schema(implementation =
RunreportsApiResourceSwagger.RunReportsResponse.class))) })
public Response runReport(@PathParam("reportName") @Parameter(description
= "reportName") final String reportName,
@Context final UriInfo uriInfo,
- @DefaultValue("false") @QueryParam("isSelfServiceUserReport")
@Parameter(description = "isSelfServiceUserReport") final boolean
isSelfServiceUserReport) {
+ @DefaultValue("false")
@QueryParam(IS_SELF_SERVICE_USER_REPORT_PARAMETER) @Parameter(description =
IS_SELF_SERVICE_USER_REPORT_PARAMETER) final boolean isSelfServiceUserReport) {
final MultivaluedMap<String, String> queryParams =
uriInfo.getQueryParameters();
-
- final boolean prettyPrint =
ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
- final boolean exportCsv =
ApiParameterHelper.exportCsv(uriInfo.getQueryParameters());
- final boolean parameterType =
ApiParameterHelper.parameterType(uriInfo.getQueryParameters());
- final boolean exportPdf =
ApiParameterHelper.exportPdf(uriInfo.getQueryParameters());
+ final boolean parameterType =
ApiParameterHelper.parameterType(queryParams);
checkUserPermissionForReport(reportName, parameterType);
- String parameterTypeValue = null;
- if (!parameterType) {
- parameterTypeValue = "report";
- String reportType =
this.readExtraDataAndReportingService.getReportType(reportName,
isSelfServiceUserReport);
- ReportingProcessService reportingProcessService =
this.reportingProcessServiceProvider.findReportingProcessService(reportType);
- if (reportingProcessService != null) {
- return reportingProcessService.processRequest(reportName,
queryParams);
- }
- } else {
- parameterTypeValue = "parameter";
- }
-
- // PDF format
-
- if (exportPdf) {
- final Map<String, String> reportParams =
getReportParams(queryParams);
- final String pdfFileName =
this.readExtraDataAndReportingService.retrieveReportPDF(reportName,
parameterTypeValue, reportParams,
- isSelfServiceUserReport);
-
- final File file = new File(pdfFileName);
-
- final ResponseBuilder response = Response.ok(file);
- response.header("Content-Disposition", "attachment; filename=\"" +
pdfFileName + "\"");
- response.header("content-Type", "application/pdf");
-
- return response.build();
+ // Pass through isSelfServiceUserReport so that
ReportingProcessService implementations can use it
+ queryParams.putSingle(IS_SELF_SERVICE_USER_REPORT_PARAMETER,
Boolean.toString(isSelfServiceUserReport));
+ String reportType =
this.readExtraDataAndReportingService.getReportType(reportName,
isSelfServiceUserReport);
+ ReportingProcessService reportingProcessService =
this.reportingProcessServiceProvider.findReportingProcessService(reportType);
+ if (reportingProcessService == null) {
+ throw new
PlatformServiceUnavailableException("err.msg.report.service.implementation.missing",
+ ReportingProcessServiceProvider.SERVICE_MISSING +
reportType, reportType);
}
-
- if (!exportCsv) {
- final Map<String, String> reportParams =
getReportParams(queryParams);
-
- final GenericResultsetData result =
this.readExtraDataAndReportingService.retrieveGenericResultset(reportName,
- parameterTypeValue, reportParams, isSelfServiceUserReport);
-
- String json = "";
- final boolean genericResultSetIsPassed =
ApiParameterHelper.genericResultSetPassed(uriInfo.getQueryParameters());
- final boolean genericResultSet =
ApiParameterHelper.genericResultSet(uriInfo.getQueryParameters());
- if (genericResultSetIsPassed) {
- if (genericResultSet) {
- json =
this.toApiJsonSerializer.serializePretty(prettyPrint, result);
- } else {
- json =
this.genericDataService.generateJsonFromGenericResultsetData(result);
- }
- } else {
- json = this.toApiJsonSerializer.serializePretty(prettyPrint,
result);
- }
-
- return
Response.ok().entity(json).type(MediaType.APPLICATION_JSON).build();
- }
-
- // CSV Export
- final Map<String, String> reportParams = getReportParams(queryParams);
- final StreamingOutput result =
this.readExtraDataAndReportingService.retrieveReportCSV(reportName,
parameterTypeValue, reportParams,
- isSelfServiceUserReport);
-
- return Response.ok().entity(result).type("text/csv")
- .header("Content-Disposition", "attachment;filename=" +
reportName.replaceAll(" ", "") + ".csv").build();
+ return reportingProcessService.processRequest(reportName, queryParams);
}
private void checkUserPermissionForReport(final String reportName, final
boolean parameterType) {
-
// Anyone can run a 'report' that is simply getting possible parameter
// (dropdown listbox) values.
if (!parameterType) {
@@ -190,22 +124,4 @@ public class RunreportsApiResource {
}
}
}
-
- private Map<String, String> getReportParams(final MultivaluedMap<String,
String> queryParams) {
-
- final Map<String, String> reportParams = new HashMap<>();
- final Set<String> keys = queryParams.keySet();
- String pKey;
- String pValue;
- for (final String k : keys) {
-
- if (k.startsWith("R_")) {
- pKey = "${" + k.substring(2) + "}";
- pValue = queryParams.get(k).get(0);
- SQLInjectionValidator.validateSQLInput(pValue);
- reportParams.put(pKey, pValue);
- }
- }
- return reportParams;
- }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/DatatableReportingProcessService.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/DatatableReportingProcessService.java
new file mode 100644
index 0000000..e7cb156
--- /dev/null
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/DatatableReportingProcessService.java
@@ -0,0 +1,110 @@
+/**
+ * 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;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.StreamingOutput;
+import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
+import
org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
+import
org.apache.fineract.infrastructure.dataqueries.api.RunreportsApiResource;
+import
org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
+import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
+import org.apache.fineract.infrastructure.report.annotation.ReportService;
+import
org.apache.fineract.infrastructure.report.service.ReportingProcessService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@ReportService(type = { "Table", "Chart", "SMS" })
+public class DatatableReportingProcessService implements
ReportingProcessService {
+
+ private final ReadReportingService readExtraDataAndReportingService;
+ private final ToApiJsonSerializer<ReportData> toApiJsonSerializer;
+ private final GenericDataService genericDataService;
+
+ @Autowired
+ public DatatableReportingProcessService(final ReadReportingService
readExtraDataAndReportingService,
+ final GenericDataService genericDataService, final
ToApiJsonSerializer<ReportData> toApiJsonSerializer) {
+ this.readExtraDataAndReportingService =
readExtraDataAndReportingService;
+ this.toApiJsonSerializer = toApiJsonSerializer;
+ this.genericDataService = genericDataService;
+ }
+
+ @Override
+ public Response processRequest(String reportName, MultivaluedMap<String,
String> queryParams) {
+ boolean isSelfServiceUserReport = Boolean.parseBoolean(
+
queryParams.getOrDefault(RunreportsApiResource.IS_SELF_SERVICE_USER_REPORT_PARAMETER,
List.of("false")).get(0));
+ final boolean prettyPrint =
ApiParameterHelper.prettyPrint(queryParams);
+ final boolean exportCsv = ApiParameterHelper.exportCsv(queryParams);
+ final boolean exportPdf = ApiParameterHelper.exportPdf(queryParams);
+ final String parameterTypeValue =
ApiParameterHelper.parameterType(queryParams) ? "parameter" : "report";
+
+ // PDF format
+ if (exportPdf) {
+ final Map<String, String> reportParams =
getReportParams(queryParams);
+ final String pdfFileName =
this.readExtraDataAndReportingService.retrieveReportPDF(reportName,
parameterTypeValue, reportParams,
+ isSelfServiceUserReport);
+
+ final File file = new File(pdfFileName);
+
+ final ResponseBuilder response = Response.ok(file);
+ response.header("Content-Disposition", "attachment; filename=\"" +
pdfFileName + "\"");
+ response.header("content-Type", "application/pdf");
+
+ return response.build();
+ }
+
+ // JSON format
+ if (!exportCsv) {
+ final Map<String, String> reportParams =
getReportParams(queryParams);
+
+ final GenericResultsetData result =
this.readExtraDataAndReportingService.retrieveGenericResultset(reportName,
+ parameterTypeValue, reportParams, isSelfServiceUserReport);
+
+ String json;
+ final boolean genericResultSetIsPassed =
ApiParameterHelper.genericResultSetPassed(queryParams);
+ final boolean genericResultSet =
ApiParameterHelper.genericResultSet(queryParams);
+ if (genericResultSetIsPassed) {
+ if (genericResultSet) {
+ json =
this.toApiJsonSerializer.serializePretty(prettyPrint, result);
+ } else {
+ json =
this.genericDataService.generateJsonFromGenericResultsetData(result);
+ }
+ } else {
+ json = this.toApiJsonSerializer.serializePretty(prettyPrint,
result);
+ }
+
+ return
Response.ok().entity(json).type(MediaType.APPLICATION_JSON).build();
+ }
+
+ // CSV format
+ final Map<String, String> reportParams = getReportParams(queryParams);
+ final StreamingOutput result =
this.readExtraDataAndReportingService.retrieveReportCSV(reportName,
parameterTypeValue, reportParams,
+ isSelfServiceUserReport);
+
+ return Response.ok().entity(result).type("text/csv")
+ .header("Content-Disposition", "attachment;filename=" +
reportName.replaceAll(" ", "") + ".csv").build();
+ }
+}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java
index dd0ef88..cf4b383 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java
@@ -30,29 +30,28 @@ import
org.apache.fineract.useradministration.domain.AppUser;
public interface ReadReportingService {
- StreamingOutput retrieveReportCSV(String name, String type, Map<String,
String> extractedQueryParams, boolean isSelfServiceUserReport);
-
- GenericResultsetData retrieveGenericResultset(String name, String type,
Map<String, String> extractedQueryParams,
- boolean isSelfServiceUserReport);
+ Collection<ReportData> retrieveReportList();
- String retrieveReportPDF(String name, String type, Map<String, String>
extractedQueryParams, boolean isSelfServiceUserReport);
+ ReportData retrieveReport(Long id);
String getReportType(String reportName, boolean isSelfServiceUserReport);
- Collection<ReportData> retrieveReportList();
-
Collection<ReportParameterData> getAllowedParameters();
- ReportData retrieveReport(Long id);
+ // TODO Move the following x3 methods into the (new; FINERACT-1173)
DatatableReportingProcessService?
- Collection<String> getAllowedReportTypes();
+ String retrieveReportPDF(String name, String type, Map<String, String>
extractedQueryParams, boolean isSelfServiceUserReport);
- // needed for smsCampaign and emailCampaign jobs where securityContext is
- // null
- GenericResultsetData retrieveGenericResultSetForSmsEmailCampaign(String
name, String type, Map<String, String> extractedQueryParams);
+ StreamingOutput retrieveReportCSV(String name, String type, Map<String,
String> extractedQueryParams, boolean isSelfServiceUserReport);
- String sqlToRunForSmsEmailCampaign(String name, String type, Map<String,
String> queryParams);
+ GenericResultsetData retrieveGenericResultset(String name, String type,
Map<String, String> extractedQueryParams,
+ boolean isSelfServiceUserReport);
+
+ // TODO This is weird, could they not be using the
retrieveGenericResultset() above after all?
+ // needed for smsCampaign and emailCampaign jobs where securityContext is
null
+ GenericResultsetData retrieveGenericResultSetForSmsEmailCampaign(String
name, String type, Map<String, String> extractedQueryParams);
+ // TODO kill this when tackling
https://issues.apache.org/jira/browse/FINERACT-1264
ByteArrayOutputStream generatePentahoReportAsOutputStream(String
reportName, String outputTypeParam, Map<String, String> queryParams,
Locale locale, AppUser runReportAsUser, StringBuilder errorLog);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java
index 7688caa..43584f3 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java
@@ -27,7 +27,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
-import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -51,7 +50,6 @@ import
org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeader
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetRowData;
import
org.apache.fineract.infrastructure.dataqueries.exception.ReportNotFoundException;
import
org.apache.fineract.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
-import
org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.infrastructure.security.utils.SQLInjectionException;
@@ -74,53 +72,43 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
private final DataSource dataSource;
private final PlatformSecurityContext context;
private final GenericDataService genericDataService;
- private final ReportingProcessServiceProvider
reportingProcessServiceProvider;
private final ColumnValidator columnValidator;
@Autowired
public ReadReportingServiceImpl(final PlatformSecurityContext context,
final RoutingDataSource dataSource,
- final GenericDataService genericDataService, final
ReportingProcessServiceProvider reportingProcessServiceProvider,
- final ColumnValidator columnValidator) {
-
+ final GenericDataService genericDataService, final ColumnValidator
columnValidator) {
this.context = context;
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
this.genericDataService = genericDataService;
- this.reportingProcessServiceProvider = reportingProcessServiceProvider;
this.columnValidator = columnValidator;
}
@Override
public StreamingOutput retrieveReportCSV(final String name, final String
type, final Map<String, String> queryParams,
final boolean isSelfServiceUserReport) {
+ return out -> {
+ try {
- return new StreamingOutput() {
-
- @Override
- public void write(final OutputStream out) {
- try {
-
- final GenericResultsetData result =
retrieveGenericResultset(name, type, queryParams, isSelfServiceUserReport);
- final StringBuilder sb = generateCsvFileBuffer(result);
+ final GenericResultsetData result =
retrieveGenericResultset(name, type, queryParams, isSelfServiceUserReport);
+ final StringBuilder sb = generateCsvFileBuffer(result);
- final InputStream in = new
ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
+ final InputStream in = new
ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
- final byte[] outputByte = new byte[4096];
- Integer readLen = in.read(outputByte, 0, 4096);
+ final byte[] outputByte = new byte[4096];
+ Integer readLen = in.read(outputByte, 0, 4096);
- while (readLen != -1) {
- out.write(outputByte, 0, readLen);
- readLen = in.read(outputByte, 0, 4096);
- }
- // in.close();
- // out.flush();
- // out.close();
- } catch (final Exception e) {
- throw new
PlatformDataIntegrityException("error.msg.exception.error", e.getMessage(), e);
+ while (readLen != -1) {
+ out.write(outputByte, 0, readLen);
+ readLen = in.read(outputByte, 0, 4096);
}
+ // in.close();
+ // out.flush();
+ // out.close();
+ } catch (final Exception e) {
+ throw new
PlatformDataIntegrityException("error.msg.exception.error", e.getMessage(), e);
}
};
-
}
private StringBuilder generateCsvFileBuffer(final GenericResultsetData
result) {
@@ -146,8 +134,8 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
final String doubleQuote = "\"";
final String twoDoubleQuotes = doubleQuote + doubleQuote;
LOG.info("NO. of Rows: {}", data.size());
- for (int i = 0; i < data.size(); i++) {
- row = data.get(i).getRow();
+ for (ResultsetRowData element : data) {
+ row = element.getRow();
rSize = row.size();
for (int j = 0; j < rSize; j++) {
// currCol = columnHeaders.get(j).getColumnName();
@@ -184,7 +172,7 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
final GenericResultsetData result =
this.genericDataService.fillGenericResultSet(sql);
final long elapsed = System.currentTimeMillis() - startTime;
- LOG.info("FINISHING Report/Request Name: {} - {} Elapsed Time:
{}", new Object[] { name, type, elapsed });
+ LOG.info("FINISHING Report/Request Name: {} - {} Elapsed Time:
{}", name, type, elapsed);
return result;
}
@@ -213,11 +201,9 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
sql = this.genericDataService.wrapSQL(sql);
return sql;
-
}
private String getSql(final String name, final String type) {
-
final String inputSql = "select " + type + "_sql as the_sql from
stretchy_" + type + " where " + type + "_name = '" + name + "'";
validateReportName(name);
@@ -234,7 +220,6 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
@Override
public String getReportType(final String reportName, final boolean
isSelfServiceUserReport) {
-
final String sql = "SELECT ifnull(report_type,'') as report_type FROM
`stretchy_report` where report_name = '" + reportName
+ "' and self_service_user_report = ?";
validateReportName(reportName);
@@ -242,7 +227,7 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
final String sqlWrapped = this.genericDataService.wrapSQL(sql);
- final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sqlWrapped, new
Object[] { isSelfServiceUserReport });
+ final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sqlWrapped,
isSelfServiceUserReport);
if (rs.next()) {
return rs.getString("report_type");
@@ -290,8 +275,8 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
String currColType;
String currVal;
LOG.info("NO. of Rows: {}", data.size());
- for (int i = 0; i < data.size(); i++) {
- row = data.get(i).getRow();
+ for (ResultsetRowData element : data) {
+ row = element.getRow();
rSize = row.size();
for (int j = 0; j < rSize; j++) {
currColType = columnHeaders.get(j).getColumnType();
@@ -338,7 +323,7 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
final String sql = rm.schema(id);
- final Collection<ReportParameterJoinData> rpJoins =
this.jdbcTemplate.query(sql, rm, new Object[] {});
+ final Collection<ReportParameterJoinData> rpJoins =
this.jdbcTemplate.query(sql, rm);
final Collection<ReportData> reportList = new ArrayList<>();
if (rpJoins == null || rpJoins.size() == 0) {
@@ -410,22 +395,12 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
@Override
public Collection<ReportParameterData> getAllowedParameters() {
-
final ReportParameterMapper rm = new ReportParameterMapper();
final String sql = rm.schema();
- final Collection<ReportParameterData> parameters =
this.jdbcTemplate.query(sql, rm, new Object[] {});
+ final Collection<ReportParameterData> parameters =
this.jdbcTemplate.query(sql, rm);
return parameters;
}
- @Override
- public Collection<String> getAllowedReportTypes() {
- final List<String> reportTypes = new ArrayList<>();
- reportTypes.add("Table");
- reportTypes.add("Chart");
-
reportTypes.addAll(this.reportingProcessServiceProvider.findAllReportingTypes());
- return reportTypes;
- }
-
private static final class ReportParameterJoinMapper implements
RowMapper<ReportParameterJoinData> {
public String schema(final Long reportId) {
@@ -460,7 +435,6 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
@Override
public ReportParameterJoinData mapRow(final ResultSet rs, final int
rowNum) throws SQLException {
-
final Long reportId = rs.getLong("reportId");
final String reportName = rs.getString("reportName");
final String reportType = rs.getString("reportType");
@@ -491,9 +465,7 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
private static final class ReportParameterMapper implements
RowMapper<ReportParameterData> {
public String schema() {
-
return "select p.id as id, p.parameter_name as parameterName from
stretchy_parameter p where ifnull(p.special,'') != 'Y' order by p.id";
-
}
@Override
@@ -516,12 +488,11 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
final GenericResultsetData result =
this.genericDataService.fillGenericResultSet(sql);
final long elapsed = System.currentTimeMillis() - startTime;
- LOG.info("FINISHING Report/Request Name: {} - {} Elapsed Time:
{}", new Object[] { name, type, elapsed });
+ LOG.info("FINISHING Report/Request Name: {} - {} Elapsed Time:
{}", name, type, elapsed);
return result;
}
- @Override
- public String sqlToRunForSmsEmailCampaign(final String name, final String
type, final Map<String, String> queryParams) {
+ private String sqlToRunForSmsEmailCampaign(final String name, final String
type, final Map<String, String> queryParams) {
String sql = getSql(name, type);
final Set<String> keys = queryParams.keySet();
@@ -599,7 +570,6 @@ public class ReadReportingServiceImpl implements
ReadReportingService {
}
private void validateReportName(final String name) {
-
if (!StringUtils.isBlank(name) &&
!name.matches(REPORT_NAME_REGEX_PATTERN)) {
throw new SQLInjectionException();
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportWritePlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportWritePlatformServiceImpl.java
index 8794b58..35069be 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportWritePlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportWritePlatformServiceImpl.java
@@ -39,6 +39,7 @@ import
org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository;
import
org.apache.fineract.infrastructure.dataqueries.exception.ReportNotFoundException;
import
org.apache.fineract.infrastructure.dataqueries.exception.ReportParameterNotFoundException;
import
org.apache.fineract.infrastructure.dataqueries.serialization.ReportCommandFromApiJsonDeserializer;
+import
org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.useradministration.domain.Permission;
import org.apache.fineract.useradministration.domain.PermissionRepository;
@@ -62,20 +63,20 @@ public class ReportWritePlatformServiceImpl implements
ReportWritePlatformServic
private final ReportParameterUsageRepository
reportParameterUsageRepository;
private final ReportParameterRepository reportParameterRepository;
private final PermissionRepository permissionRepository;
- private final ReadReportingService readReportingService;
+ private final ReportingProcessServiceProvider
reportingProcessServiceProvider;
@Autowired
public ReportWritePlatformServiceImpl(final PlatformSecurityContext
context,
final ReportCommandFromApiJsonDeserializer
fromApiJsonDeserializer, final ReportRepository reportRepository,
final ReportParameterRepository reportParameterRepository, final
ReportParameterUsageRepository reportParameterUsageRepository,
- final PermissionRepository permissionRepository, final
ReadReportingService readReportingService) {
+ final PermissionRepository permissionRepository, final
ReportingProcessServiceProvider reportingProcessServiceProvider) {
this.context = context;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.reportRepository = reportRepository;
this.reportParameterRepository = reportParameterRepository;
this.reportParameterUsageRepository = reportParameterUsageRepository;
this.permissionRepository = permissionRepository;
- this.readReportingService = readReportingService;
+ this.reportingProcessServiceProvider = reportingProcessServiceProvider;
}
@Transactional
@@ -87,7 +88,7 @@ public class ReportWritePlatformServiceImpl implements
ReportWritePlatformServic
this.fromApiJsonDeserializer.validate(command.json());
- final Report report = Report.fromJson(command,
this.readReportingService.getAllowedReportTypes());
+ final Report report = Report.fromJson(command,
this.reportingProcessServiceProvider.findAllReportingTypes());
final Set<ReportParameterUsage> reportParameterUsages =
assembleSetOfReportParameterUsages(report, command);
report.update(reportParameterUsages);
@@ -121,7 +122,7 @@ public class ReportWritePlatformServiceImpl implements
ReportWritePlatformServic
final Report report =
this.reportRepository.findById(reportId).orElseThrow(() -> new
ReportNotFoundException(reportId));
- final Map<String, Object> changes = report.update(command,
this.readReportingService.getAllowedReportTypes());
+ final Map<String, Object> changes = report.update(command,
this.reportingProcessServiceProvider.findAllReportingTypes());
if (changes.containsKey("reportParameters")) {
final Set<ReportParameterUsage> reportParameterUsages =
assembleSetOfReportParameterUsages(report, command);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/annotation/ReportService.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/annotation/ReportService.java
index 8c72b1b..a101e1f 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/annotation/ReportService.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/annotation/ReportService.java
@@ -32,5 +32,5 @@ public @interface ReportService {
/**
* @return the type of the report
*/
- String type();
+ String[] type();
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/provider/ReportingProcessServiceProvider.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/provider/ReportingProcessServiceProvider.java
index c52b1c3..84e812a 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/provider/ReportingProcessServiceProvider.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/provider/ReportingProcessServiceProvider.java
@@ -18,68 +18,46 @@
*/
package org.apache.fineract.infrastructure.report.provider;
+import com.google.common.collect.ImmutableMap;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import org.apache.commons.lang3.ArrayUtils;
import org.apache.fineract.infrastructure.report.annotation.ReportService;
import
org.apache.fineract.infrastructure.report.service.ReportingProcessService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
-public class ReportingProcessServiceProvider implements
ApplicationContextAware {
+public class ReportingProcessServiceProvider {
- private static final Logger LOGGER =
LoggerFactory.getLogger(ReportingProcessServiceProvider.class);
-
- private ApplicationContext applicationContext;
+ public static final String SERVICE_MISSING = "There is no
ReportingProcessService registered in the ReportingProcessServiceProvider for
this report type: ";
- Map<String, String> reportingProcessServices = null;
+ private static final Logger LOGGER =
LoggerFactory.getLogger(ReportingProcessServiceProvider.class);
- ReportingProcessServiceProvider() {
+ private final Map<String, ReportingProcessService>
reportingProcessServices;
- }
-
- public ReportingProcessService findReportingProcessService(final String
reportType) {
- if (this.reportingProcessServices.containsKey(reportType)) {
- return (ReportingProcessService)
this.applicationContext.getBean(this.reportingProcessServices.get(reportType));
+ @Autowired
+ public ReportingProcessServiceProvider(List<ReportingProcessService>
reportingProcessServices) {
+ var mapBuilder = ImmutableMap.<String,
ReportingProcessService>builder();
+ for (ReportingProcessService s : reportingProcessServices) {
+ String[] reportTypes =
s.getClass().getAnnotation(ReportService.class).type();
+ for (String type : reportTypes) {
+ mapBuilder.put(type, s);
+ }
+ LOGGER.info("Registered report service '{}' for type/s '{}'", s,
reportTypes);
}
- return null;
+ this.reportingProcessServices = mapBuilder.build();
}
- @Override
- public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
- this.applicationContext = applicationContext;
- this.initializeRegistry();
+ public ReportingProcessService findReportingProcessService(final String
reportType) {
+ return reportingProcessServices.getOrDefault(reportType, null);
}
public Collection<String> findAllReportingTypes() {
return this.reportingProcessServices.keySet();
-
- }
-
- private void initializeRegistry() {
- if (this.reportingProcessServices == null) {
- this.reportingProcessServices = new HashMap<>();
-
- final String[] reportServiceBeans =
this.applicationContext.getBeanNamesForAnnotation(ReportService.class);
- if (ArrayUtils.isNotEmpty(reportServiceBeans)) {
- for (final String reportName : reportServiceBeans) {
- LOGGER.info("Register report service '{}' ...",
reportName);
- final ReportService service =
this.applicationContext.findAnnotationOnBean(reportName, ReportService.class);
- try {
- this.reportingProcessServices.put(service.type(),
reportName);
- } catch (final Throwable th) {
- LOGGER.error("Unable to register reporting service
'{}'!", reportName, th);
- }
- }
- }
- }
}
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java
index 990bd0d..406157f 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java
@@ -18,11 +18,28 @@
*/
package org.apache.fineract.infrastructure.report.service;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
+import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
public interface ReportingProcessService {
Response processRequest(String reportName, MultivaluedMap<String, String>
queryParams);
+ default Map<String, String> getReportParams(final MultivaluedMap<String,
String> queryParams) {
+ final Map<String, String> reportParams = new HashMap<>();
+ final Set<String> keys = queryParams.keySet();
+ for (final String k : keys) {
+ if (k.startsWith("R_")) {
+ String pKey = "${" + k.substring(2) + "}";
+ String pValue = queryParams.get(k).get(0);
+ SQLInjectionValidator.validateSQLInput(pValue);
+ reportParams.put(pKey, pValue);
+ }
+ }
+ return reportParams;
+ }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
index 47d45ec..1de29e3 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
@@ -471,7 +471,7 @@ public class ReportMailingJobWritePlatformServiceImpl
implements ReportMailingJo
errorLog.append("Response object entity is not equal to
ByteArrayOutputStream ---------- ");
}
} else {
- errorLog.append("ReportingProcessService object is null
---------- ");
+
errorLog.append(ReportingProcessServiceProvider.SERVICE_MISSING + reportType);
}
} catch (Exception e) {
errorLog.append("The
ReportMailingJobWritePlatformServiceImpl.generateReportOutputStream method
threw an Exception: " + e
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
index 8eebfe9..03a1157 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
@@ -45,7 +45,7 @@ public class ReportsTest extends IntegrationTest {
@Test
void runExpectedPaymentsPentahoReportWithoutPlugin() {
assertThat(fineract().reportsRun.runReportGetFile("Expected Payments
By Date - Formatted", Map.of("R_endDate", "2013-04-30",
- "R_loanOfficerId", "-1", "R_officeId", "1", "R_startDate",
"2013-04-16", "output-type", "PDF"), false)).hasHttpStatus(404);
+ "R_loanOfficerId", "-1", "R_officeId", "1", "R_startDate",
"2013-04-16", "output-type", "PDF"), false)).hasHttpStatus(503);
}
@Test