diff --git a/java/claim/pom.xml b/java/claim/pom.xml index bb17d8bdc..8f153ffe0 100644 --- a/java/claim/pom.xml +++ b/java/claim/pom.xml @@ -52,6 +52,10 @@ org.springframework.boot spring-boot-starter-data-jpa + + org.springframework.boot + spring-boot-starter-data-jdbc + org.springframework.cloud spring-cloud-gcp-starter-vision @@ -154,6 +158,14 @@ json 20210307 + + org.freemarker + freemarker + + + org.springframework.boot + spring-boot-starter-quartz + diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/config/PropertyMapper.java b/java/claim/src/main/java/dev/sunbirdrc/claim/config/PropertyMapper.java new file mode 100644 index 000000000..d56b98715 --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/config/PropertyMapper.java @@ -0,0 +1,21 @@ +package dev.sunbirdrc.claim.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +@Data +public class PropertyMapper { + @Value("${simple.mail.message.from}") + private String simpleMailMessageFrom; + + @Value("${foreign.pending.item.subject}") + private String foreignPendingItemSubject; + + @Value("${up.council.name}") + private String upCouncilName; + + @Value("${regulator.table.name}") + private String regulatorTableName; +} diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/contants/AttributeNames.java b/java/claim/src/main/java/dev/sunbirdrc/claim/contants/AttributeNames.java index d76ba5e3e..95442cce4 100644 --- a/java/claim/src/main/java/dev/sunbirdrc/claim/contants/AttributeNames.java +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/contants/AttributeNames.java @@ -14,4 +14,10 @@ public class AttributeNames { public static final String CONTENT = "content"; public static final String TOTAL_PAGES = "totalPages"; public static final String TOTAL_ELEMENTS = "totalElements"; + + public static final String JPG = "JPG"; + public static final String JPEG = "JPEG"; + public static final String PNG = "PNG"; + public static final String GIF = "GIF"; + public static final String PDF = "PDF"; } diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/controller/EmailController.java b/java/claim/src/main/java/dev/sunbirdrc/claim/controller/EmailController.java index 4016b1b7d..d41cf3e54 100644 --- a/java/claim/src/main/java/dev/sunbirdrc/claim/controller/EmailController.java +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/controller/EmailController.java @@ -1,7 +1,9 @@ package dev.sunbirdrc.claim.controller; import dev.sunbirdrc.claim.dto.BarCode; +import dev.sunbirdrc.claim.dto.CertificateMailDto; import dev.sunbirdrc.claim.dto.MailDto; +import dev.sunbirdrc.claim.dto.PendingMailDTO; import dev.sunbirdrc.claim.service.EmailService; import net.sourceforge.barbecue.Barcode; import net.sourceforge.barbecue.BarcodeFactory; @@ -111,5 +113,16 @@ private String prepareBody(String idLink, String name, String credType) { return body; } + @RequestMapping(value = "/api/v1/sendCertificateMail", method = RequestMethod.POST) + public ResponseEntity sendCertificateMail(@RequestBody CertificateMailDto certificateMailDto) { + emailService.sendCertificateMail(certificateMailDto); + return new ResponseEntity<>("Mail is sending", HttpStatus.OK); + } + @RequestMapping(value = "/api/v1/sendPendingForeignItemMail", method = RequestMethod.POST) + public ResponseEntity sendPendingItemMail(@RequestHeader HttpHeaders headers, + @RequestBody PendingMailDTO pendingMailDTO) { + emailService.sendManualPendingMail(pendingMailDTO); + return new ResponseEntity<>("Mail is sending", HttpStatus.OK); + } } diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/controller/FileController.java b/java/claim/src/main/java/dev/sunbirdrc/claim/controller/FileController.java index 267339141..d758e8982 100644 --- a/java/claim/src/main/java/dev/sunbirdrc/claim/controller/FileController.java +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/controller/FileController.java @@ -45,9 +45,16 @@ public ResponseEntity downloadFile( ByteArrayResource resource = fileService.downloadFile(fileName); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "filename=\"" + fileName + "\""); - return ResponseEntity.ok(). - contentType(MediaType.APPLICATION_PDF). - headers(headers).body(resource); + + try { + MediaType mediaType = fileService.getFileMediaType(fileName); + + return ResponseEntity.ok() + .contentType(mediaType) + .headers(headers).body(resource); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED); + } } /** diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/dto/CertificateMailDto.java b/java/claim/src/main/java/dev/sunbirdrc/claim/dto/CertificateMailDto.java new file mode 100644 index 000000000..da666cd9d --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/dto/CertificateMailDto.java @@ -0,0 +1,21 @@ +package dev.sunbirdrc.claim.dto; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CertificateMailDto { + private String name; + private String certificate; + private String emailAddress; + + private String credentialsType; + + private String certificateBase64; +} diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/dto/PendingMailDTO.java b/java/claim/src/main/java/dev/sunbirdrc/claim/dto/PendingMailDTO.java new file mode 100644 index 000000000..b54401151 --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/dto/PendingMailDTO.java @@ -0,0 +1,22 @@ +package dev.sunbirdrc.claim.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PendingMailDTO { + private String name; + private String emailAddress; + private String council; + private String itemName; + private String refNo; + private String regulatorName; + private String regulatorEmail; + private String credType; + private String registrationNumber; +} diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/entity/Regulator.java b/java/claim/src/main/java/dev/sunbirdrc/claim/entity/Regulator.java new file mode 100644 index 000000000..37139c340 --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/entity/Regulator.java @@ -0,0 +1,32 @@ +package dev.sunbirdrc.claim.entity; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Regulator { + @Id + @GeneratedValue(strategy = GenerationType.TABLE) + private String ID; + @Column + private String name; + @Column + private String phoneNumber; + @Column + private String council; + @Column + private String email; + @Column + private String osOwner; + @Column + private String osid; +} \ No newline at end of file diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/exception/ClaimMailException.java b/java/claim/src/main/java/dev/sunbirdrc/claim/exception/ClaimMailException.java new file mode 100644 index 000000000..2e3377d87 --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/exception/ClaimMailException.java @@ -0,0 +1,7 @@ +package dev.sunbirdrc.claim.exception; + +public class ClaimMailException extends RuntimeException { + public ClaimMailException(String message) { + super(message); + } +} diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/JobCreator.java b/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/JobCreator.java new file mode 100644 index 000000000..1d9804377 --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/JobCreator.java @@ -0,0 +1,93 @@ +package dev.sunbirdrc.claim.quartz; + +import lombok.extern.slf4j.Slf4j; +import org.quartz.CronTrigger; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.SimpleTrigger; +import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.quartz.CronTriggerFactoryBean; +import org.springframework.scheduling.quartz.JobDetailFactoryBean; +import org.springframework.scheduling.quartz.QuartzJobBean; +import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; +import org.springframework.stereotype.Component; + +import java.text.ParseException; +import java.util.Date; + +@Slf4j +@Component +public class JobCreator { + + /** + * Create Quartz Job. + * + * @param jobClass Class whose executeInternal() method needs to be called. + * @param isDurable Job needs to be persisted even after completion. if true, job will be persisted, not otherwise. + * @param context Spring application context. + * @param jobName Job name. + * @param jobGroup Job group. + * @return JobDetail object + */ + public JobDetail createJob(Class jobClass, boolean isDurable, + ApplicationContext context, String jobName, String jobGroup) { + JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); + factoryBean.setJobClass(jobClass); + factoryBean.setDurability(isDurable); + factoryBean.setApplicationContext(context); + factoryBean.setName(jobName); + factoryBean.setGroup(jobGroup); + + // set job data map + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put(jobName + jobGroup, jobClass.getName()); + factoryBean.setJobDataMap(jobDataMap); + + factoryBean.afterPropertiesSet(); + + return factoryBean.getObject(); + } + + /** + * Create cron trigger. + * + * @param triggerName Trigger name. + * @param startTime Trigger start time. + * @param cronExpression Cron expression. + * @param misFireInstruction Misfire instruction (what to do in case of misfire happens). + * @return {@link CronTrigger} + */ + public CronTrigger createCronTrigger(String triggerName, Date startTime, String cronExpression, int misFireInstruction) { + CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean(); + factoryBean.setName(triggerName); + factoryBean.setStartTime(startTime); + factoryBean.setCronExpression(cronExpression); + factoryBean.setMisfireInstruction(misFireInstruction); + try { + factoryBean.afterPropertiesSet(); + } catch (ParseException e) { + log.error(e.getMessage(), e); + } + return factoryBean.getObject(); + } + + /** + * Create simple trigger. + * + * @param triggerName Trigger name. + * @param startTime Trigger start time. + * @param repeatTime Job repeat period mills + * @param misFireInstruction Misfire instruction (what to do in case of misfire happens). + * @return {@link SimpleTrigger} + */ + public SimpleTrigger createSimpleTrigger(String triggerName, Date startTime, Long repeatTime, int misFireInstruction) { + SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean(); + factoryBean.setName(triggerName); + factoryBean.setStartTime(startTime); + factoryBean.setRepeatInterval(repeatTime); + factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); + factoryBean.setMisfireInstruction(misFireInstruction); + factoryBean.afterPropertiesSet(); + return factoryBean.getObject(); + } +} diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/MailingJobExecutor.java b/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/MailingJobExecutor.java new file mode 100644 index 000000000..6b4d092d3 --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/MailingJobExecutor.java @@ -0,0 +1,28 @@ +package dev.sunbirdrc.claim.quartz; + +import dev.sunbirdrc.claim.service.EmailService; +import lombok.extern.slf4j.Slf4j; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.quartz.QuartzJobBean; +import org.springframework.stereotype.Component; + +import java.util.Date; + +@Slf4j +@Component +public class MailingJobExecutor extends QuartzJobBean { + + @Autowired + private EmailService emailService; + + @Override + protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { + log.info(">>>>>>>>>>>> Mail for foreign council job started :: " + new Date(System.currentTimeMillis()) + " <<<<<<<<<<<<<<<<"); + + emailService.sendForeignPendingItemMail(); + + log.info(">>>>>>>>>>>> Mail for foreign council has been completed :: " + new Date(System.currentTimeMillis()) + " <<<<<<<<<<<<<<<<"); + } +} diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/SchedulerConfig.java b/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/SchedulerConfig.java new file mode 100644 index 000000000..6a2c87631 --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/SchedulerConfig.java @@ -0,0 +1,72 @@ +package dev.sunbirdrc.claim.quartz; + +import org.quartz.JobDetail; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.quartz.QuartzProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.CronTriggerFactoryBean; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +import javax.sql.DataSource; +import java.util.Properties; + +@Configuration +public class SchedulerConfig { + @Autowired + private DataSource dataSource; + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private QuartzProperties quartzProperties; + + @Autowired + private JobCreator jobCreator; + + /** + * create scheduler factory + */ + @Bean + public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("searchJobTrigger") Trigger searchJobTrigger) { + + SchedulerJobFactory jobFactory = new SchedulerJobFactory(); + jobFactory.setApplicationContext(applicationContext); + + Properties properties = new Properties(); + properties.putAll(quartzProperties.getProperties()); + + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + factory.setOverwriteExistingJobs(true); + factory.setDataSource(dataSource); + factory.setQuartzProperties(properties); + factory.setJobFactory(jobFactory); + factory.setTriggers(searchJobTrigger); + return factory; + } + + @Bean(name = "searchJobTrigger") + public CronTriggerFactoryBean sampleJobTrigger(@Qualifier("quartzJobDetail") JobDetail jobDetail, + @Value("${samplejob.frequency}") String frequency) { + return createCronTrigger(jobDetail, frequency); + } + + private static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression) { + CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean(); + factoryBean.setJobDetail(jobDetail); + factoryBean.setCronExpression(cronExpression); + factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); + return factoryBean; + } + + @Bean + public JobDetail quartzJobDetail() { + return jobCreator.createJob(MailingJobExecutor.class, true, applicationContext, "Send mail", "Mail Event Group"); + } +} diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/SchedulerJobFactory.java b/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/SchedulerJobFactory.java new file mode 100644 index 000000000..db35e10ab --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/quartz/SchedulerJobFactory.java @@ -0,0 +1,24 @@ +package dev.sunbirdrc.claim.quartz; + +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +public class SchedulerJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { + + private AutowireCapableBeanFactory beanFactory; + + @Override + public void setApplicationContext(final ApplicationContext context) { + beanFactory = context.getAutowireCapableBeanFactory(); + } + + @Override + protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { + final Object job = super.createJobInstance(bundle); + beanFactory.autowireBean(job); + return job; + } +} \ No newline at end of file diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/repository/RegulatorRowMapper.java b/java/claim/src/main/java/dev/sunbirdrc/claim/repository/RegulatorRowMapper.java new file mode 100644 index 000000000..25d684de6 --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/repository/RegulatorRowMapper.java @@ -0,0 +1,23 @@ +package dev.sunbirdrc.claim.repository; + +import dev.sunbirdrc.claim.entity.Regulator; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class RegulatorRowMapper implements RowMapper { + @Override + public Regulator mapRow(ResultSet rs, int rowNum) throws SQLException { + return new Regulator( + rs.getString("ID"), + rs.getString("name"), + rs.getString("phoneNumber"), + rs.getString("council"), + rs.getString("email"), + rs.getString("osOwner"), + rs.getString("osid") + + ); + } +} diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/service/EmailService.java b/java/claim/src/main/java/dev/sunbirdrc/claim/service/EmailService.java index a40f8dcd3..6ded2a89c 100644 --- a/java/claim/src/main/java/dev/sunbirdrc/claim/service/EmailService.java +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/service/EmailService.java @@ -1,27 +1,68 @@ package dev.sunbirdrc.claim.service; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import dev.sunbirdrc.claim.config.PropertyMapper; +import dev.sunbirdrc.claim.controller.EmailController; +import dev.sunbirdrc.claim.dto.CertificateMailDto; +import dev.sunbirdrc.claim.dto.MailDto; +import dev.sunbirdrc.claim.dto.PendingMailDTO; +import dev.sunbirdrc.claim.entity.Claim; +import dev.sunbirdrc.claim.entity.Regulator; +import dev.sunbirdrc.claim.exception.ClaimMailException; +import dev.sunbirdrc.claim.model.ClaimStatus; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.FileSystemResource; +import org.springframework.lang.NonNull; import org.springframework.mail.MailException; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.mail.javamail.MimeMessagePreparator; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; import javax.mail.Message; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; @Service("emailService") public class EmailService { + private static final Logger logger = LoggerFactory.getLogger(EmailController.class); @Autowired private JavaMailSender mailSender; @Autowired private SimpleMailMessage preConfiguredMessage; + + @Autowired + private Configuration freeMarkerConfiguration; + + @Value("${simple.mail.message.from}") + private String simpleMailMessageFrom; + + @Autowired + private PropertyMapper propertyMapper; + + @Autowired + private ClaimService claimService; + + @Autowired + private RegulatorService regulatorService; /** * This method will send compose and send the message @@ -99,4 +140,245 @@ public void prepare(MimeMessage mimeMessage) throws Exception System.err.println(ex.getMessage()); } } + + @Async + public void sendCertificateMail(CertificateMailDto certificateMailDto) { + String subject = certificateMailDto.getCredentialsType(); + + try { + MimeMessage mimeMessage = mailSender.createMimeMessage(); + + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true); + + mimeMessageHelper.setSubject(subject); + mimeMessageHelper.setFrom(new InternetAddress(propertyMapper.getSimpleMailMessageFrom(), certificateMailDto.getName())); + mimeMessageHelper.setTo(certificateMailDto.getEmailAddress()); + mimeMessageHelper.setText(generateAttachedCertificateMailContent(certificateMailDto), true); + + + byte[] doc = Base64.getDecoder().decode(certificateMailDto.getCertificateBase64()); + mimeMessageHelper.addAttachment("certificate.pdf", new ByteArrayResource(doc)); + + mailSender.send(mimeMessageHelper.getMimeMessage()); + } catch (Exception e) { + logger.error("Exception while sending mail: ", e); + throw new ClaimMailException("Exception while composing and sending mail with OTP"); + } + } + + private String generateAttachedCertificateMailContent(CertificateMailDto certificateMailDto) { + String processedTemplateString = null; + + Map mailMap = new HashMap<>(); + mailMap.put("name", certificateMailDto.getName()); + mailMap.put("credType", certificateMailDto.getCredentialsType()); + mailMap.put("idLink", certificateMailDto.getCertificate()); + + try { + freeMarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/templates/"); + Template template = freeMarkerConfiguration.getTemplate("credentials-mail.ftl"); + processedTemplateString = FreeMarkerTemplateUtils.processTemplateIntoString(template, mailMap); + + } catch (TemplateException e) { + logger.error("TemplateException while creating mail template for certificate ", e); + throw new ClaimMailException("Error while creating mail template for certificate"); + } catch (IOException e) { + logger.error("IOException while creating mail template for certificate ", e); + throw new ClaimMailException("Error while creating mail template for certificate"); + } + return processedTemplateString; + } + + @Async + public void sendPendingMail(@NonNull List pendingMailDTOList, @NonNull String regulatorName, + @NonNull String regulatorEmail) { + + try { + MimeMessage mimeMessage = mailSender.createMimeMessage(); + + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true); + + mimeMessageHelper.setSubject(propertyMapper.getForeignPendingItemSubject()); + mimeMessageHelper.setFrom(new InternetAddress(propertyMapper.getSimpleMailMessageFrom(), + "Pending Action Item")); + mimeMessageHelper.setTo(regulatorEmail); + mimeMessageHelper.setText(generatePendingMailContent(pendingMailDTOList, regulatorName), true); + + mailSender.send(mimeMessageHelper.getMimeMessage()); + } catch (Exception e) { + logger.error("Exception while sending mail: ", e); + throw new ClaimMailException("Exception while composing and sending mail with OTP"); + } + + } + + /** + * @param mailDto + * @return + */ + private String generatePendingMailContent(@NonNull List pendingMailDTOList, + @NonNull String regulatorName) { + String processedTemplateString = null; + + Map mailMap = new HashMap<>(); + mailMap.put("candidates", pendingMailDTOList); + mailMap.put("regulatorName", regulatorName); + + try { + freeMarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/templates/"); + Template template = freeMarkerConfiguration.getTemplate("pending-item-mail.ftl"); + processedTemplateString = FreeMarkerTemplateUtils.processTemplateIntoString(template, mailMap); + + } catch (TemplateException e) { + logger.error("TemplateException while creating mail template for certificate ", e); + throw new ClaimMailException("Error while creating mail template for certificate"); + } catch (IOException e) { + logger.error("IOException while creating mail template for certificate ", e); + throw new ClaimMailException("Error while creating mail template for certificate"); + } + return processedTemplateString; + } + + public void sendForeignPendingItemMail() { + if (!regulatorService.isRegulatorTableExist()) { + logger.error(">>>>>>>>>>> Unable to find regulator table in database: No further process will be occurred"); + return; + } + + List allClaimList = claimService.findAll(); + + if (allClaimList != null && !allClaimList.isEmpty()) { + List foreignCouncilNames = getPendingForeignCouncilList(allClaimList); + + for (String foreignCouncilName : foreignCouncilNames) { + + List foreignCouncilClaims = allClaimList.stream() + .filter(claim -> foreignCouncilName.equalsIgnoreCase(getCouncilName(claim.getPropertyData()))) + .collect(Collectors.toList()); + + List pendingMailDTOList = collectEntityDetailsForMail(foreignCouncilClaims); + + List regulatorList = regulatorService.findByCouncil(foreignCouncilName); + + for (Regulator regulator : regulatorList) { + sendPendingMail(pendingMailDTOList, regulator.getName(), regulator.getEmail()); + } + } + } + } + + /** + * @param claimList + * @return + */ + private @NonNull List getPendingForeignCouncilList(@NonNull List claimList) { + List councilList = claimList.stream() + .filter(claim -> !propertyMapper.getUpCouncilName() + .equalsIgnoreCase(getCouncilName(claim.getPropertyData())) + ) + .filter(claim -> ClaimStatus.OPEN.name().equalsIgnoreCase(claim.getStatus())) + .map(claim -> getCouncilName(claim.getPropertyData())) + .distinct() + .collect(Collectors.toList()); + + if (councilList == null) { + logger.error(">>>>>>>> Unale to find any pending foreign council list"); + return Collections.emptyList(); + } else { + return councilList; + } + } + + private @NonNull List collectEntityDetailsForMail(@NonNull List claimList) { + List pendingMailDTOList = new ArrayList<>(); + + try { + for (Claim claim : claimList) { + String propertyData = claim.getPropertyData(); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(propertyData); + + PendingMailDTO pendingMailDTO = PendingMailDTO.builder() + .credType(jsonNode.get("credType") != null ? jsonNode.get("credType").asText() : "") + .emailAddress(jsonNode.get("email") != null ? jsonNode.get("email").asText() : "") + .refNo(jsonNode.get("refNo") != null ? jsonNode.get("refNo").asText() : "") + .name(jsonNode.get("name") != null ? jsonNode.get("name").asText() : "") + .registrationNumber(jsonNode.get("registrationNumber") != null ? jsonNode.get("registrationNumber").asText() : "") + .build(); + + pendingMailDTOList.add(pendingMailDTO); + } + } catch (Exception e) { + logger.error(">>>>>>>>>>> Unable to read council name from claim property data", e); + } + + + return pendingMailDTOList; + } + + private @NonNull String getCouncilName(String propertyData) { + String council = ""; + if (StringUtils.isEmpty(propertyData)) { + logger.error(">>>>>>> Error while fetching council name from property data in Claim"); + } + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(propertyData); + council = jsonNode.get("council").asText(); + } catch (Exception e) { + logger.error(">>>>>>>>>>> Unable to read council name from claim property data", e); + } + + return council; + } + + @Async + public void sendManualPendingMail(PendingMailDTO pendingMailDTO) { + + try { + MimeMessage mimeMessage = mailSender.createMimeMessage(); + + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true); + + mimeMessageHelper.setSubject(propertyMapper.getForeignPendingItemSubject()); + mimeMessageHelper.setFrom(new InternetAddress(propertyMapper.getSimpleMailMessageFrom(),pendingMailDTO.getName())); + mimeMessageHelper.setTo(pendingMailDTO.getEmailAddress()); + mimeMessageHelper.setText(generateManualPendingMailContent(pendingMailDTO), true); + + mailSender.send(mimeMessageHelper.getMimeMessage()); + } catch (Exception e) { + logger.error("Exception while sending mail: ", e); + throw new ClaimMailException("Exception while composing and sending mail with OTP"); + } + + } + + /** + * @param mailDto + * @return + */ + private String generateManualPendingMailContent(PendingMailDTO pendingMailDTO) { + String processedTemplateString = null; + + Map mailMap = new HashMap<>(); + mailMap.put("name", pendingMailDTO.getName()); + mailMap.put("council", pendingMailDTO.getCouncil()); + mailMap.put("itemName", pendingMailDTO.getItemName()); + + try { + freeMarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/templates/"); + Template template = freeMarkerConfiguration.getTemplate("manual-pending-item-mail.ftl"); + processedTemplateString = FreeMarkerTemplateUtils.processTemplateIntoString(template, mailMap); + + } catch (TemplateException e) { + logger.error("TemplateException while creating mail template for certificate ", e); + throw new ClaimMailException("Error while creating mail template for certificate"); + } catch (IOException e) { + logger.error("IOException while creating mail template for certificate ", e); + throw new ClaimMailException("Error while creating mail template for certificate"); + } + return processedTemplateString; + } + } diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/service/FileService.java b/java/claim/src/main/java/dev/sunbirdrc/claim/service/FileService.java index 3de1b64da..ffb6e8e3d 100644 --- a/java/claim/src/main/java/dev/sunbirdrc/claim/service/FileService.java +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/service/FileService.java @@ -2,6 +2,7 @@ import dev.sunbirdrc.claim.dto.FileDto; import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.MediaType; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; @@ -14,4 +15,6 @@ public interface FileService { FileDto uploadFile(MultipartFile file) throws IOException; List uploadMultipleFile(MultipartFile[] files, String entityName, String entityId); + + MediaType getFileMediaType(String fileName) throws Exception; } \ No newline at end of file diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/service/FileServiceImpl.java b/java/claim/src/main/java/dev/sunbirdrc/claim/service/FileServiceImpl.java index fc6f5ae99..917047bf4 100644 --- a/java/claim/src/main/java/dev/sunbirdrc/claim/service/FileServiceImpl.java +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/service/FileServiceImpl.java @@ -6,11 +6,14 @@ import dev.sunbirdrc.claim.exception.GCPFileUploadException; import dev.sunbirdrc.claim.utils.GCPBucketUtil; import lombok.RequiredArgsConstructor; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -21,6 +24,9 @@ import java.util.ArrayList; import java.util.List; +import static dev.sunbirdrc.claim.contants.AttributeNames.*; +import static dev.sunbirdrc.claim.contants.AttributeNames.PDF; + @Service @RequiredArgsConstructor @@ -71,6 +77,7 @@ public List uploadMultipleFile(MultipartFile[] files, String entityName for (MultipartFile file : files) { FileDto fileDto = new FileDto(); String originalFileName = entityName + "_" + entityId + "_" + file.getOriginalFilename(); + originalFileName = StringUtils.deleteWhitespace(originalFileName); Path path = new File(originalFileName).toPath(); try { @@ -87,4 +94,40 @@ public List uploadMultipleFile(MultipartFile[] files, String entityName } return fileDtoList; } + + /** + * @param fileName + * @return + * @throws Exception + */ + @Override + public MediaType getFileMediaType(String fileName) throws Exception { + if (StringUtils.isEmpty(fileName)) { + LOGGER.error("File name is either empty or blank - while finding file type"); + throw new Exception("File name is either empty or blank - while finding file type"); + } + + MediaType mediaType = MediaType.APPLICATION_PDF; + + String extension = FilenameUtils.getExtension(fileName); + extension = StringUtils.upperCase(extension); + + switch (extension) { + case JPG: + case JPEG: + mediaType = MediaType.IMAGE_JPEG; + break; + case PNG: + mediaType = MediaType.IMAGE_PNG; + break; + case PDF: + mediaType = MediaType.APPLICATION_PDF; + break; + default: + LOGGER.error("File type not supported"); + throw new Exception("File type not supported"); + } + + return mediaType; + } } \ No newline at end of file diff --git a/java/claim/src/main/java/dev/sunbirdrc/claim/service/RegulatorService.java b/java/claim/src/main/java/dev/sunbirdrc/claim/service/RegulatorService.java new file mode 100644 index 000000000..678549a1a --- /dev/null +++ b/java/claim/src/main/java/dev/sunbirdrc/claim/service/RegulatorService.java @@ -0,0 +1,45 @@ +package dev.sunbirdrc.claim.service; + +import dev.sunbirdrc.claim.config.PropertyMapper; +import dev.sunbirdrc.claim.entity.Regulator; +import dev.sunbirdrc.claim.repository.RegulatorRowMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class RegulatorService { + + @Autowired + private PropertyMapper propertyMapper; + + @Autowired + private JdbcTemplate jdbcTemplate; + + public List findByCouncil(String council) { + try { + return jdbcTemplate.query("SELECT * FROM \"" + propertyMapper.getRegulatorTableName() + + "\" where council=?", new RegulatorRowMapper(), council); + + } catch (IncorrectResultSizeDataAccessException e) { + return null; + } + } + + public List findAll() { + return jdbcTemplate.query("SELECT * from \"" + propertyMapper.getRegulatorTableName() + "\"", + new RegulatorRowMapper()); + } + + public boolean isRegulatorTableExist() { + String sqlQuery = "SELECT count(*) FROM information_schema.tables WHERE table_name = '" + + propertyMapper.getRegulatorTableName() + "'"; + + Integer tableCount = jdbcTemplate.queryForObject(sqlQuery, Integer.class); + + return tableCount > 0; + } +} diff --git a/java/claim/src/main/resources/application.properties b/java/claim/src/main/resources/application.properties index bbe651c90..caaba96c9 100644 --- a/java/claim/src/main/resources/application.properties +++ b/java/claim/src/main/resources/application.properties @@ -1,11 +1,12 @@ server.port=8082 -spring.datasource.url=${connectionInfo_uri:jdbc:postgresql://localhost:5432/registry10} +spring.datasource.url=${connectionInfo_uri:jdbc:postgresql://localhost:5432/registry} spring.datasource.username=${connectionInfo_username:postgres} spring.datasource.password=${connectionInfo_password:postgres} spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=update +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true sunbirdrc.url=${sunbirdrc_url:http://localhost:8081} @@ -42,4 +43,35 @@ gcp.file.validity=2 spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB +# Mail config +simple.mail.message.from = r.kishor.rahu@gmail.com +foreign.pending.item.subject = Pending Item + +#Pending Item config +up.council.name = upsmfac +regulator.table.name = V_Regulator + +#---------------------------- QUARTZ CONFIGS ---------------------------- +samplejob.frequency=0 40 15 ? * * * +#samplejob.frequency=0 */5 * ? * * + +quartz.enabled=true +spring.quartz.job-store-type=jdbc +spring.quartz.jdbc.initialize-schema=never + +spring.quartz.properties.org.quartz.scheduler.instanceName=quartz-mail-event +spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO +#spring.quartz.properties.org.quartz.scheduler.instanceIdGenerator.class=com.helixz.quartz.demo.component.CustomQuartzInstanceIdGenerator +spring.quartz.properties.org.quartz.threadPool.threadCount=20 +spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX +#spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate +spring.quartz.properties.org.quartz.jobStore.useProperties=true +spring.quartz.properties.org.quartz.jobStore.misfireThreshold=60000 +spring.quartz.properties.org.quartz.jobStore.tablePrefix=qrtz_ +spring.quartz.properties.org.quartz.jobStore.isClustered=true +spring.quartz.properties.org.quartz.plugin.shutdownHook.class=org.quartz.plugins.management.ShutdownHookPlugin +spring.quartz.properties.org.quartz.plugin.shutdownHook.cleanShutdown=TRUE +spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate +#------------------------------------------------------ + diff --git a/java/claim/src/main/resources/templates/credentials-mail.ftl b/java/claim/src/main/resources/templates/credentials-mail.ftl new file mode 100644 index 000000000..98d67435c --- /dev/null +++ b/java/claim/src/main/resources/templates/credentials-mail.ftl @@ -0,0 +1,12 @@ + + +

Hi ${name}

+ +

"We are pleased to inform you that a ${credType} has been issued to you. You can view and download the credential by using the following link.

+ +
${idLink}
+ +

Thank you,

+

< Registration Credential Issuing Authority >

+ + diff --git a/java/claim/src/main/resources/templates/manual-pending-item-mail.ftl b/java/claim/src/main/resources/templates/manual-pending-item-mail.ftl new file mode 100644 index 000000000..9b7a765d5 --- /dev/null +++ b/java/claim/src/main/resources/templates/manual-pending-item-mail.ftl @@ -0,0 +1,12 @@ + + +

Hi ${name}

+ +

We have pending item for ${council}, which have been requested

+ +
Item name:

${itemName}

+ +

Thank you,

+

< Registration Credential Issuing Authority >

+ + \ No newline at end of file diff --git a/java/claim/src/main/resources/templates/pending-item-mail.ftl b/java/claim/src/main/resources/templates/pending-item-mail.ftl new file mode 100644 index 000000000..5f786b2d1 --- /dev/null +++ b/java/claim/src/main/resources/templates/pending-item-mail.ftl @@ -0,0 +1,47 @@ + + + + + + +

Hi ${regulatorName}

+ +

Following candidate has applied for registration certificate from your institution as claimed by candidate.

+ +

Candidate list:

+

+ + + + + + + + + <#list candidates as candidate > + + + + + + + + +
NameCred TypeReference NumberRegistration NumberEmail
${candidate.name} ${candidate.credType}${candidate.refNo}${candidate.registrationNumber}${candidate.emailAddress}
+

+ +
your response awaited
+ +

Thank you,

+

< Registration Credential Issuing Authority >

+ + diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java index 909e3458d..c225777ed 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/controller/RegistryEntityController.java @@ -26,11 +26,13 @@ import dev.sunbirdrc.registry.middleware.util.JSONUtil; import dev.sunbirdrc.registry.middleware.util.OSSystemFields; import dev.sunbirdrc.registry.model.dto.MailDto; +import dev.sunbirdrc.registry.model.dto.ManualPendingMailDTO; import dev.sunbirdrc.registry.service.FileStorageService; import dev.sunbirdrc.registry.service.impl.CertificateServiceImpl; import dev.sunbirdrc.registry.transform.Configuration; import dev.sunbirdrc.registry.transform.Data; import dev.sunbirdrc.registry.transform.ITransformer; +import dev.sunbirdrc.registry.util.ClaimRequestClient; import org.agrona.Strings; import dev.sunbirdrc.registry.util.DigiLockerUtils; import dev.sunbirdrc.registry.util.DocDetails; @@ -87,6 +89,9 @@ public class RegistryEntityController extends AbstractController { @Autowired private ViewTemplateManager viewTemplateManager; + @Autowired + private ClaimRequestClient claimRequestClient; + @Value("${authentication.enabled:true}") boolean securityEnabled; @Value("${certificate.enableExternalTemplates:false}") @@ -1433,5 +1438,30 @@ public JsonNode searchEntity(ObjectNode searchNode, String entityName) { return result; } + @RequestMapping(value = "/api/v1/{entityName}/sendPendingForeignItemMail", method = RequestMethod.POST) + public ResponseEntity sendPendingForeignItemMail(@PathVariable String entityName, + @RequestBody ManualPendingMailDTO pendingMailDTO, + HttpServletRequest request) { + + ResponseParams responseParams = new ResponseParams(); + Response response = new Response(Response.API_ID.SEND, "OK", responseParams); + + try { + registryHelper.authorizeInviteEntity(request, entityName); + + String mailStatus = claimRequestClient.sendPendingForeignItemMail(pendingMailDTO); + + response.setResult(mailStatus); + responseParams.setStatus(Response.Status.SUCCESSFUL); + + } catch (Exception exception) { + logger.error("Exception : {}", exception.getMessage()); + responseParams.setStatus(Response.Status.UNSUCCESSFUL); + responseParams.setErrmsg(exception.getMessage()); + return new ResponseEntity<>(responseParams, HttpStatus.INTERNAL_SERVER_ERROR); + } + + return new ResponseEntity<>(response, HttpStatus.OK); + } } diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/model/dto/ManualPendingMailDTO.java b/java/registry/src/main/java/dev/sunbirdrc/registry/model/dto/ManualPendingMailDTO.java new file mode 100644 index 000000000..fcb67e61a --- /dev/null +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/model/dto/ManualPendingMailDTO.java @@ -0,0 +1,15 @@ +package dev.sunbirdrc.registry.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ManualPendingMailDTO { + private String name; + private String emailAddress; + private String council; + private String itemName; +} diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/model/dto/PendingMailDTO.java b/java/registry/src/main/java/dev/sunbirdrc/registry/model/dto/PendingMailDTO.java new file mode 100644 index 000000000..349f9143e --- /dev/null +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/model/dto/PendingMailDTO.java @@ -0,0 +1,15 @@ +package dev.sunbirdrc.registry.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PendingMailDTO { + private String name; + private String emailAddress; + private String council; + private String itemName; +} diff --git a/java/registry/src/main/java/dev/sunbirdrc/registry/util/ClaimRequestClient.java b/java/registry/src/main/java/dev/sunbirdrc/registry/util/ClaimRequestClient.java index 739aae38d..8baa0a608 100644 --- a/java/registry/src/main/java/dev/sunbirdrc/registry/util/ClaimRequestClient.java +++ b/java/registry/src/main/java/dev/sunbirdrc/registry/util/ClaimRequestClient.java @@ -6,9 +6,7 @@ import dev.sunbirdrc.pojos.dto.ClaimDTO; import dev.sunbirdrc.registry.controller.RegistryController; import dev.sunbirdrc.registry.dao.Learner; -import dev.sunbirdrc.registry.model.dto.BarCode; -import dev.sunbirdrc.registry.model.dto.FileDto; -import dev.sunbirdrc.registry.model.dto.MailDto; +import dev.sunbirdrc.registry.model.dto.*; import lombok.NonNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -48,6 +46,8 @@ public class ClaimRequestClient { private static final String CLAIM_MULTI_FILE_UPLOAD = "/api/v1/files/upload/multiple"; private static String URL_APPENDER = "/"; + private static final String MAIL_SEND_PENDING_FOREIGN_ITEM_URL = "/api/v1/sendPendingForeignItemMail"; + ClaimRequestClient(@Value("${claims.url}") String claimRequestUrl, RestTemplate restTemplate) { this.claimRequestUrl = claimRequestUrl; this.restTemplate = restTemplate; @@ -242,8 +242,11 @@ public List uploadCLaimMultipleFiles(@NonNull MultipartFile[] files, St return fileDtoList; } + public String sendPendingForeignItemMail(ManualPendingMailDTO pendingMailDTO) { + String mailStatus = restTemplate.postForObject( + claimRequestUrl + MAIL_SEND_PENDING_FOREIGN_ITEM_URL, pendingMailDTO, String.class); - - - + logger.info("Pending foreign item mail status: " + mailStatus); + return mailStatus; + } }