Repository: incubator-fineract Updated Branches: refs/heads/develop f8f3f743c -> 47f21bc8d
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java new file mode 100644 index 0000000..afec180 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java @@ -0,0 +1,203 @@ +/** + * 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.reportmailingjob.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.dataqueries.data.ReportData; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobData; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailAttachmentFileFormat; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobStretchyReportParamDateOption; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobTimelineData; +import org.apache.fineract.infrastructure.reportmailingjob.exception.ReportMailingJobNotFoundException; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class ReportMailingJobReadPlatformServiceImpl implements ReportMailingJobReadPlatformService { + private final JdbcTemplate jdbcTemplate; + + @Autowired + public ReportMailingJobReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Page<ReportMailingJobData> retrieveAllReportMailingJobs(final SearchParameters searchParameters) { + final StringBuilder sqlStringBuilder = new StringBuilder(200); + final List<Object> queryParameters = new ArrayList<>(); + final ReportMailingJobMapper mapper = new ReportMailingJobMapper(); + final PaginationHelper<ReportMailingJobData> paginationHelper = new PaginationHelper<>(); + + sqlStringBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlStringBuilder.append(mapper.ReportMailingJobSchema()); + sqlStringBuilder.append(" where rmj.is_deleted = 0"); + + if (searchParameters.isOrderByRequested()) { + sqlStringBuilder.append(" order by ").append(searchParameters.getOrderBy()); + + if (searchParameters.isSortOrderProvided()) { + sqlStringBuilder.append(" ").append(searchParameters.getSortOrder()); + } + } else { + sqlStringBuilder.append(" order by rmj.name "); + } + + if (searchParameters.isLimited()) { + sqlStringBuilder.append(" limit ").append(searchParameters.getLimit()); + + if (searchParameters.isOffset()) { + sqlStringBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + + return paginationHelper.fetchPage(this.jdbcTemplate, "SELECT FOUND_ROWS()", sqlStringBuilder.toString(), + queryParameters.toArray(), mapper); + } + + @Override + public Collection<ReportMailingJobData> retrieveAllActiveReportMailingJobs() { + final ReportMailingJobMapper mapper = new ReportMailingJobMapper(); + final String sql = "select " + mapper.ReportMailingJobSchema() + " where rmj.is_deleted = 0 and is_active = 1" + + " order by rmj.name"; + + return this.jdbcTemplate.query(sql, mapper, new Object[] {}); + } + + @Override + public ReportMailingJobData retrieveReportMailingJob(final Long reportMailingJobId) { + try { + final ReportMailingJobMapper mapper = new ReportMailingJobMapper(); + final String sql = "select " + mapper.ReportMailingJobSchema() + " where rmj.id = ? and rmj.is_deleted = 0"; + + return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { reportMailingJobId }); + } + + catch (final EmptyResultDataAccessException ex) { + throw new ReportMailingJobNotFoundException(reportMailingJobId); + } + } + + @Override + public ReportMailingJobData retrieveReportMailingJobEnumOptions() { + final List<EnumOptionData> emailAttachmentFileFormatOptions = ReportMailingJobEmailAttachmentFileFormat.validOptions(); + final List<EnumOptionData> stretchyReportParamDateOptions = ReportMailingJobStretchyReportParamDateOption.validOptions(); + + return ReportMailingJobData.newInstance(emailAttachmentFileFormatOptions, stretchyReportParamDateOptions); + } + + private static final class ReportMailingJobMapper implements RowMapper<ReportMailingJobData> { + public String ReportMailingJobSchema() { + return "rmj.id, rmj.name, rmj.description, rmj.start_datetime as startDateTime, rmj.recurrence, rmj.created_date as createdOnDate, " + + "cbu.username as createdByUsername, cbu.firstname as createdByFirstname, cbu.lastname as createdByLastname, " + + "rmj.lastmodified_date as updatedOnDate, " + + "mbu.username as updatedByUsername, mbu.firstname as updatedByFirstname, mbu.lastname as updatedByLastname, " + + "rmj.email_recipients as emailRecipients, " + + "rmj.email_subject as emailSubject, rmj.email_message as emailMessage, " + + "rmj.email_attachment_file_format as emailAttachmentFileFormat, " + + "rmj.stretchy_report_param_map as stretchyReportParamMap, rmj.previous_run_datetime as previousRunDateTime, " + + "rmj.next_run_datetime as nextRunDateTime, rmj.previous_run_status as previousRunStatus, " + + "rmj.previous_run_error_log as previousRunErrorLog, rmj.previous_run_error_message as previousRunErrorMessage, " + + "rmj.number_of_runs as numberOfRuns, rmj.is_active as isActive, rmj.run_as_userid as runAsUserId, " + + "sr.id as reportId, sr.report_name as reportName, sr.report_type as reportType, sr.report_subtype as reportSubType, " + + "sr.report_category as reportCategory, sr.report_sql as reportSql, sr.description as reportDescription, " + + "sr.core_report as coreReport, sr.use_report as useReport " + + "from m_report_mailing_job rmj " + + "inner join m_appuser cbu " + + "on cbu.id = rmj.createdby_id " + + "left join m_appuser mbu " + + "on mbu.id = rmj.lastmodifiedby_id " + + "left join stretchy_report sr " + + "on rmj.stretchy_report_id = sr.id"; + } + + @Override + public ReportMailingJobData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String name = rs.getString("name"); + final String description = rs.getString("description"); + final DateTime startDateTime = JdbcSupport.getDateTime(rs, "startDateTime"); + final String recurrence = rs.getString("recurrence"); + final LocalDate createdOnDate = JdbcSupport.getLocalDate(rs, "createdOnDate"); + final LocalDate updatedOnDate = JdbcSupport.getLocalDate(rs, "updatedOnDate"); + final String emailRecipients = rs.getString("emailRecipients"); + final String emailSubject = rs.getString("emailSubject"); + final String emailMessage = rs.getString("emailMessage"); + final String emailAttachmentFileFormatString = rs.getString("emailAttachmentFileFormat"); + EnumOptionData emailAttachmentFileFormat = null; + + if (emailAttachmentFileFormatString != null) { + ReportMailingJobEmailAttachmentFileFormat format = ReportMailingJobEmailAttachmentFileFormat.newInstance(emailAttachmentFileFormatString); + + emailAttachmentFileFormat = format.toEnumOptionData(); + } + + final String stretchyReportParamMap = rs.getString("stretchyReportParamMap"); + final DateTime previousRunDateTime = JdbcSupport.getDateTime(rs, "previousRunDateTime"); + final DateTime nextRunDateTime = JdbcSupport.getDateTime(rs, "nextRunDateTime"); + final String previousRunStatus = rs.getString("previousRunStatus"); + final String previousRunErrorLog = rs.getString("previousRunErrorLog"); + final String previousRunErrorMessage = rs.getString("previousRunErrorMessage"); + final Integer numberOfRuns = JdbcSupport.getInteger(rs, "numberOfRuns"); + final boolean isActive = rs.getBoolean("isActive"); + final String createdByUsername = rs.getString("createdByUsername"); + final String createdByFirstname = rs.getString("createdByFirstname"); + final String createdByLastname = rs.getString("createdByLastname"); + final String updatedByUsername = rs.getString("updatedByUsername"); + final String updatedByFirstname = rs.getString("updatedByFirstname"); + final String updatedByLastname = rs.getString("updatedByLastname"); + final ReportMailingJobTimelineData timeline = new ReportMailingJobTimelineData(createdOnDate, createdByUsername, + createdByFirstname, createdByLastname, updatedOnDate, updatedByUsername, updatedByFirstname, updatedByLastname); + final Long runAsUserId = JdbcSupport.getLong(rs, "runAsUserId"); + + final Long reportId = JdbcSupport.getLong(rs, "reportId"); + final String reportName = rs.getString("reportName"); + final String reportType = rs.getString("reportType"); + final String reportSubType = rs.getString("reportSubType"); + final String reportCategory = rs.getString("reportCategory"); + final String reportSql = rs.getString("reportSql"); + final String reportDescription = rs.getString("reportDescription"); + final boolean coreReport = rs.getBoolean("coreReport"); + final boolean useReport = rs.getBoolean("useReport"); + + final ReportData stretchyReport = new ReportData(reportId, reportName, reportType, reportSubType, reportCategory, + reportDescription, reportSql, coreReport, useReport, null); + + return ReportMailingJobData.newInstance(id, name, description, startDateTime, recurrence, timeline, emailRecipients, + emailSubject, emailMessage, emailAttachmentFileFormat, stretchyReport, stretchyReportParamMap, previousRunDateTime, + nextRunDateTime, previousRunStatus, previousRunErrorLog, previousRunErrorMessage, numberOfRuns, isActive, + runAsUserId); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformService.java new file mode 100644 index 0000000..9f4a204 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformService.java @@ -0,0 +1,34 @@ +/** + * 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.reportmailingjob.service; + +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobRunHistoryData; + +public interface ReportMailingJobRunHistoryReadPlatformService { + /** + * Retrieve all report mailing run history with similar job id to the one passed + * + * @param reportMailingJobId ReportMailingJob identifier + * @param searchParameters {@link SearchParameters} object + * @return collection of {@link ReportMailingJobRunHistoryData} objects + **/ + Page<ReportMailingJobRunHistoryData> retrieveRunHistoryByJobId(Long reportMailingJobId, SearchParameters searchParameters); +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java new file mode 100644 index 0000000..4aeb68f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java @@ -0,0 +1,106 @@ +/** + * 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.reportmailingjob.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobRunHistoryData; +import org.joda.time.DateTime; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class ReportMailingJobRunHistoryReadPlatformServiceImpl implements ReportMailingJobRunHistoryReadPlatformService { + private final JdbcTemplate jdbcTemplate; + private final ReportMailingJobRunHistoryMapper reportMailingJobRunHistoryMapper; + private final PaginationHelper<ReportMailingJobRunHistoryData> paginationHelper = new PaginationHelper<>(); + + @Autowired + public ReportMailingJobRunHistoryReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.reportMailingJobRunHistoryMapper = new ReportMailingJobRunHistoryMapper(); + } + + @Override + public Page<ReportMailingJobRunHistoryData> retrieveRunHistoryByJobId(final Long reportMailingJobId, + final SearchParameters searchParameters) { + final StringBuilder sqlStringBuilder = new StringBuilder(200); + final List<Object> queryParameters = new ArrayList<>(); + + sqlStringBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlStringBuilder.append(this.reportMailingJobRunHistoryMapper.ReportMailingJobRunHistorySchema()); + + if (reportMailingJobId != null) { + sqlStringBuilder.append(" where rmjrh.job_id = ? "); + queryParameters.add(reportMailingJobId); + } + + if (searchParameters.isOrderByRequested()) { + sqlStringBuilder.append(" order by ").append(searchParameters.getOrderBy()); + + if (searchParameters.isSortOrderProvided()) { + sqlStringBuilder.append(" ").append(searchParameters.getSortOrder()); + } + } + + if (searchParameters.isLimited()) { + sqlStringBuilder.append(" limit ").append(searchParameters.getLimit()); + + if (searchParameters.isOffset()) { + sqlStringBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + + return this.paginationHelper.fetchPage(this.jdbcTemplate, "SELECT FOUND_ROWS()", sqlStringBuilder.toString(), + queryParameters.toArray(), this.reportMailingJobRunHistoryMapper); + } + + private static final class ReportMailingJobRunHistoryMapper implements RowMapper<ReportMailingJobRunHistoryData> { + public String ReportMailingJobRunHistorySchema() { + return "rmjrh.id, rmjrh.job_id as reportMailingJobId, rmjrh.start_datetime as startDateTime, " + + "rmjrh.end_datetime as endDateTime, rmjrh.status, rmjrh.error_message as errorMessage, " + + "rmjrh.error_log as errorLog " + + "from m_report_mailing_job_run_history rmjrh"; + } + + @Override + public ReportMailingJobRunHistoryData mapRow(ResultSet rs, int rowNum) throws SQLException { + final Long id = JdbcSupport.getLong(rs, "id"); + final Long reportMailingJobId = JdbcSupport.getLong(rs, "reportMailingJobId"); + final DateTime startDateTime = JdbcSupport.getDateTime(rs, "startDateTime"); + final DateTime endDateTime = JdbcSupport.getDateTime(rs, "endDateTime"); + final String status = rs.getString("status"); + final String errorMessage = rs.getString("errorMessage"); + final String errorLog = rs.getString("errorLog"); + + return ReportMailingJobRunHistoryData.newInstance(id, reportMailingJobId, startDateTime, endDateTime, status, + errorMessage, errorLog); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformService.java new file mode 100644 index 0000000..ce54919 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformService.java @@ -0,0 +1,30 @@ +/** + * 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.reportmailingjob.service; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; + +public interface ReportMailingJobWritePlatformService { + CommandProcessingResult createReportMailingJob(JsonCommand jsonCommand); + CommandProcessingResult updateReportMailingJob(Long reportMailingJobId, JsonCommand jsonCommand); + CommandProcessingResult deleteReportMailingJob(Long reportMailingJobId); + void executeReportMailingJobs() throws JobExecutionException; +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..02972a9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java @@ -0,0 +1,495 @@ +/** + * 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.reportmailingjob.service; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import org.apache.commons.lang.StringUtils; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.dataqueries.domain.Report; +import org.apache.fineract.infrastructure.dataqueries.domain.ReportRepositoryWrapper; +import org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService; +import org.apache.fineract.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository; +import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; +import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; +import org.apache.fineract.infrastructure.jobs.service.JobName; +import org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider; +import org.apache.fineract.infrastructure.report.service.ReportingProcessService; +import org.apache.fineract.infrastructure.reportmailingjob.ReportMailingJobConstants; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailAttachmentFileFormat; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailData; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobPreviousRunStatus; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobStretchyReportParamDateOption; +import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJob; +import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepository; +import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepositoryWrapper; +import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistory; +import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistoryRepository; +import org.apache.fineract.infrastructure.reportmailingjob.util.ReportMailingJobDateUtil; +import org.apache.fineract.infrastructure.reportmailingjob.validation.ReportMailingJobValidator; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.portfolio.calendar.service.CalendarUtils; +import org.apache.fineract.useradministration.domain.AppUser; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.sun.jersey.core.util.MultivaluedMapImpl; + +@Service +public class ReportMailingJobWritePlatformServiceImpl implements ReportMailingJobWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(ReportMailingJobWritePlatformServiceImpl.class); + private final ReportRepositoryWrapper reportRepositoryWrapper; + private final ReportMailingJobValidator reportMailingJobValidator; + private final ReportMailingJobRepositoryWrapper reportMailingJobRepositoryWrapper; + private final ReportMailingJobRepository reportMailingJobRepository; + private final PlatformSecurityContext platformSecurityContext; + private final ReportMailingJobEmailService reportMailingJobEmailService; + private final ReadReportingService readReportingService; + private final ReportingProcessServiceProvider reportingProcessServiceProvider; + private final ReportMailingJobRunHistoryRepository reportMailingJobRunHistoryRepository; + private final static String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + @Autowired + public ReportMailingJobWritePlatformServiceImpl(final ReportRepositoryWrapper reportRepositoryWrapper, + final ReportMailingJobValidator reportMailingJobValidator, + final ReportMailingJobRepositoryWrapper reportMailingJobRepositoryWrapper, + final ReportMailingJobRepository reportMailingJobRepository, + final PlatformSecurityContext platformSecurityContext, + final ReportMailingJobEmailService reportMailingJobEmailService, + final ReadReportingService readReportingService, + final ReportMailingJobRunHistoryRepository reportMailingJobRunHistoryRepository, + final ReportingProcessServiceProvider reportingProcessServiceProvider) { + this.reportRepositoryWrapper = reportRepositoryWrapper; + this.reportMailingJobValidator = reportMailingJobValidator; + this.reportMailingJobRepositoryWrapper = reportMailingJobRepositoryWrapper; + this.reportMailingJobRepository = reportMailingJobRepositoryWrapper.getReportMailingJobRepository(); + this.platformSecurityContext = platformSecurityContext; + this.reportMailingJobEmailService = reportMailingJobEmailService; + this.readReportingService = readReportingService; + this.reportMailingJobRunHistoryRepository = reportMailingJobRunHistoryRepository; + this.reportingProcessServiceProvider = reportingProcessServiceProvider; + } + + @Override + @Transactional + public CommandProcessingResult createReportMailingJob(JsonCommand jsonCommand) { + try { + // validate the create request + this.reportMailingJobValidator.validateCreateRequest(jsonCommand); + + final AppUser appUser = this.platformSecurityContext.authenticatedUser(); + + // get the stretchy Report object + final Report stretchyReport = this.reportRepositoryWrapper.findOneThrowExceptionIfNotFound(jsonCommand.longValueOfParameterNamed( + ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME)); + + // create an instance of ReportMailingJob class from the JsonCommand object + final ReportMailingJob reportMailingJob = ReportMailingJob.newInstance(jsonCommand, stretchyReport, appUser); + + // save entity + this.reportMailingJobRepository.save(reportMailingJob); + + return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()). + withEntityId(reportMailingJob.getId()).build(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + + return CommandProcessingResult.empty(); + } + } + + @Override + @Transactional + public CommandProcessingResult updateReportMailingJob(Long reportMailingJobId, JsonCommand jsonCommand) { + try { + // validate the update request + this.reportMailingJobValidator.validateUpdateRequest(jsonCommand); + + // retrieve the ReportMailingJob object from the database + final ReportMailingJob reportMailingJob = this.reportMailingJobRepositoryWrapper.findOneThrowExceptionIfNotFound(reportMailingJobId); + + final Map<String, Object> changes = reportMailingJob.update(jsonCommand); + + // get the recurrence rule string + final String recurrence = reportMailingJob.getRecurrence(); + + // get the next run DateTime from the ReportMailingJob entity + DateTime nextRunDateTime = reportMailingJob.getNextRunDateTime(); + + // check if the stretchy report id was updated + if (changes.containsKey(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME)) { + final Long stretchyReportId = (Long) changes.get(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME); + final Report stretchyReport = this.reportRepositoryWrapper.findOneThrowExceptionIfNotFound(stretchyReportId); + + // update the stretchy report + reportMailingJob.update(stretchyReport); + } + + // check if the recurrence was updated + if (changes.containsKey(ReportMailingJobConstants.RECURRENCE_PARAM_NAME)) { + + // go ahead if the recurrence is not null + if (StringUtils.isNotBlank(recurrence)) { + // set the start DateTime to the current tenant date time + DateTime startDateTime = DateUtils.getLocalDateTimeOfTenant().toDateTime(); + + // check if the start DateTime was updated + if (changes.containsKey(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME)) { + // get the updated start DateTime + startDateTime = reportMailingJob.getStartDateTime(); + } + + startDateTime = reportMailingJob.getStartDateTime(); + + // get the next recurring DateTime + final DateTime nextRecurringDateTime = this.createNextRecurringDateTime(recurrence, startDateTime); + + // update the next run time property + reportMailingJob.updateNextRunDateTime(nextRecurringDateTime); + + // check if the next run DateTime is not empty and the recurrence is empty + } else if (StringUtils.isBlank(recurrence) && (nextRunDateTime != null)) { + // the next run DateTime should be set to null + reportMailingJob.updateNextRunDateTime(null); + } + } + + if (changes.containsKey(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME)) { + final DateTime startDateTime = reportMailingJob.getStartDateTime(); + + // initially set the next recurring date time to the new start date time + DateTime nextRecurringDateTime = startDateTime; + + // ensure that the recurrence pattern string is not empty + if (StringUtils.isNotBlank(recurrence)) { + // get the next recurring DateTime + nextRecurringDateTime = this.createNextRecurringDateTime(recurrence, startDateTime); + } + + // update the next run time property + reportMailingJob.updateNextRunDateTime(nextRecurringDateTime); + } + + if (!changes.isEmpty()) { + // save and flush immediately so any data integrity exception can be handled in the "catch" block + this.reportMailingJobRepository.saveAndFlush(reportMailingJob); + } + + return new CommandProcessingResultBuilder(). + withCommandId(jsonCommand.commandId()). + withEntityId(reportMailingJob.getId()). + with(changes). + build(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(jsonCommand, dve); + + return CommandProcessingResult.empty(); + } + } + + @Override + @Transactional + public CommandProcessingResult deleteReportMailingJob(Long reportMailingJobId) { + // retrieve the ReportMailingJob object from the database + final ReportMailingJob reportMailingJob = this.reportMailingJobRepositoryWrapper.findOneThrowExceptionIfNotFound(reportMailingJobId); + + // delete the report mailing job by setting the isDeleted property to 1 and altering the name + reportMailingJob.delete(); + + // save the report mailing job entity + this.reportMailingJobRepository.save(reportMailingJob); + + return new CommandProcessingResultBuilder().withEntityId(reportMailingJobId).build(); + } + + @Override + @CronTarget(jobName = JobName.EXECUTE_REPORT_MAILING_JOBS) + public void executeReportMailingJobs() throws JobExecutionException { + final Collection<ReportMailingJob> reportMailingJobCollection = this.reportMailingJobRepository.findByIsActiveTrueAndIsDeletedFalse(); + + for (ReportMailingJob reportMailingJob : reportMailingJobCollection) { + // get the tenant's date as a DateTime object + final DateTime localDateTimeOftenant = DateUtils.getLocalDateTimeOfTenant().toDateTime(); + final DateTime nextRunDateTime = reportMailingJob.getNextRunDateTime(); + + if (nextRunDateTime != null && nextRunDateTime.isBefore(localDateTimeOftenant)) { + // get the emailAttachmentFileFormat enum object + final ReportMailingJobEmailAttachmentFileFormat emailAttachmentFileFormat = ReportMailingJobEmailAttachmentFileFormat. + newInstance(reportMailingJob.getEmailAttachmentFileFormat()); + + if (emailAttachmentFileFormat != null && emailAttachmentFileFormat.isValid()) { + final Report stretchyReport = reportMailingJob.getStretchyReport(); + final String reportName = (stretchyReport != null) ? stretchyReport.getReportName() : null; + final StringBuilder errorLog = new StringBuilder(); + final Map<String, String> validateStretchyReportParamMap = this.reportMailingJobValidator. + validateStretchyReportParamMap(reportMailingJob.getStretchyReportParamMap()); + MultivaluedMap<String, String> reportParams = new MultivaluedMapImpl(); + + if (validateStretchyReportParamMap != null) { + Iterator<Map.Entry<String, String>> validateStretchyReportParamMapEntries = validateStretchyReportParamMap.entrySet().iterator(); + + while (validateStretchyReportParamMapEntries.hasNext()) { + Map.Entry<String, String> validateStretchyReportParamMapEntry = validateStretchyReportParamMapEntries.next(); + String key = validateStretchyReportParamMapEntry.getKey(); + String value = validateStretchyReportParamMapEntry.getValue(); + + if (StringUtils.containsIgnoreCase(key, "date")) { + ReportMailingJobStretchyReportParamDateOption reportMailingJobStretchyReportParamDateOption = + ReportMailingJobStretchyReportParamDateOption.newInstance(value); + + if (reportMailingJobStretchyReportParamDateOption.isValid()) { + value = ReportMailingJobDateUtil.getDateAsString(reportMailingJobStretchyReportParamDateOption); + } + } + + reportParams.add(key, value); + } + } + + // generate the report output stream, method in turn call another that sends the file to the email recipients + this.generateReportOutputStream(reportMailingJob, emailAttachmentFileFormat, reportParams, reportName, errorLog); + + // update the previous run time, next run time, status, error log properties + this.updateReportMailingJobAfterJobExecution(reportMailingJob, errorLog, localDateTimeOftenant); + } + } + } + } + + /** + * update the report mailing job entity after job execution + * + * @param reportMailingJob -- the report mailing job entity + * @param errorLog -- StringBuilder object containing the error log if any + * @param jobStartDateTime -- the start DateTime of the job + * @return None + **/ + private void updateReportMailingJobAfterJobExecution(final ReportMailingJob reportMailingJob, final StringBuilder errorLog, + final DateTime jobStartDateTime) { + final String recurrence = reportMailingJob.getRecurrence(); + final DateTime nextRunDateTime = reportMailingJob.getNextRunDateTime(); + ReportMailingJobPreviousRunStatus reportMailingJobPreviousRunStatus = ReportMailingJobPreviousRunStatus.SUCCESS; + + reportMailingJob.updatePreviousRunErrorLog(null); + + if (errorLog != null && errorLog.length() > 0) { + reportMailingJobPreviousRunStatus = ReportMailingJobPreviousRunStatus.ERROR; + reportMailingJob.updatePreviousRunErrorLog(errorLog.toString()); + } + + reportMailingJob.increaseNumberOfRunsByOne(); + reportMailingJob.updatePreviousRunStatus(reportMailingJobPreviousRunStatus.getValue()); + reportMailingJob.updatePreviousRunDateTime(reportMailingJob.getNextRunDateTime()); + + // check if the job has a recurrence pattern, if not deactivate the job. The job will only run once + if (StringUtils.isEmpty(recurrence)) { + // deactivate job + reportMailingJob.deactivate(); + + // job will only run once, no next run time + reportMailingJob.updateNextRunDateTime(null); + } else if (nextRunDateTime != null) { + final DateTime nextRecurringDateTime = this.createNextRecurringDateTime(recurrence, nextRunDateTime); + + // finally update the next run date time property + reportMailingJob.updateNextRunDateTime(nextRecurringDateTime); + } + + // save the ReportMailingJob entity + this.reportMailingJobRepository.save(reportMailingJob); + + // create a new report mailing job run history entity + this.createReportMailingJobRunHistroryAfterJobExecution(reportMailingJob, errorLog, jobStartDateTime, + reportMailingJobPreviousRunStatus.getValue()); + } + + /** + * create the next recurring DateTime from recurrence pattern, start DateTime and current DateTime + * + * @param recurrencePattern + * @param startDateTime + * @return DateTime object + */ + private DateTime createNextRecurringDateTime(final String recurrencePattern, final DateTime startDateTime) { + DateTime nextRecurringDateTime = null; + + // the recurrence pattern/rule cannot be empty + if (StringUtils.isNotBlank(recurrencePattern) && startDateTime != null) { + final LocalDate nextDayLocalDate = startDateTime.plus(1).toLocalDate(); + final LocalDate nextRecurringLocalDate = CalendarUtils.getNextRecurringDate(recurrencePattern, startDateTime.toLocalDate(), + nextDayLocalDate); + final String nextDateTimeString = nextRecurringLocalDate + " " + startDateTime.getHourOfDay() + ":" + startDateTime.getMinuteOfHour() + + ":" + startDateTime.getSecondOfMinute(); + final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(DATETIME_FORMAT); + + nextRecurringDateTime = DateTime.parse(nextDateTimeString, dateTimeFormatter); + } + + return nextRecurringDateTime; + } + + /** + * create a new report mailing job run history entity after job execution + * + * @param reportMailingJob -- the report mailing job entity + * @param errorLog -- StringBuilder object containing the error log if any + * @param jobStartDateTime -- the start DateTime of the job + * @param jobRunStatus -- the status of the job (success/error) + * @return None + **/ + private void createReportMailingJobRunHistroryAfterJobExecution(final ReportMailingJob reportMailingJob, final StringBuilder errorLog, + final DateTime jobStartDateTime, final String jobRunStatus) { + final DateTime jobEndDateTime = DateUtils.getLocalDateTimeOfTenant().toDateTime(); + final String errorLogToString = (errorLog != null) ? errorLog.toString() : null; + final ReportMailingJobRunHistory reportMailingJobRunHistory = ReportMailingJobRunHistory.newInstance(reportMailingJob, jobStartDateTime, + jobEndDateTime, jobRunStatus, null, errorLogToString); + + this.reportMailingJobRunHistoryRepository.save(reportMailingJobRunHistory); + } + + /** + * Handle any SQL data integrity issue + * + * @param jsonCommand -- JsonCommand object + * @param dve -- data integrity exception object + * @return None + **/ + private void handleDataIntegrityIssues(final JsonCommand jsonCommand, final DataIntegrityViolationException dve) { + final Throwable realCause = dve.getMostSpecificCause(); + + if (realCause.getMessage().contains(ReportMailingJobConstants.NAME_PARAM_NAME)) { + final String name = jsonCommand.stringValueOfParameterNamed(ReportMailingJobConstants.NAME_PARAM_NAME); + throw new PlatformDataIntegrityException("error.msg.report.mailing.job.duplicate.name", "Report mailing job with name `" + name + "` already exists", + ReportMailingJobConstants.NAME_PARAM_NAME, name); + } + + logger.error(dve.getMessage(), dve); + + throw new PlatformDataIntegrityException("error.msg.charge.unknown.data.integrity.issue", + "Unknown data integrity issue with resource: " + realCause.getMessage()); + } + + /** + * generate the report output stream + * + * @param reportMailingJob + * @param emailAttachmentFileFormat + * @param reportParams + * @param reportName + * @param errorLog + * @return the error log StringBuilder object + */ + private StringBuilder generateReportOutputStream(final ReportMailingJob reportMailingJob, final ReportMailingJobEmailAttachmentFileFormat emailAttachmentFileFormat, + final MultivaluedMap<String, String> reportParams, final String reportName, final StringBuilder errorLog) { + + try { + final String reportType = this.readReportingService.getReportType(reportName); + final ReportingProcessService reportingProcessService = this.reportingProcessServiceProvider.findReportingProcessService(reportType); + + if (reportingProcessService != null) { + final Response processReport = reportingProcessService.processRequest(reportName, reportParams); + final Object reponseObject = (processReport != null) ? processReport.getEntity() : null; + + if (reponseObject != null && reponseObject.getClass().equals(ByteArrayOutputStream.class)) { + final ByteArrayOutputStream byteArrayOutputStream = ByteArrayOutputStream.class.cast(reponseObject); + final String fileLocation = FileSystemContentRepository.FINERACT_BASE_DIR + File.separator + ""; + final String fileNameWithoutExtension = fileLocation + File.separator + reportName; + + // check if file directory exists, if not create directory + if (!new File(fileLocation).isDirectory()) { + new File(fileLocation).mkdirs(); + } + + if ((byteArrayOutputStream == null) || byteArrayOutputStream.size() == 0) { + errorLog.append("Report processing failed, empty output stream created"); + } else if ((errorLog != null && errorLog.length() == 0) && (byteArrayOutputStream.size() > 0)) { + final String fileName = fileNameWithoutExtension + "." + emailAttachmentFileFormat.getValue(); + + // send the file to email recipients + this.sendReportFileToEmailRecipients(reportMailingJob, fileName, byteArrayOutputStream, errorLog); + } + } else { + errorLog.append("Response object entity is not equal to ByteArrayOutputStream ---------- "); + } + } else { + errorLog.append("ReportingProcessService object is null ---------- "); + } + } catch (Exception e) { + errorLog.append("The ReportMailingJobWritePlatformServiceImpl.generateReportOutputStream method threw an Exception: " + + e + " ---------- "); + } + + return errorLog; + } + + /** + * send report file to email recipients + * + * @param reportMailingJob + * @param fileName + * @param byteArrayOutputStream + * @param errorLog + */ + private void sendReportFileToEmailRecipients(final ReportMailingJob reportMailingJob, final String fileName, + final ByteArrayOutputStream byteArrayOutputStream, final StringBuilder errorLog) { + final Set<String> emailRecipients = this.reportMailingJobValidator.validateEmailRecipients(reportMailingJob.getEmailRecipients()); + + try { + final File file = new File(fileName); + final FileOutputStream outputStream = new FileOutputStream(file); + byteArrayOutputStream.writeTo(outputStream); + + for (String emailRecipient : emailRecipients) { + final ReportMailingJobEmailData reportMailingJobEmailData = new ReportMailingJobEmailData(emailRecipient, + reportMailingJob.getEmailMessage(), reportMailingJob.getEmailSubject(), file); + + this.reportMailingJobEmailService.sendEmailWithAttachment(reportMailingJobEmailData); + } + + outputStream.close(); + + } catch (IOException e) { + errorLog.append("The ReportMailingJobWritePlatformServiceImpl.sendReportFileToEmailRecipients method threw an IOException " + + "exception: " + e + " ---------- "); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/util/ReportMailingJobDateUtil.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/util/ReportMailingJobDateUtil.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/util/ReportMailingJobDateUtil.java new file mode 100644 index 0000000..a1d97fa --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/util/ReportMailingJobDateUtil.java @@ -0,0 +1,114 @@ +/** + * 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.reportmailingjob.util; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobStretchyReportParamDateOption; + +public class ReportMailingJobDateUtil { + public static final String MYSQL_DATE_FORMAT = "yyyy-MM-dd"; + + /** + * get the current date as string using the mysql date format yyyy-MM-dd + **/ + public static String getTodayDateAsString() { + // get a calendar instance, which defaults to "now" + Calendar calendar = Calendar.getInstance(); + + // get a date to represent "today" + Date today = calendar.getTime(); + + // get a SimpleDateFormat instance, passing the mysql date format as parameter + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(MYSQL_DATE_FORMAT); + + // return date as string + return simpleDateFormat.format(today); + } + + /** + * get the yesterday's date as string using the mysql date format yyyy-MM-dd + **/ + public static String getYesterdayDateAsString() { + // get a calendar instance, which defaults to "now" + Calendar calendar = Calendar.getInstance(); + + // add one day to the date/calendar + calendar.add(Calendar.DAY_OF_YEAR, -1); + + // now get "yesterday" + Date yesterday = calendar.getTime(); + + // get a SimpleDateFormat instance, passing the mysql date format as parameter + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(MYSQL_DATE_FORMAT); + + // return date as string + return simpleDateFormat.format(yesterday); + } + + /** + * get the tomorrow's date as string using the mysql date format yyyy-MM-dd + **/ + public static String getTomorrowDateAsString() { + // get a calendar instance, which defaults to "now" + Calendar calendar = Calendar.getInstance(); + + // add one day to the date/calendar + calendar.add(Calendar.DAY_OF_YEAR, 1); + + // now get "tomorrow" + Date tomorrow = calendar.getTime(); + + // get a SimpleDateFormat instance, passing the mysql date format as parameter + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(MYSQL_DATE_FORMAT); + + // return date as string + return simpleDateFormat.format(tomorrow); + } + + /** + * get date as string based on the value of the {@link ReportMailingJobStretchyReportParamDateOption} object + * + * @param reportMailingJobStretchyReportParamDateOption {@link ReportMailingJobStretchyReportParamDateOption} Enum + **/ + public static String getDateAsString(final ReportMailingJobStretchyReportParamDateOption reportMailingJobStretchyReportParamDateOption) { + String dateAsString = null; + + switch (reportMailingJobStretchyReportParamDateOption) { + case TODAY: + dateAsString = getTodayDateAsString(); + break; + + case YESTERDAY: + dateAsString = getYesterdayDateAsString(); + break; + + case TOMORROW: + dateAsString = getTomorrowDateAsString(); + break; + + default: + break; + } + + return dateAsString; + } +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/validation/ReportMailingJobValidator.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/validation/ReportMailingJobValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/validation/ReportMailingJobValidator.java new file mode 100644 index 0000000..c3bbaf8 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/validation/ReportMailingJobValidator.java @@ -0,0 +1,319 @@ +/** + * 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.reportmailingjob.validation; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.ApiParameterError; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.infrastructure.reportmailingjob.ReportMailingJobConstants; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailAttachmentFileFormat; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; +import org.joda.time.LocalDateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +@Component +public class ReportMailingJobValidator { + private final FromJsonHelper fromJsonHelper; + private static final String EMAIL_REGEX = "^[\\w!#$%&â*+/=?`{|}~^-]+(?:\\.[\\w!#$%&â*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$"; + private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX); + + @Autowired + public ReportMailingJobValidator(final FromJsonHelper fromJsonHelper) { + this.fromJsonHelper = fromJsonHelper; + } + + /** + * validate the request to create a new report mailing job + * + * @param jsonCommand -- the JSON command object (instance of the JsonCommand class) + * @return None + **/ + public void validateCreateRequest(final JsonCommand jsonCommand) { + final String jsonString = jsonCommand.json(); + final JsonElement jsonElement = jsonCommand.parsedJson(); + + if (StringUtils.isBlank(jsonString)) { + throw new InvalidJsonException(); + } + + final Type typeToken = new TypeToken<Map<String, Object>>() {}.getType(); + this.fromJsonHelper.checkForUnsupportedParameters(typeToken, jsonString, + ReportMailingJobConstants.CREATE_REQUEST_PARAMETERS); + + final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors). + resource(StringUtils.lowerCase(ReportMailingJobConstants.REPORT_MAILING_JOB_RESOURCE_NAME)); + + final String name = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.NAME_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.NAME_PARAM_NAME).value(name).notBlank().notExceedingLengthOf(100); + + final String startDateTime = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME, + jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME).value(startDateTime).notBlank(); + + final Integer stretchyReportId = this.fromJsonHelper.extractIntegerWithLocaleNamed(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME, + jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME).value(stretchyReportId).notNull(). + integerGreaterThanZero(); + + final String emailRecipients = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME).value(emailRecipients).notBlank(); + + final String emailSubject = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME).value(emailSubject).notBlank().notExceedingLengthOf(100); + + final String emailMessage = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME).value(emailMessage).notBlank(); + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME, jsonElement)) { + final Boolean isActive = this.fromJsonHelper.extractBooleanNamed(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME, + jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME).value(isActive).notNull(); + } + + final Integer emailAttachmentFileFormatId = this.fromJsonHelper.extractIntegerSansLocaleNamed( + ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME). + value(emailAttachmentFileFormatId).notNull(); + + if (emailAttachmentFileFormatId != null) { + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).value(emailAttachmentFileFormatId). + isOneOfTheseValues(ReportMailingJobEmailAttachmentFileFormat.validIds()); + } + + final String dateFormat = jsonCommand.dateFormat(); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).notBlank(); + + if (StringUtils.isNotEmpty(dateFormat)) { + + try { + final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(dateFormat).withLocale(jsonCommand.extractLocale()); + + // try to parse the date time string + LocalDateTime.parse(startDateTime, dateTimeFormatter); + } + + catch(IllegalArgumentException ex) { + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).failWithCode("invalid.date.format"); + } + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + /** + * validate the request to update a report mailing job + * + * @param jsonCommand -- the JSON command object (instance of the JsonCommand class) + * @return None + **/ + public void validateUpdateRequest(final JsonCommand jsonCommand) { + final String jsonString = jsonCommand.json(); + final JsonElement jsonElement = jsonCommand.parsedJson(); + + if (StringUtils.isBlank(jsonString)) { + throw new InvalidJsonException(); + } + + final Type typeToken = new TypeToken<Map<String, Object>>() {}.getType(); + this.fromJsonHelper.checkForUnsupportedParameters(typeToken, jsonString, + ReportMailingJobConstants.UPDATE_REQUEST_PARAMETERS); + + final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors). + resource(StringUtils.lowerCase(ReportMailingJobConstants.REPORT_MAILING_JOB_RESOURCE_NAME)); + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.NAME_PARAM_NAME, jsonElement)) { + final String name = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.NAME_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.NAME_PARAM_NAME).value(name).notBlank().notExceedingLengthOf(100); + } + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME, jsonElement)) { + final Integer stretchyReportId = this.fromJsonHelper.extractIntegerWithLocaleNamed(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME, + jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME).value(stretchyReportId).notNull(). + integerGreaterThanZero(); + } + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME, jsonElement)) { + final String emailRecipients = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME).value(emailRecipients).notBlank(); + } + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME, jsonElement)) { + final String emailSubject = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME).value(emailSubject).notBlank().notExceedingLengthOf(100); + } + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME, jsonElement)) { + final String emailMessage = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME).value(emailMessage).notBlank(); + } + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME, jsonElement)) { + final Boolean isActive = this.fromJsonHelper.extractBooleanNamed(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME, + jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME).value(isActive).notNull(); + } + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, jsonElement)) { + final Integer emailAttachmentFileFormatId = this.fromJsonHelper.extractIntegerSansLocaleNamed( + ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME). + value(emailAttachmentFileFormatId).notNull(); + + if (emailAttachmentFileFormatId != null) { + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).value(emailAttachmentFileFormatId). + isOneOfTheseValues(ReportMailingJobEmailAttachmentFileFormat.validIds()); + } + } + + if (this.fromJsonHelper.parameterExists(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME, jsonElement)) { + final String dateFormat = jsonCommand.dateFormat(); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).notBlank(); + + final String startDateTime = this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME, + jsonElement); + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME).value(startDateTime).notBlank(); + + if (StringUtils.isNotEmpty(dateFormat)) { + + try { + final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(dateFormat).withLocale(jsonCommand.extractLocale()); + + // try to parse the date time string + LocalDateTime.parse(startDateTime, dateTimeFormatter); + } + + catch(IllegalArgumentException ex) { + dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).failWithCode("invalid.date.format"); + } + } + } + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + + /** + * check if string is a valid email address + * + * @param email -- string to be validated + * @return true if string is a valid email address + **/ + public boolean isValidEmail(String email) { + // this is the easiest check + if (email == null) { + return false; + } + + // this is another easy check + if (email.endsWith(".")) { + return false; + } + + // Check the whole email address structure + Matcher emailMatcher = EMAIL_PATTERN.matcher(email); + + // check if the Matcher matches the email pattern + if (!emailMatcher.matches()) { + return false; + } + + return true; + } + + /** + * Validate the email recipients string + * + * @param emailRecipients -- the email recipients string to be validated + * @return a hashset containing valid email addresses + **/ + public Set<String> validateEmailRecipients(String emailRecipients) { + Set<String> emailRecipientsSet = new HashSet<>(); + + if (emailRecipients != null) { + String[] split = emailRecipients.split(","); + + for (String emailAddress : split) { + emailAddress = emailAddress.trim(); + + if (this.isValidEmail(emailAddress)) { + emailRecipientsSet.add(emailAddress); + } + } + } + + return emailRecipientsSet; + } + + /** + * validate the stretchy report param json string + * + * @param stretchyReportParamMap -- json string to be validated + * @return if string is valid or empty, a HashMap object, else null + **/ + public HashMap<String,String> validateStretchyReportParamMap(String stretchyReportParamMap) { + HashMap<String,String> stretchyReportParamHashMap = new HashMap<>(); + + if (!StringUtils.isEmpty(stretchyReportParamMap)) { + try { + stretchyReportParamHashMap = new ObjectMapper().readValue(stretchyReportParamMap, new TypeReference<HashMap<String,String>>(){}); + } + + catch(Exception e) { + stretchyReportParamHashMap = null; + } + } + + return stretchyReportParamHashMap; + } + + /** + * throw a PlatformApiDataValidationException exception if there are validation errors + * + * @param dataValidationErrors -- list of ApiParameterError objects + * @return None + **/ + private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/resources/sql/migrations/core_db/V312__report_mailing_job_module.sql ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V312__report_mailing_job_module.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V312__report_mailing_job_module.sql new file mode 100644 index 0000000..155c77a --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V312__report_mailing_job_module.sql @@ -0,0 +1,61 @@ +create table if not exists m_report_mailing_job ( +id bigint primary key auto_increment, +name varchar(100) not null, +description text null, +start_datetime datetime not null, +recurrence varchar(100) null, +created_date date not null, +createdby_id bigint not null, +lastmodified_date date null, +lastmodifiedby_id bigint null, +email_recipients text not null, +email_subject varchar(100) not null, +email_message text not null, +email_attachment_file_format varchar(10) not null, +stretchy_report_id int not null, +stretchy_report_param_map text null, +previous_run_datetime datetime null, +next_run_datetime datetime null, +previous_run_status varchar(10) null, +previous_run_error_log text null, +previous_run_error_message text null, +number_of_runs int not null default 0, +is_active tinyint(1) not null default 0, +is_deleted tinyint(1) not null default 0, +run_as_userid bigint not null, +foreign key (createdby_id) references m_appuser(id), +foreign key (lastmodifiedby_id) references m_appuser(id), +foreign key (stretchy_report_id) references stretchy_report(id), +foreign key (run_as_userid) references m_appuser(id), +constraint unique_name unique (name) +); + +create table if not exists m_report_mailing_job_run_history ( +id bigint primary key auto_increment, +job_id bigint not null, +start_datetime datetime not null, +end_datetime datetime not null, +status varchar(10) not null, +error_message text null, +error_log text null, +foreign key (job_id) references m_report_mailing_job (id) +); + +create table if not exists m_report_mailing_job_configuration ( +id int primary key auto_increment, +name varchar(50) not null, +`value` varchar(200) not null, +constraint unique_name unique (name) +); + +insert into m_permission (`grouping`, code, entity_name, action_name, can_maker_checker) +values ('jobs', 'CREATE_REPORTMAILINGJOB', 'REPORTMAILINGJOB', 'CREATE', 0), +('jobs', 'UPDATE_REPORTMAILINGJOB', 'REPORTMAILINGJOB', 'UPDATE', 0), +('jobs', 'DELETE_REPORTMAILINGJOB', 'REPORTMAILINGJOB', 'DELETE', 0), +('jobs', 'READ_REPORTMAILINGJOB', 'REPORTMAILINGJOB', 'READ', 0); + +insert into m_report_mailing_job_configuration (name, `value`) +values ('GMAIL_SMTP_SERVER', 'smtp.gmail.com'), ('GMAIL_SMTP_PORT', 587), ('GMAIL_SMTP_USERNAME', ''), ('GMAIL_SMTP_PASSWORD', ''); + +insert into job (name, display_name, cron_expression, create_time) +values ('Execute Report Mailing Jobs', 'Execute Report Mailing Jobs', '0 0/15 * * * ?', NOW());
