Merge branch 'PR432' into develop

This commit is contained in:
Nazeer Hussain Shaik
2017-12-11 15:30:14 +05:30
10 changed files with 272 additions and 44 deletions
@@ -25,7 +25,10 @@ import java.util.Set;
* Enum of all parameters passed in while creating/updating a AdHocQuery
***/
public enum AdHocJsonInputParams {
ID("id"), NAME("name"),QUERY("query"),TABLENAME("tableName"),TABLEFIELD("tableFields"), ISACTIVE("isActive"),EMAIL("email");
ID("id"), NAME("name"),QUERY("query"),TABLENAME("tableName"), TABLEFIELDS("tableFields"), ISACTIVE("isActive"),
REPORT_RUN_FREQUENCY("reportRunFrequency"),
REPORT_RUN_EVERY("reportRunEvery"),
EMAIL("email");
private final String value;
@@ -18,13 +18,13 @@
*/
package org.apache.fineract.adhocquery.data;
import java.util.Collection;
import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.useradministration.data.AppUserData;
import org.apache.fineract.useradministration.data.RoleData;
import org.apache.fineract.adhocquery.domain.ReportRunFrequency;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Immutable data object represent note or case information AdHocData
@@ -32,8 +32,8 @@ import org.joda.time.LocalDate;
*/
public class AdHocData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
@@ -59,13 +59,19 @@ public class AdHocData {
@SuppressWarnings("unused")
private final String createdBy;
private final List<EnumOptionData> reportRunFrequencies;
public AdHocData(final Long id, final String name,final String query, final String tableName,final String tableFields,
final boolean isActive, final DateTime createdOn, final Long createdById,final Long updatedById,
final DateTime updatedOn,final String createdBy,final String email
) {
private final Long reportRunFrequency;
private final Long reportRunEvery;
private final DateTime lastRun;
public AdHocData(final Long id, final String name, final String query, final String tableName, final String tableFields,
final boolean isActive, final DateTime createdOn, final Long createdById, final Long updatedById,
final DateTime updatedOn, final String createdBy, final String email,
final List<EnumOptionData> reportRunFrequencies, final Long reportRunFrequency, final Long reportRunEvery,
final DateTime lastRun) {
this.id = id;
this.name=name;
this.query=query;
@@ -78,9 +84,17 @@ public class AdHocData {
this.updatedOn=updatedOn;
this.createdBy=createdBy;
this.email=email;
this.reportRunFrequencies = reportRunFrequencies;
this.reportRunFrequency = reportRunFrequency;
this.reportRunEvery = reportRunEvery;
this.lastRun = lastRun;
}
public static AdHocData template() {
AdHocData adHocData = new AdHocData(null,null,null,null,null,false,null,null,null,null,null,null);
List<EnumOptionData> reportRunFrequencies = Arrays.stream(ReportRunFrequency.values()).map(rrf -> new EnumOptionData(
(long) rrf.getValue(), rrf.getCode(), rrf.getCode()
)).collect(Collectors.toList());
AdHocData adHocData = new AdHocData(null,null,null,null,null,false,null,null,null,null,null,null, reportRunFrequencies, null, null, null);
return adHocData;
}
public Long getId() {
@@ -119,4 +133,16 @@ public class AdHocData {
public String getCreatedBy() {
return this.createdBy;
}
public List<EnumOptionData> getReportRunFrequencies() {
return this.reportRunFrequencies;
}
public Long getReportRunFrequency() {
return this.reportRunFrequency;
}
public Long getReportRunEvery() {
return this.reportRunEvery;
}
public DateTime getLastRun() {
return this.lastRun;
}
}
@@ -53,17 +53,24 @@ public class AdHoc extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "email", length = 500)
private String email;
@Column(name = "report_run_frequency_code")
private Long reportRunFrequency;
@Column(name = "report_run_every")
private Long reportRunEvery;
@Column(name = "IsActive", nullable = false)
private boolean isActive = false;
private AdHoc(final String name, final String query,final String tableName,final String tableFields ,final String email,final boolean isActive) {
private AdHoc(final String name, final String query,final String tableName,final String tableFields ,final String email, final Long reportRunFrequency, final Long reportRunEvery, final boolean isActive) {
this.name = StringUtils.defaultIfEmpty(name, null);
this.query=StringUtils.defaultIfEmpty(query,null);
this.tableName=StringUtils.defaultIfEmpty(tableName,null);
this.tableFields=StringUtils.defaultIfEmpty(tableFields,null);
this.email=StringUtils.defaultIfEmpty(email,null);
this.reportRunFrequency = reportRunFrequency;
this.reportRunEvery = reportRunEvery;
this.isActive = BooleanUtils.toBooleanDefaultIfNull(isActive, false);
}
@@ -75,10 +82,12 @@ public class AdHoc extends AbstractAuditableCustom<AppUser, Long> {
SQLInjectionValidator.validateAdhocQuery(commandQuery);
final String query = commandQuery;
final String tableName = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLENAME.getValue());
final String tableFields = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLEFIELD.getValue());
final String tableFields = command.stringValueOfParameterNamed(AdHocJsonInputParams.TABLEFIELDS.getValue());
final String email = command.stringValueOfParameterNamed(AdHocJsonInputParams.EMAIL.getValue());
final Long reportRunFrequency = command.longValueOfParameterNamed(AdHocJsonInputParams.REPORT_RUN_FREQUENCY.getValue());
final Long reportRunEvery = command.longValueOfParameterNamed(AdHocJsonInputParams.REPORT_RUN_EVERY.getValue());
final boolean isActive = command.booleanPrimitiveValueOfParameterNamed(AdHocJsonInputParams.ISACTIVE.getValue());
return new AdHoc(name,query,tableName,tableFields ,email,isActive);
return new AdHoc(name,query,tableName,tableFields, email, reportRunFrequency, reportRunEvery, isActive);
}
public Map<String, Object> update(final JsonCommand command) {
@@ -104,7 +113,7 @@ public class AdHoc extends AbstractAuditableCustom<AppUser, Long> {
actualChanges.put(tableName, newValue);
this.tableName = newValue;
}
final String tableField = "tableField";
final String tableField = "tableFields";
if (command.isChangeInStringParameterNamed(tableField, this.tableFields)) {
final String newValue = command.stringValueOfParameterNamed(tableField);
actualChanges.put(tableField, newValue);
@@ -116,6 +125,18 @@ public class AdHoc extends AbstractAuditableCustom<AppUser, Long> {
actualChanges.put(email, newValue);
this.email = newValue;
}
final String reportRunFrequency = "reportRunFrequency";
if (command.isChangeInLongParameterNamed(reportRunFrequency, this.getReportRunFrequency())) {
final Long newValue = command.longValueOfParameterNamed(reportRunFrequency);
actualChanges.put(reportRunFrequency, newValue);
this.reportRunFrequency = newValue;
}
final String reportRunEvery = "reportRunEvery";
if (command.isChangeInLongParameterNamed(reportRunEvery, this.getReportRunEvery())) {
final Long newValue = command.longValueOfParameterNamed(reportRunEvery);
actualChanges.put(reportRunEvery, newValue);
this.reportRunEvery = newValue;
}
final String paramisActive = "isActive";
if (command.isChangeInBooleanParameterNamed(paramisActive, this.isActive)) {
final Boolean newValue = command.booleanObjectValueOfParameterNamed(paramisActive);
@@ -149,5 +170,10 @@ public class AdHoc extends AbstractAuditableCustom<AppUser, Long> {
public void enableActive() {
this.isActive = false;
}
public Long getReportRunFrequency() {
return this.reportRunFrequency;
}
public Long getReportRunEvery() {
return this.reportRunEvery;
}
}
@@ -0,0 +1,55 @@
/**
* 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.adhocquery.domain;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
public enum ReportRunFrequency {
DAILY(1, "reportRunFrequency.daily"),
WEEKLY(2, "reportRunFrequency.weekly"),
MONTHLY(3, "reportRunFrequency.monthly"),
YEARLY(4, "reportRunFrequency.yearly"),
CUSTOM(5, "reportRunFrequency.custom");
private static final Map<Long, ReportRunFrequency> MAP = Arrays.stream(ReportRunFrequency.values()).collect(Collectors.toMap(
ReportRunFrequency::getValue, e -> e
));
private final long value;
private final String code;
private ReportRunFrequency(final long value, final String code) {
this.value = value;
this.code = code;
}
public long getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
public static ReportRunFrequency fromId(final long id) {
return ReportRunFrequency.MAP.get(id);
}
}
@@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.adhocquery.domain.ReportRunFrequency;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
@@ -44,7 +45,9 @@ public final class AdHocDataValidator {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("name","query","tableName","tableFields","email","isActive"));
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList(
"name","query","tableName","tableFields","email","isActive", "reportRunFrequency", "reportRunEvery"
));
private final FromJsonHelper fromApiJsonHelper;
@@ -77,7 +80,19 @@ public final class AdHocDataValidator {
baseDataValidator.reset().parameter("tableFields").value(tableFields).notBlank().notExceedingLengthOf(1000);
final String email = this.fromApiJsonHelper.extractStringNamed("email", element);
baseDataValidator.reset().parameter("email").value(email).notBlank().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("email").value(email).ignoreIfNull().notExceedingLengthOf(500);
final Long reportRunFrequencyCode = this.fromApiJsonHelper.extractLongNamed("reportRunFrequency", element);
if (reportRunFrequencyCode != null) {
baseDataValidator.reset().parameter("reportRunFrequency").value(reportRunFrequencyCode)
.inMinMaxRange((int) ReportRunFrequency.DAILY.getValue(), (int) ReportRunFrequency.CUSTOM.getValue());
}
final Long reportRunEvery = this.fromApiJsonHelper.extractLongNamed("reportRunEvery", element);
if (reportRunEvery != null) {
baseDataValidator.reset().parameter("reportRunEvery").value(reportRunFrequencyCode).integerGreaterThanZero();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -111,7 +126,16 @@ public final class AdHocDataValidator {
}
if (this.fromApiJsonHelper.parameterExists("email", element)) {
final String email = this.fromApiJsonHelper.extractStringNamed("email", element);
baseDataValidator.reset().parameter("email").value(email).notBlank().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("email").value(email).ignoreIfNull().notExceedingLengthOf(500);
}
if (this.fromApiJsonHelper.parameterExists("reportRunFrequency", element)) {
final Long reportRunFrequencyCode = this.fromApiJsonHelper.extractLongNamed("reportRunFrequency", element);
baseDataValidator.reset().parameter("reportRunFrequency").value(reportRunFrequencyCode)
.inMinMaxRange((int) ReportRunFrequency.DAILY.getValue(), (int) ReportRunFrequency.CUSTOM.getValue());
}
if (this.fromApiJsonHelper.parameterExists("reportRunEvery", element)) {
final Long reportRunEvery = this.fromApiJsonHelper.extractLongNamed("reportRunEvery", element);
baseDataValidator.reset().parameter("reportRunEvery").value(reportRunEvery).integerGreaterThanZero();
}
/*if (this.fromApiJsonHelper.parameterExists("isActive", element)) {
final Integer isActive = this.fromApiJsonHelper.extractIntegerNamed("isActive", element, Locale.getDefault());
@@ -88,13 +88,17 @@ public class AdHocReadPlatformServiceImpl implements AdHocReadPlatformService {
final DateTime updatedOn=JdbcSupport.getDateTime(rs, "updatedOn");
final String createdByUsername=rs.getString("createdBy");
final String email=rs.getString("email");
return new AdHocData(id,name,query, tableName,tableFields,isActive,createdDate,createdById,updatedById,updatedOn,createdByUsername,email);
final Long reportRunFrequency=JdbcSupport.getLong(rs, "report_run_frequency_code");
final Long reportRunEvery=JdbcSupport.getLong(rs, "report_run_every");
final DateTime lastRun = JdbcSupport.getDateTime(rs, "last_run");
return new AdHocData(id,name,query, tableName,tableFields,isActive,createdDate,createdById,updatedById,updatedOn,createdByUsername,email, AdHocData.template().getReportRunFrequencies(), reportRunFrequency, reportRunEvery, lastRun);
}
public String schema() {
return " r.id as id, r.name as name, r.query as query, r.table_name as tableName,r.table_fields as tableField ,r.IsActive as isActive ,r.email as email ,"
+" r.created_date as createdDate, r.createdby_id as createdById,cb.username as createdBy,r.lastmodifiedby_id as updatedById ,r.lastmodified_date as updatedOn "
+ " r.report_run_frequency_code, r.report_run_every, r.last_run, "
+ " r.created_date as createdDate, r.createdby_id as createdById,cb.username as createdBy,r.lastmodifiedby_id as updatedById ,r.lastmodified_date as updatedOn "
+ " from m_adhoc r left join m_appuser cb on cb.id=r.createdby_id left join m_appuser mb on mb.id=r.lastmodifiedby_id";
}
@@ -18,13 +18,17 @@
*/
package org.apache.fineract.adhocquery.service;
import java.util.Collection;
import org.apache.fineract.adhocquery.data.AdHocData;
import org.apache.fineract.adhocquery.domain.ReportRunFrequency;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.joda.time.Months;
import org.joda.time.Years;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,6 +36,9 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date;
@Service(value = "adHocScheduledJobRunnerService")
public class AdHocScheduledJobRunnerServiceImpl implements AdHocScheduledJobRunnerService {
@@ -47,6 +54,7 @@ public class AdHocScheduledJobRunnerServiceImpl implements AdHocScheduledJobRunn
this.adHocReadPlatformService = adHocReadPlatformService;
}
@Transactional
@Override
@CronTarget(jobName = JobName.GENERATE_ADHOCCLIENT_SCEHDULE)
@@ -54,16 +62,56 @@ public class AdHocScheduledJobRunnerServiceImpl implements AdHocScheduledJobRunn
final Collection<AdHocData> adhocs = this.adHocReadPlatformService.retrieveAllActiveAdHocQuery();
if(adhocs.size()>0){
adhocs.forEach(adhoc->{
//jdbcTemplate.execute("truncate table "+adhoc.getTableName());
final StringBuilder insertSqlBuilder = new StringBuilder(900);
insertSqlBuilder
.append("INSERT INTO ")
.append(adhoc.getTableName()+"(")
.append(adhoc.getTableFields()+") ")
.append(adhoc.getQuery());
if (insertSqlBuilder.length() > 0) {
final int result = this.jdbcTemplate.update(insertSqlBuilder.toString());
logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by inserted: " + result);
boolean run = true;
LocalDate next = null;
if (adhoc.getReportRunFrequency() != null) {
if (adhoc.getLastRun() != null) {
LocalDate start = adhoc.getLastRun().toLocalDate();
LocalDate end = new DateTime().toLocalDate();
switch (ReportRunFrequency.fromId(adhoc.getReportRunFrequency())) {
case DAILY:
next = start.plusDays(1);
run = Days.daysBetween(start, end).getDays() >= 1;
break;
case WEEKLY:
next = start.plusDays(7);
run = Days.daysBetween(start, end).getDays() >= 7;
break;
case MONTHLY:
next = start.plusMonths(1);
run = Months.monthsBetween(start, end).getMonths() >= 1;
break;
case YEARLY:
next = start.plusYears(1);
run = Years.yearsBetween(start, end).getYears() >= 1;
break;
case CUSTOM:
next = start.plusDays((int) (long) adhoc.getReportRunEvery());
run = Days.daysBetween(start, end).getDays() >= adhoc.getReportRunEvery();
break;
default:
throw new IllegalStateException();
}
}
}
if (run) {
//jdbcTemplate.execute("truncate table "+adhoc.getTableName());
final StringBuilder insertSqlBuilder = new StringBuilder(900);
insertSqlBuilder
.append("INSERT INTO ")
.append(adhoc.getTableName()+"(")
.append(adhoc.getTableFields()+") ")
.append(adhoc.getQuery());
if (insertSqlBuilder.length() > 0) {
final int result = this.jdbcTemplate.update(insertSqlBuilder.toString());
logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by inserted: " + result);
this.jdbcTemplate.update("UPDATE m_adhoc SET last_run=? WHERE id=?", new Date(), adhoc.getId());
}
} else {
logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Skipping execution of " + adhoc.getName() + ", scheduled for execution on " + next);
}
});
}else{
@@ -30,7 +30,7 @@ public class SQLInjectionValidator {
private final static String[] COMMENTS = { "--", "({", "/*", "#" };
private final static String SQL_PATTERN = "[a-zA-Z_=,\\-'!><.?\"`% ()0-9]*";
private final static String SQL_PATTERN = "[a-zA-Z_=,\\-'!><.?\"`% ()0-9*\n\r]*";
public final static void validateSQLInput(final String sqlSearch) {
String lowerCaseSQL = sqlSearch.toLowerCase();
@@ -115,9 +115,9 @@ public class SQLInjectionValidator {
}
}
public final static void validateAdhocQuery(final String sqlSearch) {
String lowerCaseSQL = sqlSearch.toLowerCase();
String lowerCaseSQL = sqlSearch.toLowerCase().trim();
for (String ddl : DDL_COMMANDS) {
if (lowerCaseSQL.contains(ddl)) {
if (lowerCaseSQL.startsWith(ddl)) {
throw new SQLInjectionException();
}
}
@@ -0,0 +1,22 @@
--
-- 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.
--
ALTER TABLE m_adhoc ADD COLUMN report_run_frequency_code INT NULL DEFAULT NULL;
ALTER TABLE m_adhoc ADD COLUMN report_run_every INT NULL DEFAULT NULL;
ALTER TABLE m_adhoc ADD COLUMN last_run TIMESTAMP NULL DEFAULT NULL;
@@ -0,0 +1,20 @@
--
-- 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.
--
ALTER TABLE m_adhoc MODIFY `email` VARCHAR(500) NULL DEFAULT NULL;