Improve error handling if reporting service implementation is missing [FINERACT-1173]
This commit is contained in:
committed by
Petri Tuomola
parent
a3ab467278
commit
b96061613b
+8
-3
@@ -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);
|
||||
|
||||
+14
-98
@@ -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";
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+110
@@ -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();
|
||||
}
|
||||
}
|
||||
+15
-16
@@ -30,29 +30,28 @@ import org.apache.fineract.useradministration.domain.AppUser;
|
||||
|
||||
public interface ReadReportingService {
|
||||
|
||||
Collection<ReportData> retrieveReportList();
|
||||
|
||||
ReportData retrieveReport(Long id);
|
||||
|
||||
String getReportType(String reportName, boolean isSelfServiceUserReport);
|
||||
|
||||
Collection<ReportParameterData> getAllowedParameters();
|
||||
|
||||
// TODO Move the following x3 methods into the (new; FINERACT-1173) DatatableReportingProcessService?
|
||||
|
||||
String retrieveReportPDF(String name, String type, Map<String, String> extractedQueryParams, boolean isSelfServiceUserReport);
|
||||
|
||||
StreamingOutput retrieveReportCSV(String name, String type, Map<String, String> extractedQueryParams, boolean isSelfServiceUserReport);
|
||||
|
||||
GenericResultsetData retrieveGenericResultset(String name, String type, Map<String, String> extractedQueryParams,
|
||||
boolean isSelfServiceUserReport);
|
||||
|
||||
String retrieveReportPDF(String name, String type, Map<String, String> extractedQueryParams, boolean isSelfServiceUserReport);
|
||||
|
||||
String getReportType(String reportName, boolean isSelfServiceUserReport);
|
||||
|
||||
Collection<ReportData> retrieveReportList();
|
||||
|
||||
Collection<ReportParameterData> getAllowedParameters();
|
||||
|
||||
ReportData retrieveReport(Long id);
|
||||
|
||||
Collection<String> getAllowedReportTypes();
|
||||
|
||||
// needed for smsCampaign and emailCampaign jobs where securityContext is
|
||||
// null
|
||||
// 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);
|
||||
|
||||
String sqlToRunForSmsEmailCampaign(String name, String type, Map<String, String> queryParams);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
+26
-56
@@ -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() {
|
||||
final GenericResultsetData result = retrieveGenericResultset(name, type, queryParams, isSelfServiceUserReport);
|
||||
final StringBuilder sb = generateCsvFileBuffer(result);
|
||||
|
||||
@Override
|
||||
public void write(final OutputStream out) {
|
||||
try {
|
||||
final InputStream in = new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
final GenericResultsetData result = retrieveGenericResultset(name, type, queryParams, isSelfServiceUserReport);
|
||||
final StringBuilder sb = generateCsvFileBuffer(result);
|
||||
final byte[] outputByte = new byte[4096];
|
||||
Integer readLen = in.read(outputByte, 0, 4096);
|
||||
|
||||
final InputStream in = new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
+6
-5
@@ -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);
|
||||
|
||||
+1
-1
@@ -32,5 +32,5 @@ public @interface ReportService {
|
||||
/**
|
||||
* @return the type of the report
|
||||
*/
|
||||
String type();
|
||||
String[] type();
|
||||
}
|
||||
|
||||
+19
-41
@@ -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 {
|
||||
|
||||
public static final String SERVICE_MISSING = "There is no ReportingProcessService registered in the ReportingProcessServiceProvider for this report type: ";
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ReportingProcessServiceProvider.class);
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
Map<String, String> reportingProcessServices = null;
|
||||
|
||||
ReportingProcessServiceProvider() {
|
||||
private final Map<String, ReportingProcessService> reportingProcessServices;
|
||||
|
||||
@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);
|
||||
}
|
||||
this.reportingProcessServices = mapBuilder.build();
|
||||
}
|
||||
|
||||
public ReportingProcessService findReportingProcessService(final String reportType) {
|
||||
if (this.reportingProcessServices.containsKey(reportType)) {
|
||||
return (ReportingProcessService) this.applicationContext.getBean(this.reportingProcessServices.get(reportType));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
this.initializeRegistry();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user