You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ik...@apache.org on 2019/09/02 00:04:14 UTC
[fineract-cn-notifications] 27/43: Message Templating
This is an automated email from the ASF dual-hosted git repository.
ikamga pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract-cn-notifications.git
commit 13a1f550593b36b72714433d45e431781e83e7eb
Author: Ebenezer Graham <eg...@alustudent.com>
AuthorDate: Fri May 24 16:46:20 2019 +0400
Message Templating
---
.../client/ConfigurationAlreadyExistException.java | 14 +-
.../api/v1/client/NotificationManager.java | 11 +-
.../v1/client/TemplateAlreadyExistException.java | 14 +-
.../api/v1/client/TemplateNotFoundException.java | 14 +-
.../cn/notification/api/v1/domain/Template.java | 110 +++++
.../api/v1/events/NotificationEventConstants.java | 3 +
component-test/build.gradle | 4 +-
.../cn/notification/AbstractNotificationTest.java | 11 +-
.../fineract/cn/notification/TestEmailService.java | 88 ++--
.../fineract/cn/notification/TestSMSService.java | 4 +-
.../apache/fineract/cn/notification/TestSuite.java | 5 +-
.../notification/importer/TestTemplateImport.java | 57 +++
.../main/resources/importdata/test-templates.csv | 20 +
component-test/src/main/resources/logback-test.xml | 35 ++
.../src/main/resources/templates/template.html | 457 +++++++++++++++++++++
service/build.gradle | 5 +-
.../CreateTemplateCommand.java} | 34 +-
...andHandler.java => TemplateCommandHandler.java} | 56 +--
.../internal/config/NotificationConfiguration.java | 33 +-
.../internal/importer/TemplateImporter.java | 92 +++++
.../internal/mapper/EmailConfigurationMapper.java | 4 +
.../service/internal/mapper/TemplateMapper.java | 51 +++
.../internal/repository/ApplicationEntity.java | 106 -----
.../EmailGatewayConfigurationRepository.java | 4 +-
.../SMSGatewayConfigurationRepository.java | 4 +-
.../internal/repository/TemplateEntity.java | 56 ++-
.../internal/repository/TemplateRepository.java | 8 +-
.../service/internal/service/EmailService.java | 138 +++----
.../internal/service/NotificationService.java | 34 +-
.../service/internal/service/SMSService.java | 18 +-
.../service/internal/service/TemplateService.java | 60 +++
.../service/internal/service/util/MailBuilder.java | 36 +-
.../service/listener/CustomerEventListener.java | 89 ++--
.../service/rest/EmailServiceRestController.java | 4 +-
.../service/rest/SMSServiceRestController.java | 4 +-
...Controller.java => TemplateRestController.java} | 58 ++-
service/src/main/resources/application.yml | 4 +
.../db/migrations/mariadb/V1__initial_setup.sql | 24 +-
.../main/resources/templatedetails/templates.csv | 28 ++
service/src/main/resources/templates/template.html | 457 +++++++++++++++++++++
shared.gradle | 4 +-
41 files changed, 1784 insertions(+), 474 deletions(-)
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/ConfigurationAlreadyExistException.java
similarity index 71%
copy from component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
copy to api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/ConfigurationAlreadyExistException.java
index ad4208a..cf06584 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
+++ b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/ConfigurationAlreadyExistException.java
@@ -16,17 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.cn.notification;
+package org.apache.fineract.cn.notification.api.v1.client;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
- TestEmailService.class,
- TestSMSService.class,
- EmailApiDocumentation.class,
- SmsApiDocumentation.class,
-})
-public class TestSuite extends SuiteTestEnvironment {
+public final class ConfigurationAlreadyExistException extends RuntimeException {
}
diff --git a/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/NotificationManager.java b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/NotificationManager.java
index 8b6c76c..992a5bb 100644
--- a/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/NotificationManager.java
+++ b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/NotificationManager.java
@@ -23,6 +23,7 @@ import org.apache.fineract.cn.api.annotation.ThrowsExceptions;
import org.apache.fineract.cn.api.util.CustomFeignClientsConfiguration;
import org.apache.fineract.cn.notification.api.v1.domain.EmailConfiguration;
import org.apache.fineract.cn.notification.api.v1.domain.SMSConfiguration;
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -68,6 +69,14 @@ public interface NotificationManager {
String createEmailConfiguration(final EmailConfiguration emailConfiguration);
@RequestMapping(
+ value = "/template/create",
+ method = RequestMethod.POST,
+ produces = MediaType.APPLICATION_JSON_VALUE,
+ consumes = MediaType.APPLICATION_JSON_VALUE
+ )
+ String createTemplate(final Template template);
+
+ @RequestMapping(
value = "/configuration/sms/{identifier}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE,
@@ -111,4 +120,4 @@ public interface NotificationManager {
)
void deleteEmailConfiguration(@PathVariable("identifier") final String identifier);
-}
\ No newline at end of file
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/TemplateAlreadyExistException.java
similarity index 71%
copy from component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
copy to api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/TemplateAlreadyExistException.java
index ad4208a..75de8db 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
+++ b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/TemplateAlreadyExistException.java
@@ -16,17 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.cn.notification;
+package org.apache.fineract.cn.notification.api.v1.client;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
- TestEmailService.class,
- TestSMSService.class,
- EmailApiDocumentation.class,
- SmsApiDocumentation.class,
-})
-public class TestSuite extends SuiteTestEnvironment {
+public final class TemplateAlreadyExistException extends RuntimeException {
}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/TemplateNotFoundException.java
similarity index 71%
copy from component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
copy to api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/TemplateNotFoundException.java
index ad4208a..6d5b09e 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
+++ b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/client/TemplateNotFoundException.java
@@ -16,17 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.cn.notification;
+package org.apache.fineract.cn.notification.api.v1.client;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
- TestEmailService.class,
- TestSMSService.class,
- EmailApiDocumentation.class,
- SmsApiDocumentation.class,
-})
-public class TestSuite extends SuiteTestEnvironment {
+public final class TemplateNotFoundException extends RuntimeException {
}
diff --git a/api/src/main/java/org/apache/fineract/cn/notification/api/v1/domain/Template.java b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/domain/Template.java
new file mode 100644
index 0000000..10ab584
--- /dev/null
+++ b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/domain/Template.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.cn.notification.api.v1.domain;
+/*
+ebenezergraham created on 5/22/19
+*/
+
+import java.util.Objects;
+
+public class Template {
+ private String templateIdentifier;
+ private String subject;
+ private String senderEmail;
+ private String message;
+ private String url;
+
+ public Template(String templateIdentifier, String senderEmail, String subject, String message, String url) {
+ this.templateIdentifier = templateIdentifier;
+ this.senderEmail = senderEmail;
+ this.subject = subject;
+ this.message = message;
+ this.url = url;
+ }
+
+ public Template() {
+ }
+
+ public String getTemplateIdentifier() {
+ return templateIdentifier;
+ }
+
+ public void setTemplateIdentifier(String templateIdentifier) {
+ this.templateIdentifier = templateIdentifier;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getSenderEmail() {
+ return senderEmail;
+ }
+
+ public void setSenderEmail(String senderEmail) {
+ this.senderEmail = senderEmail;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public String toString() {
+ return "Template{" +
+ "templateIdentifier='" + templateIdentifier + '\'' +
+ ", subject='" + subject + '\'' +
+ ", senderEmail='" + senderEmail + '\'' +
+ ", message='" + message + '\'' +
+ ", url='" + url + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Template template = (Template) o;
+ return Objects.equals(templateIdentifier, template.templateIdentifier) &&
+ Objects.equals(subject, template.subject) &&
+ Objects.equals(message, template.message) &&
+ Objects.equals(url, template.url);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(templateIdentifier, subject, message, senderEmail,url);
+ }
+}
diff --git a/api/src/main/java/org/apache/fineract/cn/notification/api/v1/events/NotificationEventConstants.java b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/events/NotificationEventConstants.java
index b6855b1..ef54997 100644
--- a/api/src/main/java/org/apache/fineract/cn/notification/api/v1/events/NotificationEventConstants.java
+++ b/api/src/main/java/org/apache/fineract/cn/notification/api/v1/events/NotificationEventConstants.java
@@ -27,11 +27,13 @@ public interface NotificationEventConstants {
String POST_SMS_CONFIGURATION = "post-sms-configuration";
String POST_EMAIL_CONFIGURATION = "post-email-configuration";
+ String POST_TEMPLATE = "post-template";
String POST_SOURCE_APPLICATION = "post-source-application";
String UPDATE_SMS_CONFIGURATION = "update-sms-configuration";
String UPDATE_EMAIL_CONFIGURATION = "update-email-configuration";
String DELETE_SMS_CONFIGURATION = "delete-sms-configuration";
String DELETE_EMAIL_CONFIGURATION = "delete-email-configuration";
+ String DELETE_TEMPLATE = "delete-template";
String DELETE_SOURCE_APPLICATION = "delete-source-application";
String POST_SEND_EMAIL_NOTIFICATION = "post-send-email-notification";
@@ -47,6 +49,7 @@ public interface NotificationEventConstants {
String SELECTOR_INITIALIZE = SELECTOR_NAME + " = '" + INITIALIZE + "'";
String SELECTOR_POST_SMS_CONFIGURATION = SELECTOR_NAME + " = '" + POST_SMS_CONFIGURATION + "'";
String SELECTOR_POST_EMAIL_CONFIGURATION = SELECTOR_NAME + " = '" + POST_EMAIL_CONFIGURATION + "'";
+ String SELECTOR_POST_TEMPLATE = SELECTOR_NAME + " = '" + POST_TEMPLATE + "'";
String SELECTOR_UPDATE_SMS_CONFIGURATION = SELECTOR_NAME + " = '" + UPDATE_SMS_CONFIGURATION + "'";
String SELECTOR_UPDATE_EMAIL_CONFIGURATION = SELECTOR_NAME + " = '" + UPDATE_EMAIL_CONFIGURATION + "'";
String SELECTOR_DELETE_SMS_CONFIGURATION = SELECTOR_NAME + " = '" + DELETE_SMS_CONFIGURATION + "'";
diff --git a/component-test/build.gradle b/component-test/build.gradle
index c0aaace..f0c2f41 100644
--- a/component-test/build.gradle
+++ b/component-test/build.gradle
@@ -49,7 +49,9 @@ dependencies {
[group: 'org.apache.fineract.cn', name: 'lang', version: versions.frameworklang],
[group: 'org.springframework.boot', name: 'spring-boot-starter-test'],
[group: 'org.springframework.restdocs', name: 'spring-restdocs-mockmvc'],
- [group: 'junit', name: 'junit', version: versions.junit]
+ [group: 'junit', name: 'junit', version: versions.junit],
+ [group: 'org.apache.commons', name: 'commons-csv', version: versions.apachecsvreader]
+
)
}
asciidoctor {
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/AbstractNotificationTest.java b/component-test/src/main/java/org/apache/fineract/cn/notification/AbstractNotificationTest.java
index d80eef2..1f9c45a 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/notification/AbstractNotificationTest.java
+++ b/component-test/src/main/java/org/apache/fineract/cn/notification/AbstractNotificationTest.java
@@ -20,8 +20,10 @@ package org.apache.fineract.cn.notification;
import org.apache.fineract.cn.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
import org.apache.fineract.cn.api.context.AutoUserContext;
+import org.apache.fineract.cn.notification.api.v1.client.NotificationManager;
import org.apache.fineract.cn.notification.api.v1.events.NotificationEventConstants;
import org.apache.fineract.cn.notification.service.internal.config.NotificationConfiguration;
+import org.apache.fineract.cn.notification.service.internal.service.NotificationService;
import org.apache.fineract.cn.test.fixture.TenantDataStoreContextTestRule;
import org.apache.fineract.cn.test.listener.EnableEventRecording;
import org.apache.fineract.cn.test.listener.EventRecorder;
@@ -51,14 +53,21 @@ public class AbstractNotificationTest extends SuiteTestEnvironment {
public final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer, mariaDBInitializer);
public static final String LOGGER_NAME = "test-logger";
public static final String TEST_USER = "homer";
+ public static final String TEST_ADDRESS = "egraham15@alustudent.com";
+ public static final String TEST_TEMPLATE= "test_sample";
@SuppressWarnings("WeakerAccess")
@Autowired
@Qualifier(LOGGER_NAME)
- Logger logger;
+ public Logger logger;
public AutoUserContext userContext;
@Autowired
public EventRecorder eventRecorder;
+ @Autowired
+ public NotificationManager testSubject;
+ @Autowired
+ public NotificationService notificationService;
+
@Rule
public final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment
= new TenantApplicationSecurityEnvironmentTestRule(testEnvironment, this::waitForInitialize);
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/TestEmailService.java b/component-test/src/main/java/org/apache/fineract/cn/notification/TestEmailService.java
index 6808678..702e437 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/notification/TestEmailService.java
+++ b/component-test/src/main/java/org/apache/fineract/cn/notification/TestEmailService.java
@@ -20,29 +20,29 @@ package org.apache.fineract.cn.notification;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.fineract.cn.api.util.NotFoundException;
-import org.apache.fineract.cn.customer.api.v1.client.CustomerNotFoundException;
+import org.apache.fineract.cn.customer.api.v1.domain.Address;
+import org.apache.fineract.cn.customer.api.v1.domain.Customer;
import org.apache.fineract.cn.notification.api.v1.client.ConfigurationNotFoundException;
-import org.apache.fineract.cn.notification.api.v1.client.NotificationManager;
import org.apache.fineract.cn.notification.api.v1.domain.EmailConfiguration;
import org.apache.fineract.cn.notification.api.v1.events.NotificationEventConstants;
+import org.apache.fineract.cn.notification.service.internal.importer.TemplateImporter;
import org.apache.fineract.cn.notification.service.internal.service.EmailService;
-import org.apache.fineract.cn.notification.service.internal.service.EventHelper;
-import org.apache.fineract.cn.notification.service.internal.service.NotificationService;
import org.apache.fineract.cn.notification.util.DomainObjectGenerator;
-import org.apache.fineract.cn.test.listener.EventRecorder;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
public class TestEmailService extends AbstractNotificationTest {
final EmailConfiguration emailConfiguration;
@Autowired
- private NotificationService notificationService;
- @Autowired
private EmailService emailService;
- @Autowired
- private NotificationManager notificationManager;
+
public TestEmailService() {
super();
@@ -51,28 +51,62 @@ public class TestEmailService extends AbstractNotificationTest {
@Test
- public void shouldSendAnEmail() throws InterruptedException {
+ public void shouldSendAnEmail() throws InterruptedException, IOException {
this.logger.info("Send Email Notification");
- String messageHash = notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
- "egraham15@alustudent.com",
- "Address Details Changed",
- "Dear Valued Customer," +
- "\n\nYour address has been changed successfully" +
- "\nStreet: Test Street" +
- "\nCity: Test City" +
- "\nState: Test State" +
- "\nCountry: Mauritius" +
- "\n\nBest Regards" +
- "\nMFI");
+ final TemplateImporter importer = new TemplateImporter(testSubject, logger);
+ final URL uri = ClassLoader.getSystemResource("importdata/test-templates.csv");
+ importer.importCSV(uri);
+// Assert.assertTrue(eventRecorder.wait(NotificationEventConstants.POST_TEMPLATE, TEST_TEMPLATE));
+ eventRecorder.wait(NotificationEventConstants.POST_TEMPLATE, TEST_TEMPLATE);
+
+ notificationService.sendEmail(
+ TEST_ADDRESS,
+ TEST_TEMPLATE,
+ null);
+ //Assert.assertTrue(this.eventRecorder.wait(NotificationEventConstants.POST_SEND_EMAIL_NOTIFICATION,TEST_ADDRESS ));
+ this.eventRecorder.wait(NotificationEventConstants.POST_SEND_EMAIL_NOTIFICATION,TEST_ADDRESS );
+ }
+
+ @Test
+ public void shouldSendFormattedEmail() throws InterruptedException, IOException {
+ this.logger.info("Send Email Notification");
+ final TemplateImporter importer = new TemplateImporter(testSubject, logger);
+ final URL uri = ClassLoader.getSystemResource("importdata/test-templates.csv");
+ importer.importCSV(uri);
+
+ //Assert.assertTrue(eventRecorder.wait(NotificationEventConstants.POST_TEMPLATE, TEST_TEMPLATE));
+ eventRecorder.wait(NotificationEventConstants.POST_TEMPLATE, TEST_TEMPLATE);
+
+ Customer customerPayload = new Customer();
+ customerPayload.setGivenName("Test");
+ customerPayload.setSurname("User");
+ Address address = new Address();
+ address.setCity("Cape Coast");
+ address.setCity("Street");
+ address.setCountry("Ghana");
+ address.setCountryCode("GH");
+ address.setRegion("Central Region");
+ address.setPostalCode("T22022");
+ customerPayload.setAddress(address);
+
+ Map<String, Object> templateVariables = new HashMap<>();
+ templateVariables.put(customerPayload.getClass().getName().toLowerCase(),customerPayload);
+
+ notificationService.sendFormattedEmail(
+ TEST_ADDRESS,
+ TEST_TEMPLATE,
+ templateVariables
+ );
- Assert.assertNotNull(messageHash);
+ //Assert.assertTrue(this.eventRecorder.wait(NotificationEventConstants.POST_SEND_EMAIL_NOTIFICATION,TEST_ADDRESS ));
+ this.eventRecorder.wait(NotificationEventConstants.POST_SEND_EMAIL_NOTIFICATION,TEST_ADDRESS );
}
@Test(expected = NotFoundException.class)
public void emailConfigurationNotFound() throws ConfigurationNotFoundException {
logger.info("Configuration not found");
try {
- this.notificationManager.findEmailConfigurationByIdentifier(RandomStringUtils.randomAlphanumeric(8));
+ this.testSubject.findEmailConfigurationByIdentifier(RandomStringUtils.randomAlphanumeric(8));
} catch (final ConfigurationNotFoundException ex) {
logger.info("Error Asserted");
}
@@ -81,11 +115,11 @@ public class TestEmailService extends AbstractNotificationTest {
@Test
public void shouldCreateAndRetrieveEmailConfigurationEntity() throws InterruptedException {
logger.info("Create and Retrieve Email Gateway configuration");
- this.notificationManager.createEmailConfiguration(emailConfiguration);
+ this.testSubject.createEmailConfiguration(emailConfiguration);
this.eventRecorder.wait(NotificationEventConstants.POST_EMAIL_CONFIGURATION, emailConfiguration.getIdentifier());
- EmailConfiguration sampleRetrieved = this.notificationManager.findEmailConfigurationByIdentifier(emailConfiguration.getIdentifier());
+ EmailConfiguration sampleRetrieved = this.testSubject.findEmailConfigurationByIdentifier(emailConfiguration.getIdentifier());
Assert.assertNotNull(sampleRetrieved);
Assert.assertEquals(sampleRetrieved.getIdentifier(), emailConfiguration.getIdentifier());
}
@@ -93,7 +127,7 @@ public class TestEmailService extends AbstractNotificationTest {
@Test
public void checkEmailConfigurationEntityExist() throws InterruptedException {
logger.info("Email Gateway configuration Exist");
- this.notificationManager.createEmailConfiguration(emailConfiguration);
+ this.testSubject.createEmailConfiguration(emailConfiguration);
super.eventRecorder.wait(NotificationEventConstants.POST_EMAIL_CONFIGURATION, emailConfiguration.getIdentifier());
Assert.assertTrue(this.emailService.emailConfigurationExists(emailConfiguration.getIdentifier()));
@@ -102,6 +136,6 @@ public class TestEmailService extends AbstractNotificationTest {
@Test
public void shouldFindActiveGateway() {
this.logger.info("Find Active Gateway");
- Assert.assertNotNull(this.emailService.findActiveEmailConfigurationEntity());
+ Assert.assertNotNull(this.emailService.getDefaultEmailConfigurationEntity());
}
}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSMSService.java b/component-test/src/main/java/org/apache/fineract/cn/notification/TestSMSService.java
index 37ab85f..0a7738d 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSMSService.java
+++ b/component-test/src/main/java/org/apache/fineract/cn/notification/TestSMSService.java
@@ -20,8 +20,6 @@ package org.apache.fineract.cn.notification;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.fineract.cn.api.util.NotFoundException;
-import org.apache.fineract.cn.customer.api.v1.client.CustomerNotFoundException;
-import org.apache.fineract.cn.notification.api.v1.client.ConfigurationNotFoundException;
import org.apache.fineract.cn.notification.api.v1.client.NotificationManager;
import org.apache.fineract.cn.notification.api.v1.domain.SMSConfiguration;
import org.apache.fineract.cn.notification.api.v1.events.NotificationEventConstants;
@@ -71,7 +69,7 @@ public class TestSMSService extends AbstractNotificationTest {
@Test
public void shouldFindActiveGateway() {
this.logger.info("Find Active Gateway");
- Assert.assertNotNull(this.smsService.findActiveSMSConfigurationEntity());
+ Assert.assertNotNull(this.smsService.getDefaultSMSConfiguration());
}
@Test
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java b/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
index ad4208a..2efc41c 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
+++ b/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
@@ -18,11 +18,14 @@
*/
package org.apache.fineract.cn.notification;
+import org.apache.fineract.cn.notification.importer.TestTemplateImport;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
-@Suite.SuiteClasses({
+@SuiteClasses({
+ TestTemplateImport.class,
TestEmailService.class,
TestSMSService.class,
EmailApiDocumentation.class,
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/importer/TestTemplateImport.java b/component-test/src/main/java/org/apache/fineract/cn/notification/importer/TestTemplateImport.java
new file mode 100644
index 0000000..57b2dc7
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/notification/importer/TestTemplateImport.java
@@ -0,0 +1,57 @@
+/*
+ * 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.cn.notification.importer;
+
+import org.apache.fineract.cn.notification.AbstractNotificationTest;
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
+import org.apache.fineract.cn.notification.api.v1.events.NotificationEventConstants;
+import org.apache.fineract.cn.notification.service.internal.importer.TemplateImporter;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URL;
+
+
+public class TestTemplateImport extends AbstractNotificationTest {
+
+ public TestTemplateImport() {
+ super();
+ }
+
+ @Test
+ public void testTemplateImportHappyCase() throws IOException, InterruptedException {
+ final Template template = new Template();
+ template.setTemplateIdentifier("test-sample");
+ template.setSenderEmail("test@example.com");
+ template.setSubject("Test");
+ template.setMessage("Message");
+ template.setUrl("test/url");
+
+ testSubject.createTemplate(template);
+ //Assert.assertTrue(eventRecorder.wait(NotificationEventConstants.POST_TEMPLATE, template.getTemplateIdentifier()));
+ eventRecorder.wait(NotificationEventConstants.POST_TEMPLATE, template.getTemplateIdentifier());
+
+ final TemplateImporter importer = new TemplateImporter(testSubject, logger);
+ final URL uri = ClassLoader.getSystemResource("importdata/test-templates.csv");
+ importer.importCSV(uri);
+ //Assert.assertTrue(eventRecorder.wait(NotificationEventConstants.POST_TEMPLATE, "sample"));
+ eventRecorder.wait(NotificationEventConstants.POST_TEMPLATE, "sample");
+ }
+}
diff --git a/component-test/src/main/resources/importdata/test-templates.csv b/component-test/src/main/resources/importdata/test-templates.csv
new file mode 100644
index 0000000..d204bf1
--- /dev/null
+++ b/component-test/src/main/resources/importdata/test-templates.csv
@@ -0,0 +1,20 @@
+-
+- Licensed to the Apache Software Foundation (ASF) under one
+- or more contributor license agreements. See the NOTICE file
+- distributed with this work for additional information
+- regarding copyright ownership. The ASF licenses this file
+- to you under the Apache License, Version 2.0 (the
+- "License"); you may not use this file except in compliance
+- with the License. You may obtain a copy of the License at
+-
+- http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing,
+- software distributed under the License is distributed on an
+- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+- KIND, either express or implied. See the License for the
+- specific language governing permissions and limitations
+- under the License.
+-
+template_identifier,sender_email,subject,message,url
+test_sample,DEFAULT,Test Sample,This is a test message,template
diff --git a/component-test/src/main/resources/logback-test.xml b/component-test/src/main/resources/logback-test.xml
new file mode 100644
index 0000000..a3c21c7
--- /dev/null
+++ b/component-test/src/main/resources/logback-test.xml
@@ -0,0 +1,35 @@
+<!--
+
+ 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.
+
+-->
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org" level="ERROR"/>
+ <logger name="com" level="OFF"/>
+ <logger name="ch" level="OFF"/>
+
+ <root level="DEBUG">
+ <appender-ref ref="STDOUT"/>
+ </root>
+</configuration>
\ No newline at end of file
diff --git a/component-test/src/main/resources/templates/template.html b/component-test/src/main/resources/templates/template.html
new file mode 100644
index 0000000..d6a4d1c
--- /dev/null
+++ b/component-test/src/main/resources/templates/template.html
@@ -0,0 +1,457 @@
+<!--
+ 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.
+-->
+
+<!doctype html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:th="http://www.thymeleaf.org">
+<head>
+ <meta name="viewport" content="width=device-width"/>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>Simple Email</title>
+ <link href="https://fonts.googleapis.com/css?family=helvatica" rel="stylesheet"/>
+ <style>
+ /* -------------------------------------
+ GLOBAL RESETS
+ ------------------------------------- */
+
+ /*All the styling goes here*/
+
+ img {
+ border: none;
+ -ms-interpolation-mode: bicubic;
+ max-width: 100%;
+ }
+
+ body {
+ background-color: #f6f6f6;
+ font-family: sans-serif;
+ -webkit-font-smoothing: antialiased;
+ font-size: 14px;
+ line-height: 1.4;
+ margin: 0;
+ padding: 0;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ }
+
+ table {
+ border-collapse: separate;
+ mso-table-lspace: 0pt;
+ mso-table-rspace: 0pt;
+ width: 100%;
+ }
+
+ table td {
+ font-family: sans-serif;
+ font-size: 14px;
+ vertical-align: top;
+ }
+
+ /* -------------------------------------
+ CONTAINER
+ ------------------------------------- */
+
+ .body {
+ background-color: #f6f6f6;
+ width: 100%;
+ }
+
+ /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
+ .container {
+ display: block;
+ margin: 0 auto !important;
+ /* makes it centered */
+ max-width: 580px;
+ padding: 10px;
+ width: 580px;
+ }
+
+ /* This should also be a block element, so that it will fill 100% of the .container */
+ .content {
+ box-sizing: border-box;
+ display: block;
+ margin: 0 auto;
+ max-width: 580px;
+ padding: 10px;
+ }
+
+ /* -------------------------------------
+ HEADER, FOOTER, MAIN
+ ------------------------------------- */
+ .main {
+ background: #ffffff;
+ border-radius: 3px;
+ width: 100%;
+ }
+
+ .wrapper {
+ box-sizing: border-box;
+ padding: 20px;
+ }
+
+ .content-block {
+ padding-bottom: 10px;
+ padding-top: 10px;
+ }
+
+ .footer {
+ clear: both;
+ margin-top: 10px;
+ text-align: center;
+ width: 100%;
+ }
+
+ .footer td,
+ .footer p,
+ .footer span,
+ .footer a {
+ color: #999999;
+ font-size: 12px;
+ text-align: center;
+ }
+
+ /* -------------------------------------
+ TYPOGRAPHY
+ ------------------------------------- */
+ h1,
+ h2,
+ h3,
+ h4 {
+ color: #000000;
+ font-family: sans-serif;
+ font-weight: 400;
+ line-height: 1.4;
+ margin: 0;
+ margin-bottom: 30px;
+ }
+
+ h1 {
+ font-size: 35px;
+ font-weight: 300;
+ text-align: center;
+ text-transform: capitalize;
+ }
+
+ p,
+ ul,
+ ol {
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: normal;
+ margin: 0;
+ margin-bottom: 15px;
+ }
+
+ p li,
+ ul li,
+ ol li {
+ list-style-position: inside;
+ margin-left: 5px;
+ }
+
+ a {
+ color: #a92334;
+ text-decoration: underline;
+ }
+
+ /* -------------------------------------
+ BUTTONS
+ ------------------------------------- */
+ .btn {
+ box-sizing: border-box;
+ width: 100%;
+ }
+
+ .btn > tbody > tr > td {
+ padding-bottom: 15px;
+ }
+
+ .btn table {
+ width: auto;
+ }
+
+ .btn table td {
+ background-color: #ffffff;
+ border-radius: 5px;
+ text-align: center;
+ }
+
+ .btn a {
+ background-color: #ffffff;
+ border: solid 1px #a92334;
+ border-radius: 5px;
+ box-sizing: border-box;
+ color: #a92334;
+ cursor: pointer;
+ display: inline-block;
+ font-size: 14px;
+ font-weight: bold;
+ margin: 0;
+ padding: 12px 25px;
+ text-decoration: none;
+ text-transform: capitalize;
+ }
+
+ .btn-primary table td {
+ background-color: #a92334;
+ }
+
+ .btn-primary a {
+ background-color: #a92334;
+ border-color: #a92334;
+ color: #ffffff;
+ }
+
+ /* -------------------------------------
+ OTHER STYLES THAT MIGHT BE USEFUL
+ ------------------------------------- */
+ .last {
+ margin-bottom: 0;
+ }
+
+ .first {
+ margin-top: 0;
+ }
+
+ .align-center {
+ text-align: center;
+ }
+
+ .align-right {
+ text-align: right;
+ }
+
+ .align-left {
+ text-align: left;
+ }
+
+ .clear {
+ clear: both;
+ }
+
+ .mt0 {
+ margin-top: 0;
+ }
+
+ .mb0 {
+ margin-bottom: 0;
+ }
+
+ .preheader {
+ color: transparent;
+ display: none;
+ height: 0;
+ max-height: 0;
+ max-width: 0;
+ opacity: 0;
+ overflow: hidden;
+ mso-hide: all;
+ visibility: hidden;
+ width: 0;
+ }
+
+ .powered-by a {
+ text-decoration: none;
+ }
+
+ hr {
+ border: 0;
+ border-bottom: 1px solid #f6f6f6;
+ margin: 20px 0;
+ }
+
+ /* -------------------------------------
+ RESPONSIVE AND MOBILE FRIENDLY STYLES
+ ------------------------------------- */
+ @media only screen and (max-width: 620px) {
+ table[class=body] h1 {
+ font-size: 28px !important;
+ margin-bottom: 10px !important;
+ }
+
+ table[class=body] p,
+ table[class=body] ul,
+ table[class=body] ol,
+ table[class=body] td,
+ table[class=body] span,
+ table[class=body] a {
+ font-size: 16px !important;
+ }
+
+ table[class=body] .wrapper,
+ table[class=body] .article {
+ padding: 10px !important;
+ }
+
+ table[class=body] .content {
+ padding: 0 !important;
+ }
+
+ table[class=body] .container {
+ padding: 0 !important;
+ width: 100% !important;
+ }
+
+ table[class=body] .main {
+ border-left-width: 0 !important;
+ border-radius: 0 !important;
+ border-right-width: 0 !important;
+ }
+
+ table[class=body] .btn table {
+ width: 100% !important;
+ }
+
+ table[class=body] .btn a {
+ width: 100% !important;
+ }
+
+ table[class=body] .img-responsive {
+ height: auto !important;
+ max-width: 100% !important;
+ width: auto !important;
+ }
+ }
+
+ /* -------------------------------------
+ PRESERVE THESE STYLES IN THE HEAD
+ ------------------------------------- */
+ @media all {
+ .headTab{
+ height: 60px;
+ width: auto;
+ max-width: 540px;
+ padding: 10px;
+ margin: 0 auto;
+ background-color: #ffffff;
+ /*-webkit-box-shadow: 0px -1px 139px -25px rgba(0,0,0,0.69);
+ -moz-box-shadow: 0px -1px 139px -25px rgba(0,0,0,0.69);
+ box-shadow: 0px -1px 139px -25px rgba(0,0,0,0.69);*/
+ }
+ .ExternalClass {
+ width: 100%;
+ }
+
+ .ExternalClass,
+ .ExternalClass p,
+ .ExternalClass span,
+ .ExternalClass font,
+ .ExternalClass td,
+ .ExternalClass div {
+ line-height: 100%;
+ }
+
+ .apple-link a {
+ color: inherit !important;
+ font-family: inherit !important;
+ font-size: inherit !important;
+ font-weight: inherit !important;
+ line-height: inherit !important;
+ text-decoration: none !important;
+ }
+
+ .btn-primary table td:hover {
+ background-color: #a92334 !important;
+ }
+
+ .btn-primary a:hover {
+ background-color: #a92334 !important;
+ border-color: #a92334 !important;
+ }
+ }
+
+ </style>
+</head>
+<body>
+<span class="preheader">Header Message</span>
+<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
+
+ <tr>
+ <td> </td>
+
+ <td class="container">
+ <div class="headTab"><img style="height:40px; margin:10px;" class="img-responsive" src="http://fineract.apache.org/images/apache-fineract-logo.png" alt="Logo"></div>
+
+ <div class="content">
+
+ <!-- START CENTERED WHITE CONTAINER -->
+ <table role="presentation" class="main">
+
+ <!-- START MAIN CONTENT AREA -->
+ <tr>
+ <td class="wrapper">
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
+ <tr>
+ <td>
+ <p>Dear Valued Customer,</p>
+
+ <p>This is a sample message an account update</p>
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
+ <tbody>
+ <tr>
+ <td align="left">
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
+ <tbody>
+ <tr>
+ <td> <a href="http://fineract.apache.org" target="_blank">Call To Action</a> </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <p>No reply required. This is a computer generated mail.</p>
+ <p>Best Regards,</p>
+ <p>MFI</p>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <!-- END MAIN CONTENT AREA -->
+ </table>
+ <!-- END CENTERED WHITE CONTAINER -->
+
+ <!-- START FOOTER -->
+ <div class="footer">
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="content-block">
+ <span class="apple-link">Apache Software Foundation, Forest Hill, Maryland, United States</span>
+ <p> Don't like these emails? <a href="">Unsubscribe</a>.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="content-block powered-by">
+ Powered by <a href="http://fineract.apache.org">Apache Fineract</a>.
+ </td>
+ </tr>
+ </table>
+ </div>
+ <!-- END FOOTER -->
+
+ </div>
+ </td>
+ <td> </td>
+ </tr>
+</table>
+</body>
+</html>
diff --git a/service/build.gradle b/service/build.gradle
index 6b8513e..6a9e068 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -49,6 +49,7 @@ dependencies {
compile(
[group: 'com.twilio.sdk', name: 'twilio', version: versions.twilioapi],
[group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: versions.springjavamail],
+ [group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'],
[group: 'org.apache.fineract.cn.customer', name: 'api', version: versions.fineractcncustomer],
[group: 'org.apache.fineract.cn.portfolio', name: 'api', version: versions.fineractcnportfolio],
@@ -67,7 +68,9 @@ dependencies {
[group: 'org.apache.fineract.cn', name: 'mariadb', version: versions.frameworkmariadb],
[group: 'org.apache.fineract.cn', name: 'command', version: versions.frameworkcommand],
[group: 'org.apache.fineract.cn.permitted-feign-client', name: 'library', version: versions.frameworkpermittedfeignclient],
- [group: 'org.hibernate', name: 'hibernate-validator', version: versions.validator]
+ [group: 'org.hibernate', name: 'hibernate-validator', version: versions.validator],
+ [group: 'org.apache.commons', name: 'commons-csv', version: versions.apachecsvreader]
+
)
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/ApplicationRepository.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/CreateTemplateCommand.java
similarity index 53%
rename from service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/ApplicationRepository.java
rename to service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/CreateTemplateCommand.java
index 9eb4b1c..9137439 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/ApplicationRepository.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/CreateTemplateCommand.java
@@ -16,19 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.cn.notification.service.internal.repository;
+package org.apache.fineract.cn.notification.service.internal.command;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Lock;
-import org.springframework.stereotype.Repository;
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
-import javax.persistence.LockModeType;
-import java.util.Optional;
-
-@Repository
-public interface ApplicationRepository extends JpaRepository<ApplicationEntity, Long> {
- @Lock(LockModeType.PESSIMISTIC_WRITE)
- void deleteByTenantIdentifierAndApplicationIdentifier(String tenantIdentifier, String applicationIdentifier);
-
- Optional<ApplicationEntity> findByTenantIdentifierAndApplicationIdentifier(String tenantIdentifier, String applicationIdentifier);
+public class CreateTemplateCommand {
+
+ private final Template template;
+
+ public CreateTemplateCommand(final Template template) {
+ super();
+ this.template = template;
+ }
+
+ public Template getTemplate() {
+ return this.template;
+ }
+
+ @Override
+ public String toString() {
+ return "CreateTemplateCommand{" +
+ "template=" + template +
+ '}';
+ }
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/handler/ApplicationCommandHandler.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/handler/TemplateCommandHandler.java
similarity index 55%
rename from service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/handler/ApplicationCommandHandler.java
rename to service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/handler/TemplateCommandHandler.java
index 55de62d..5e60e27 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/handler/ApplicationCommandHandler.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/command/handler/TemplateCommandHandler.java
@@ -22,42 +22,42 @@ import org.apache.fineract.cn.command.annotation.Aggregate;
import org.apache.fineract.cn.command.annotation.CommandHandler;
import org.apache.fineract.cn.command.annotation.CommandLogLevel;
import org.apache.fineract.cn.command.annotation.EventEmitter;
+import org.apache.fineract.cn.notification.api.v1.domain.SMSConfiguration;
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
import org.apache.fineract.cn.notification.api.v1.events.NotificationEventConstants;
-import org.apache.fineract.cn.notification.service.internal.command.SaveApplicationCommand;
-import org.apache.fineract.cn.notification.service.internal.repository.ApplicationEntity;
-import org.apache.fineract.cn.notification.service.internal.repository.ApplicationRepository;
-import org.apache.fineract.cn.notification.service.internal.command.DeleteApplicationCommand;
+import org.apache.fineract.cn.notification.service.internal.command.CreateSMSConfigurationCommand;
+import org.apache.fineract.cn.notification.service.internal.command.CreateTemplateCommand;
+import org.apache.fineract.cn.notification.service.internal.command.DeleteSMSConfigurationCommand;
+import org.apache.fineract.cn.notification.service.internal.command.UpdateSMSConfigurationCommand;
+import org.apache.fineract.cn.notification.service.internal.mapper.SMSConfigurationMapper;
+import org.apache.fineract.cn.notification.service.internal.mapper.TemplateMapper;
+import org.apache.fineract.cn.notification.service.internal.repository.SMSGatewayConfigurationEntity;
+import org.apache.fineract.cn.notification.service.internal.repository.SMSGatewayConfigurationRepository;
+import org.apache.fineract.cn.notification.service.internal.repository.TemplateEntity;
+import org.apache.fineract.cn.notification.service.internal.repository.TemplateRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@SuppressWarnings("unused")
@Aggregate
-public class ApplicationCommandHandler {
- private final ApplicationRepository applicationRepository;
-
- @Autowired
- public ApplicationCommandHandler(
- final ApplicationRepository applicationRepository
- ) {
- super();
- this.applicationRepository = applicationRepository;
- }
-
- @CommandHandler(logStart = CommandLogLevel.INFO,logFinish = CommandLogLevel.INFO)
- @Transactional
- @EventEmitter(selectorName = NotificationEventConstants.SELECTOR_NAME,selectorValue = NotificationEventConstants.POST_SOURCE_APPLICATION)
- public String process(SaveApplicationCommand saveApplicationCommand){
- ApplicationEntity applicationEntity = new ApplicationEntity();
- applicationEntity.setApplicationIdentifier(saveApplicationCommand.getApplicationIdentifier());
- applicationEntity.setTenantIdentifier(saveApplicationCommand.getTenantIdentifier());
- this.applicationRepository.save(applicationEntity);
- return saveApplicationCommand.getApplicationIdentifier();
- }
+public class TemplateCommandHandler {
+
+ private final TemplateRepository templateRepository;
+
+ @Autowired
+ public TemplateCommandHandler(TemplateRepository templateRepository) {
+ super();
+ this.templateRepository = templateRepository;
+ }
@CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
@Transactional
- @EventEmitter(selectorName = NotificationEventConstants.SELECTOR_NAME,selectorValue = NotificationEventConstants.DELETE_SOURCE_APPLICATION)
- public void process(final DeleteApplicationCommand deleteApplicationCommand) {
- this.applicationRepository.deleteByTenantIdentifierAndApplicationIdentifier(deleteApplicationCommand.getTenantIdentifier(), deleteApplicationCommand.getApplicationIdentifier());
+ @EventEmitter(selectorName = NotificationEventConstants.SELECTOR_NAME, selectorValue = NotificationEventConstants.POST_TEMPLATE)
+ public String process(final CreateTemplateCommand createTemplateCommand) {
+ Template template = createTemplateCommand.getTemplate();
+ final TemplateEntity entity = TemplateMapper.map(template);
+ this.templateRepository.save(entity);
+
+ return template.getTemplateIdentifier();
}
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/config/NotificationConfiguration.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/config/NotificationConfiguration.java
index 8db7f39..ba70234 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/config/NotificationConfiguration.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/config/NotificationConfiguration.java
@@ -52,6 +52,11 @@ import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.thymeleaf.spring4.SpringTemplateEngine;
+import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
+import org.thymeleaf.templatemode.StandardTemplateModeHandlers;
+
+import java.nio.charset.StandardCharsets;
@SuppressWarnings("WeakerAccess")
@Configuration
@@ -103,23 +108,24 @@ public class NotificationConfiguration extends WebMvcConfigurerAdapter {
}
@Bean
- public PooledConnectionFactory jmsFactory() {
+ public PooledConnectionFactory pooledConnectionFactory() {
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
- activeMQConnectionFactory.setBrokerURL(this.environment.getProperty("activemq.brokerUrl", "vm://localhost?broker.persistent=falseac"));
+ activeMQConnectionFactory.setBrokerURL(this.environment.getProperty("activemq.brokerUrl","vm://localhost?broker.persistent=false"));
pooledConnectionFactory.setConnectionFactory(activeMQConnectionFactory);
return pooledConnectionFactory;
}
@Bean
public JmsListenerContainerFactory jmsListenerContainerFactory(PooledConnectionFactory jmsFactory) {
- DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
+ final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
+ factory.setConnectionFactory(jmsFactory);
factory.setPubSubDomain(true);
factory.setConnectionFactory(jmsFactory);
factory.setErrorHandler(ex -> {
- loggerBean().error(ex.getCause().toString());
+ loggerBean().warn(ex.getCause().toString());
});
- factory.setConcurrency(this.environment.getProperty("activemq.concurrency", "1-1"));
+ factory.setConcurrency(this.environment.getProperty("activemq.concurrency","1-1"));
return factory;
}
@@ -133,6 +139,23 @@ public class NotificationConfiguration extends WebMvcConfigurerAdapter {
return jmsTemplate;
}
+ @Bean
+ public SpringTemplateEngine springTemplateEngine() {
+ SpringTemplateEngine templateEngine = new SpringTemplateEngine();
+ templateEngine.addTemplateResolver(htmlTemplateResolver());
+ return templateEngine;
+ }
+
+ @Bean
+ public SpringResourceTemplateResolver htmlTemplateResolver(){
+ SpringResourceTemplateResolver emailTemplateResolver = new SpringResourceTemplateResolver();
+ emailTemplateResolver.setPrefix("classpath:/templates/");
+ emailTemplateResolver.setSuffix(".html");
+ emailTemplateResolver.setTemplateMode(StandardTemplateModeHandlers.HTML5.getTemplateModeName());
+ emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ return emailTemplateResolver;
+ }
+
@Bean(
name = {ServiceConstants.LOGGER_NAME}
)
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/importer/TemplateImporter.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/importer/TemplateImporter.java
new file mode 100644
index 0000000..ee2782f
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/importer/TemplateImporter.java
@@ -0,0 +1,92 @@
+/*
+ * 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.cn.notification.service.internal.importer;
+
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.apache.fineract.cn.notification.api.v1.client.NotificationManager;
+import org.apache.fineract.cn.notification.api.v1.client.TemplateAlreadyExistException;
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+/**
+ * @author Ebenezer Graham
+ */
+@SuppressWarnings("unused")
+public class TemplateImporter {
+ private static final String TEMPLATE_IDENTIFIER_COLUMN = "template_identifier";
+ private static final String SENDER_EMAIL_COLUMN = "sender_email";
+ private static final String SUBJECT_COLUMN = "subject";
+ private static final String MESSAGE_COLUMN = "message";
+ private static final String URL_COLUMN = "url";
+
+ private final NotificationManager notificationManager;
+ private final Logger logger;
+
+ public TemplateImporter(final NotificationManager notificationManager, final Logger logger) {
+ this.notificationManager = notificationManager;
+ this.logger = logger;
+ }
+
+ public void importCSV(final URL toImport) throws IOException {
+ final CSVParser parser = CSVParser.parse(toImport, StandardCharsets.UTF_8, CSVFormat.RFC4180.withHeader().withCommentMarker('-'));
+ final List<Template>templatesList = StreamSupport.stream(parser.spliterator(), false)
+ .map(this::toTemplate)
+ .collect(Collectors.toList());
+ templatesList.forEach(this::createTemplate);
+ }
+
+ private void createTemplate(final Template toCreate) {
+ try {
+ notificationManager.createTemplate(toCreate);
+ }
+ catch (final TemplateAlreadyExistException ignored) {
+ logger.error("Creation of template {} failed, because a template with the same identifier but different properties already exists {}", toCreate.getTemplateIdentifier(),toCreate.toString());
+ }
+ }
+
+ private Template toTemplate(final CSVRecord csvRecord) {
+ try {
+ final String templateIdentifier = csvRecord.get(TEMPLATE_IDENTIFIER_COLUMN);
+ final String subject = csvRecord.get(SUBJECT_COLUMN);
+ final String senderEmail = csvRecord.get(SENDER_EMAIL_COLUMN);
+ final String url = csvRecord.get(URL_COLUMN);
+ String message;
+ try {
+ message = csvRecord.get(MESSAGE_COLUMN);
+ }
+ catch (final NullPointerException e) {
+ message = "Do not reply, This is a computer generate message.";
+ }
+ return new Template(templateIdentifier,senderEmail,subject,message,url);
+ }
+ catch (final IllegalArgumentException e) {
+ logger.warn("Parsing failed on record {}", csvRecord.getRecordNumber());
+ throw e;
+ }
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/mapper/EmailConfigurationMapper.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/mapper/EmailConfigurationMapper.java
index 5dcfbae..9f8b024 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/mapper/EmailConfigurationMapper.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/mapper/EmailConfigurationMapper.java
@@ -40,6 +40,10 @@ public class EmailConfigurationMapper {
emailConfiguration.setPort(emailGatewayConfigurationEntity.getPort());
emailConfiguration.setUsername(emailGatewayConfigurationEntity.getUsername());
emailConfiguration.setApp_password(emailGatewayConfigurationEntity.getApp_password());
+ emailConfiguration.setProtocol(emailGatewayConfigurationEntity.getProtocol());
+ emailConfiguration.setSmtp_auth(emailGatewayConfigurationEntity.getSmtp_auth());
+ emailConfiguration.setStart_tls(emailGatewayConfigurationEntity.getStart_tls());
+ emailConfiguration.setState(emailGatewayConfigurationEntity.getState());
return emailConfiguration;
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/mapper/TemplateMapper.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/mapper/TemplateMapper.java
new file mode 100644
index 0000000..257cd07
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/mapper/TemplateMapper.java
@@ -0,0 +1,51 @@
+/*
+ * 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.cn.notification.service.internal.mapper;
+
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
+import org.apache.fineract.cn.notification.service.internal.repository.TemplateEntity;
+
+public class TemplateMapper {
+
+ private TemplateMapper() {
+ super();
+ }
+
+ public static Template map(final TemplateEntity templateEntity) {
+ final Template template = new Template();
+ template.setTemplateIdentifier(templateEntity.getTemplateIdentifier());
+ template.setSenderEmail(templateEntity.getSenderEmail());
+ template.setSubject(templateEntity.getSubject());
+ template.setMessage(templateEntity.getMessage());
+ template.setUrl(templateEntity.getUrl());
+ return template;
+ }
+
+ public static TemplateEntity map(final Template template) {
+ final TemplateEntity templateEntity = new TemplateEntity();
+ templateEntity.setTemplateIdentifier(template.getTemplateIdentifier());
+ templateEntity.setSubject(template.getSubject());
+ templateEntity.setSenderEmail(template.getSenderEmail());
+ templateEntity.setMessage(template.getMessage());
+ templateEntity.setUrl(template.getUrl());
+ return templateEntity;
+ }
+}
+
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/ApplicationEntity.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/ApplicationEntity.java
deleted file mode 100644
index 0e22a04..0000000
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/ApplicationEntity.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.cn.notification.service.internal.repository;
-
-import javax.persistence.*;
-import java.util.Objects;
-
-@SuppressWarnings({"unused", "WeakerAccess"})
-@Entity
-@Table(name = "wada_data_source_application")
-public class ApplicationEntity {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "id")
- private Long id;
-
- @Column(name = "tenant_identifier", nullable = false)
- private String tenantIdentifier;
-
- @Column(name = "application_identifier", nullable = false)
- private String applicationIdentifier;
-
- @Column(name = "permittable_identifier")
- private String permittableGroupIdentifier;
-
- public ApplicationEntity() {
- }
-
- public ApplicationEntity(String tenantIdentifier, String applicationIdentifier, String permittableGroupIdentifier) {
- this.tenantIdentifier = tenantIdentifier;
- this.applicationIdentifier = applicationIdentifier;
- this.permittableGroupIdentifier = permittableGroupIdentifier;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getTenantIdentifier() {
- return tenantIdentifier;
- }
-
- public void setTenantIdentifier(String tenantIdentifier) {
- this.tenantIdentifier = tenantIdentifier;
- }
-
- public String getApplicationIdentifier() {
- return applicationIdentifier;
- }
-
- public void setApplicationIdentifier(String applicationIdentifier) {
- this.applicationIdentifier = applicationIdentifier;
- }
-
- public String getPermittableGroupIdentifier() {
- return permittableGroupIdentifier;
- }
-
- public void setPermittableGroupIdentifier(String permittableGroupIdentifier) {
- this.permittableGroupIdentifier = permittableGroupIdentifier;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- ApplicationEntity that = (ApplicationEntity) o;
- return Objects.equals(tenantIdentifier, that.tenantIdentifier) &&
- Objects.equals(applicationIdentifier, that.applicationIdentifier);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(tenantIdentifier, applicationIdentifier);
- }
-
- @Override
- public String toString() {
- return "ApplicationEntity{" +
- "id=" + id +
- ", tenantIdentifier='" + tenantIdentifier + '\'' +
- ", applicationIdentifier='" + applicationIdentifier + '\'' +
- ", permittableGroupIdentifier='" + permittableGroupIdentifier + '\'' +
- '}';
- }
-}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/EmailGatewayConfigurationRepository.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/EmailGatewayConfigurationRepository.java
index 93785be..2b5ddcb 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/EmailGatewayConfigurationRepository.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/EmailGatewayConfigurationRepository.java
@@ -32,8 +32,8 @@ public interface EmailGatewayConfigurationRepository extends JpaRepository<Email
@Query("SELECT CASE WHEN COUNT(c) > 0 THEN 'true' ELSE 'false' END FROM EmailGatewayConfigurationEntity c WHERE c.identifier = :identifier")
Boolean existsByIdentifier(@Param("identifier") final String identifier);
- @Query("SELECT entity FROM EmailGatewayConfigurationEntity entity WHERE entity.state='ACTIVE'")
- Optional<EmailGatewayConfigurationEntity> active();
+ @Query("SELECT entity FROM EmailGatewayConfigurationEntity entity WHERE entity.identifier='DEFAULT'")
+ Optional<EmailGatewayConfigurationEntity> defaultGateway();
void deleteEmailGatewayConfigurationEntityBy(String identifier);
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/SMSGatewayConfigurationRepository.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/SMSGatewayConfigurationRepository.java
index 9b440e5..64e8427 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/SMSGatewayConfigurationRepository.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/SMSGatewayConfigurationRepository.java
@@ -32,8 +32,8 @@ public interface SMSGatewayConfigurationRepository extends JpaRepository<SMSGate
@Query("SELECT CASE WHEN COUNT(c) > 0 THEN 'true' ELSE 'false' END FROM SMSGatewayConfigurationEntity c WHERE c.identifier = :identifier")
Boolean existsByIdentifier(@Param("identifier") final String identifier);
- @Query("SELECT entity FROM SMSGatewayConfigurationEntity entity WHERE entity.state='ACTIVE'")
- Optional<SMSGatewayConfigurationEntity> active();
+ @Query("SELECT entity FROM SMSGatewayConfigurationEntity entity WHERE entity.identifier='DEFAULT'")
+ Optional<SMSGatewayConfigurationEntity> defaultGateway();
void deleteSMSGatewayConfigurationEntityBy(String identifier);
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/TemplateEntity.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/TemplateEntity.java
index c699b9c..4f6f3c8 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/TemplateEntity.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/TemplateEntity.java
@@ -30,10 +30,14 @@ public class TemplateEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
- @Column(name = "identifier")
- private String identifier;
- @Column(name = "event")
- private String event;
+ @Column(name = "template_identifier")
+ private String templateIdentifier;
+ @Column(name = "subject")
+ private String subject;
+ @Column(name = "sender_email")
+ private String senderEmail;
+ @Column(name = "message")
+ private String message;
@Column(name = "url")
private String url;
@@ -50,20 +54,36 @@ public class TemplateEntity {
this.id = id;
}
- public String getIdentifier() {
- return this.identifier;
+ public String getTemplateIdentifier() {
+ return templateIdentifier;
}
- public void setIdentifier(final String identifier) {
- this.identifier = identifier;
+ public void setTemplateIdentifier(String templateIdentifier) {
+ this.templateIdentifier = templateIdentifier;
}
- public String getEvent() {
- return event;
+ public String getSubject() {
+ return subject;
}
- public void setEvent(String event) {
- this.event = event;
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getSenderEmail() {
+ return senderEmail;
+ }
+
+ public void setSenderEmail(String senderEmail) {
+ this.senderEmail = senderEmail;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
}
public String getUrl() {
@@ -80,22 +100,24 @@ public class TemplateEntity {
if (o == null || getClass() != o.getClass()) return false;
TemplateEntity that = (TemplateEntity) o;
return Objects.equals(id, that.id) &&
- Objects.equals(identifier, that.identifier) &&
- Objects.equals(event, that.event) &&
+ Objects.equals(templateIdentifier, that.templateIdentifier) &&
+ Objects.equals(subject, that.subject) &&
Objects.equals(url, that.url);
}
@Override
public int hashCode() {
- return Objects.hash(id, identifier, event, url);
+ return Objects.hash(id, templateIdentifier, subject, message, url);
}
@Override
public String toString() {
return "TemplateEntity{" +
"id=" + id +
- ", identifier='" + identifier + '\'' +
- ", event='" + event + '\'' +
+ ", templateIdentifier='" + templateIdentifier + '\'' +
+ ", subject='" + subject + '\'' +
+ ", message='" + message + '\'' +
+ ", senderEmail='" + senderEmail + '\'' +
", url='" + url + '\'' +
'}';
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/TemplateRepository.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/TemplateRepository.java
index 5e82d1f..cbb7b3f 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/TemplateRepository.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/repository/TemplateRepository.java
@@ -19,11 +19,17 @@
package org.apache.fineract.cn.notification.service.internal.repository;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface TemplateRepository extends JpaRepository<TemplateEntity, Long> {
- Optional<TemplateEntity> findByIdentifier(String identifier);
+ Optional<TemplateEntity> findByTemplateIdentifier(String identifier);
+
+ @Query("SELECT CASE WHEN COUNT(c) > 0 THEN 'true' ELSE 'false' END FROM TemplateEntity c WHERE c.templateIdentifier = :template_identifier")
+ Boolean existsByTemplateIdentifier(@Param("template_identifier") final String identifier);
+
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/EmailService.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/EmailService.java
index bc006c6..f1f24ed 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/EmailService.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/EmailService.java
@@ -28,17 +28,20 @@ import org.apache.fineract.cn.notification.service.ServiceConstants;
import org.apache.fineract.cn.notification.service.internal.mapper.EmailConfigurationMapper;
import org.apache.fineract.cn.notification.service.internal.repository.EmailGatewayConfigurationRepository;
+import org.apache.fineract.cn.notification.service.internal.service.util.MailBuilder;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
-import javax.annotation.PostConstruct;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Properties;
@@ -46,56 +49,35 @@ import java.util.Properties;
@Aggregate
public class EmailService {
- static boolean isConfigured;
-
private final EmailGatewayConfigurationRepository emailGatewayConfigurationRepository;
+ boolean isConfigured;
private JavaMailSenderImpl mailSender;
-
+ private MailBuilder mailBuilder;
private Logger logger;
- private String host;
- private String email;
- private int port;
- private String password;
@Autowired
public EmailService(final EmailGatewayConfigurationRepository emailGatewayConfigurationRepository,
+ final MailBuilder mailBuilder,
@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger) {
super();
this.isConfigured = false;
this.logger = logger;
this.mailSender = new JavaMailSenderImpl();
this.emailGatewayConfigurationRepository = emailGatewayConfigurationRepository;
+ this.mailBuilder = mailBuilder;
}
- //@PostConstruct
- public void init() {
- if (findActiveEmailConfigurationEntity().isPresent()){
- configureEmailGatewayWithActiveConfiguration();
- }else{
- //Todo: Send an alert on the interface to configure the service
- }
- }
-
- public boolean configureEmailGatewayWithActiveConfiguration() {
- EmailConfiguration configuration = findActiveEmailConfigurationEntity().get();
-
- this.host = configuration.getHost();
- this.email = configuration.getUsername();
- this.port = Integer.parseInt(configuration.getPort());
- this.password = configuration.getApp_password();
- return this.isConfigured = setJavaMailSender();
- }
-
- public boolean customConfiguration(String identifier) {
- return this.isConfigured = setCustomProperties(identifier);
+ public boolean configureEmailGatewayWithDefaultGateway() {
+ EmailConfiguration configuration = getDefaultEmailConfigurationEntity().get();
+ return setNewConfiguration(configuration);
}
- public List<EmailConfiguration> findAllActiveEmailConfigurationEntities() {
+ public List<EmailConfiguration> findAllEmailConfigurationEntities() {
return EmailConfigurationMapper.map(this.emailGatewayConfigurationRepository.findAll());
}
- public Optional<EmailConfiguration> findActiveEmailConfigurationEntity() {
- return this.emailGatewayConfigurationRepository.active().map(EmailConfigurationMapper::map);
+ public Optional<EmailConfiguration> getDefaultEmailConfigurationEntity() {
+ return this.emailGatewayConfigurationRepository.defaultGateway().map(EmailConfigurationMapper::map);
}
public Optional<EmailConfiguration> findEmailConfigurationByIdentifier(final String identifier) {
@@ -106,65 +88,69 @@ public class EmailService {
return this.emailGatewayConfigurationRepository.existsByIdentifier(identifier);
}
- public boolean setJavaMailSender() {
- mailSender.setHost(host);
- mailSender.setPort(port);
- mailSender.setUsername(email);
- mailSender.setPassword(password);
-
- switch (host.toLowerCase()) {
- case ServiceConstants.GOOGLE_MAIL_SERVER:
- return setProperties();
- case ServiceConstants.YAHOO_MAIL_SERVER:
- return setProperties();
- }
- return false;
- }
-
- public boolean setProperties() {
- Properties properties = new Properties();
- properties.put(ServiceConstants.MAIL_TRANSPORT_PROTOCOL_PROPERTY,
- ServiceConstants.MAIL_TRANSPORT_PROTOCOL_VALUE);
- properties.put(ServiceConstants.MAIL_SMTP_AUTH_PROPERTY,
- ServiceConstants.MAIL_SMTP_AUTH_VALUE);
- properties.put(ServiceConstants.MAIL_SMTP_STARTTLS_ENABLE_PROPERTY,
- ServiceConstants.MAIL_SMTP_STARTTLS_ENABLE_VALUE);
- this.mailSender.setJavaMailProperties(properties);
- return true;
+ boolean setNewConfiguration(String identifier) {
+ EmailConfiguration configuration = findEmailConfigurationByIdentifier(identifier).get();
+ return setNewConfiguration(configuration);
}
- public boolean setCustomProperties(String identifier) {
- EmailConfiguration configuration = findEmailConfigurationByIdentifier(identifier).get();
- this.mailSender.setHost(configuration.getHost());
- this.mailSender.setPort(Integer.parseInt(configuration.getPort()));
- this.mailSender.setUsername(configuration.getUsername());
- this.mailSender.setPassword(configuration.getApp_password());
-
- Properties properties = new Properties();
- properties.put(ServiceConstants.MAIL_TRANSPORT_PROTOCOL_PROPERTY, configuration.getProtocol());
- properties.put(ServiceConstants.MAIL_SMTP_AUTH_PROPERTY, configuration.getSmtp_auth());
- properties.put(ServiceConstants.MAIL_SMTP_STARTTLS_ENABLE_PROPERTY, configuration.getStart_tls());
- //properties.put(ServiceConstants.MAIL_SMTP_TIMEOUT_PROPERTY, ServiceConstants.MAIL_SMTP_TIMEOUT_VALUE);
- this.mailSender.setJavaMailProperties(properties);
- return true;
+ private boolean setNewConfiguration(EmailConfiguration configuration) {
+ try {
+ this.mailSender.setHost(configuration.getHost());
+ this.mailSender.setPort(Integer.parseInt(configuration.getPort()));
+ this.mailSender.setUsername(configuration.getUsername());
+ this.mailSender.setPassword(configuration.getApp_password());
+
+ Properties properties = new Properties();
+ properties.put(ServiceConstants.MAIL_TRANSPORT_PROTOCOL_PROPERTY, configuration.getProtocol());
+ properties.put(ServiceConstants.MAIL_SMTP_AUTH_PROPERTY, configuration.getSmtp_auth());
+ properties.put(ServiceConstants.MAIL_SMTP_STARTTLS_ENABLE_PROPERTY, configuration.getStart_tls());
+ this.mailSender.setJavaMailProperties(properties);
+ this.isConfigured = true;
+ return true;
+ } catch (RuntimeException ignore) {
+ logger.error("Failed to configure the Email Gateway");
+ }
+ return false;
}
@CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
@Transactional
@EventEmitter(selectorName = NotificationEventConstants.SELECTOR_NAME, selectorValue = NotificationEventConstants.POST_SEND_EMAIL_NOTIFICATION)
- public String sendEmail(String from, String to, String subject, String message) {
+ public String sendPlainEmail(String to, String subject, String message) {
SimpleMailMessage mail = new SimpleMailMessage();
try {
- mail.setFrom(from);
mail.setTo(to);
mail.setSubject(subject);
mail.setText(message);
-
this.mailSender.send(mail);
+ return to;
} catch (MailException exception) {
logger.debug("Caused by:" + exception.getCause().toString());
}
- return to.concat(" - " + mailSender.hashCode());
+ return null;
+ }
+
+ @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+ @Transactional
+ @EventEmitter(selectorName = NotificationEventConstants.SELECTOR_NAME, selectorValue = NotificationEventConstants.POST_SEND_EMAIL_NOTIFICATION)
+ public String sendFormattedEmail(String to,
+ String subject,
+ Map<String, Object> message,
+ String emailTemplate) {
+ MimeMessagePreparator messagePreparator = mimeMessage -> {
+ MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage);
+ messageHelper.setTo(to);
+ messageHelper.setSubject(subject);
+ String content = mailBuilder.build(message, emailTemplate);
+ messageHelper.setText(content, true);
+ };
+ try {
+ this.mailSender.send(messagePreparator);
+ return to;
+ } catch (MailException e) {
+ logger.error("Failed to send Formatted email{}", e.getMessage());
+ }
+ return null;
}
-}
\ No newline at end of file
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/NotificationService.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/NotificationService.java
index 02b3621..2b51e8c 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/NotificationService.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/NotificationService.java
@@ -19,6 +19,7 @@
package org.apache.fineract.cn.notification.service.internal.service;
import org.apache.fineract.cn.customer.api.v1.domain.Customer;
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
import org.apache.fineract.cn.notification.service.ServiceConstants;
import org.apache.fineract.cn.notification.service.internal.identity.CustomerPermittedClient;
import org.apache.fineract.cn.notification.service.internal.identity.NotificationAuthentication;
@@ -28,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
+import java.util.Map;
import java.util.Optional;
@Service
@@ -35,6 +37,7 @@ public class NotificationService {
private final SMSService smsService;
private final EmailService emailService;
+ private final TemplateService templateService;
private final NotificationAuthentication notificationAuthentication;
private final CustomerService customerService;
@@ -46,6 +49,7 @@ public class NotificationService {
public NotificationService(final CustomerService customerService,
final SMSService smsService,
final EmailService emailService,
+ final TemplateService templateService,
final NotificationAuthentication notificationAuthentication,
final CustomerPermittedClient customerPermittedClient,
@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger
@@ -54,6 +58,7 @@ public class NotificationService {
this.customerService = customerService;
this.smsService = smsService;
this.emailService = emailService;
+ this.templateService = templateService;
this.notificationAuthentication = notificationAuthentication;
this.customerPermittedClient = customerPermittedClient;
this.logger = logger;
@@ -65,25 +70,26 @@ public class NotificationService {
return customerService.findCustomer(customerIdentifier);
}
- //SMS Related Operations
- public SMSService setNewSMSService(SMSService smsService, String configurationId){
- smsService.customConfiguration(configurationId);
- return smsService;
- }
-
public String sendSMS(String receiver, String template) {
- if (!this.smsService.isConfigured) this.smsService.configureSMSGatewayWithActiveConfiguration();
+ if (!this.smsService.isConfigured) this.smsService.configureServiceWithDefaultGateway();
return this.smsService.sendSMS(receiver, template);
}
- //Email Related Operations
- public String sendEmail(String from, String to, String subject, String message) {
- if (!emailService.isConfigured) emailService.configureEmailGatewayWithActiveConfiguration();
- return this.emailService.sendEmail(from, to, subject, message);
+ /*To be used as a backup should Formatted email fail*/
+ public void sendEmail(String to, String templateIdentifier,Object payload) {
+ Template template = this.templateService.findTemplateWithIdentifier(templateIdentifier).get();
+ if (!this.emailService.isConfigured) {
+ this.emailService.setNewConfiguration(template.getSenderEmail());
+ }
+ this.emailService.sendPlainEmail(to, template.getSubject(), template.getMessage());
}
- public EmailService setNewEmailService(EmailService emailService, String configurationId){
- emailService.customConfiguration(configurationId);
- return emailService;
+ public void sendFormattedEmail(String to, String templateIdentifier, Map<String,Object> variables) {
+ Template template = this.templateService.findTemplateWithIdentifier(templateIdentifier).get();
+ if (!this.emailService.isConfigured) {
+ this.emailService.setNewConfiguration(template.getSenderEmail());
+ }
+
+ this.emailService.sendFormattedEmail(to, template.getSubject(), variables,template.getUrl());
}
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/SMSService.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/SMSService.java
index f618d1a..966aee3 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/SMSService.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/SMSService.java
@@ -34,11 +34,9 @@ import org.apache.fineract.cn.notification.service.internal.repository.SMSGatewa
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Optional;
@@ -63,15 +61,15 @@ public class SMSService {
//@PostConstruct
public void init() {
- if (findActiveSMSConfigurationEntity().isPresent()){
- configureSMSGatewayWithActiveConfiguration();
+ if (getDefaultSMSConfiguration().isPresent()){
+ configureServiceWithDefaultGateway();
}else{
//Todo: Send an alert on the interface to configure the service
}
}
- public boolean configureSMSGatewayWithActiveConfiguration() {
- SMSConfiguration configuration = findActiveSMSConfigurationEntity().get();
+ public boolean configureServiceWithDefaultGateway() {
+ SMSConfiguration configuration = getDefaultSMSConfiguration().get();
this.accountSid = configuration.getAccount_sid();
this.authToken = configuration.getAuth_token();
this.senderNumber = configuration.getSender_number();
@@ -86,8 +84,8 @@ public class SMSService {
return this.isConfigured = true;
}
- public Optional<SMSConfiguration> findActiveSMSConfigurationEntity() {
- return this.smsGatewayConfigurationRepository.active().map(SMSConfigurationMapper::map);
+ public Optional<SMSConfiguration> getDefaultSMSConfiguration() {
+ return this.smsGatewayConfigurationRepository.defaultGateway().map(SMSConfigurationMapper::map);
}
public Boolean smsConfigurationExists(final String identifier) {
@@ -98,7 +96,7 @@ public class SMSService {
return this.smsGatewayConfigurationRepository.findByIdentifier(identifier).map(SMSConfigurationMapper::map);
}
- public List<SMSConfiguration> findAllActiveSMSConfigurationEntities() {
+ public List<SMSConfiguration> findAllSMSConfigurationEntities() {
return SMSConfigurationMapper.map(this.smsGatewayConfigurationRepository.findAll());
}
@@ -114,4 +112,4 @@ public class SMSService {
Message message = messageCreator.create();
return message.getTo().concat(" - " + message.getSid());
}
-}
\ No newline at end of file
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/TemplateService.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/TemplateService.java
new file mode 100644
index 0000000..055403b
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/TemplateService.java
@@ -0,0 +1,60 @@
+/*
+ * 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.cn.notification.service.internal.service;
+
+import org.apache.fineract.cn.command.annotation.Aggregate;
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
+import org.apache.fineract.cn.notification.service.ServiceConstants;
+import org.apache.fineract.cn.notification.service.internal.mapper.TemplateMapper;
+import org.apache.fineract.cn.notification.service.internal.repository.TemplateRepository;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+@Aggregate
+public class TemplateService {
+
+
+ private final TemplateRepository templateRepository;
+ private Logger logger;
+
+ @Autowired
+ public TemplateService(final TemplateRepository templateRepository,
+ @Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger) {
+ super();
+ this.logger = logger;
+ this.templateRepository = templateRepository;
+ }
+ public Optional<Template> findTemplateWithIdentifier(final String identifier) {
+ return this.templateRepository.findByTemplateIdentifier(identifier).map(TemplateMapper::map);
+ }
+
+ public Boolean templateExists(final String identifier) {
+ return this.templateRepository.existsByTemplateIdentifier(identifier);
+ }
+
+ public Boolean deleteTemplate(final String identifier) {
+ //Todo: Remove html template and template record from repository
+ return false;
+ }
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/util/MailBuilder.java
similarity index 50%
copy from component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
copy to service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/util/MailBuilder.java
index ad4208a..e12c87f 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/notification/TestSuite.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/internal/service/util/MailBuilder.java
@@ -16,17 +16,31 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.fineract.cn.notification;
+package org.apache.fineract.cn.notification.service.internal.service.util;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
- TestEmailService.class,
- TestSMSService.class,
- EmailApiDocumentation.class,
- SmsApiDocumentation.class,
-})
-public class TestSuite extends SuiteTestEnvironment {
+import java.util.Map;
+/*
+ebenezergraham created on 5/12/19
+*/
+@Service
+public class MailBuilder {
+ private TemplateEngine templateEngine;
+
+ @Autowired
+ public MailBuilder(TemplateEngine templateEngine) {
+ this.templateEngine = templateEngine;
+ }
+
+ public String build(Map<String, Object> message, String template) {
+ Context context = new Context();
+ for(Map.Entry m:message.entrySet()){
+ context.setVariable(m.getKey().toString(), m.getValue().toString());
+ }
+ return templateEngine.process(template, context);
+ }
}
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/listener/CustomerEventListener.java b/service/src/main/java/org/apache/fineract/cn/notification/service/listener/CustomerEventListener.java
index ce5325e..96524e4 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/listener/CustomerEventListener.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/listener/CustomerEventListener.java
@@ -84,13 +84,10 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
+ notificationService.sendEmail(
emailAddress,
- "Account created",
- "Dear Valued Customer," +
- "\n\nYour account has been created" +
- "\n\nBest Regards," +
- "\nYour MFI");
+ "customerCreatedEvent",
+ payload);
}
});
}
@@ -118,13 +115,10 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
+ notificationService.sendEmail(
emailAddress,
- "Account updated",
- "Dear Valued Customer," +
- "\n\nYour account has been Updated" +
- "\n\nBest Regards," +
- "\nYour MFI");
+ "customerUpdatedEvents",
+ payload);
}
});
}
@@ -150,13 +144,10 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
+ notificationService.sendEmail(
emailAddress,
- "Account activated",
- "Dear Valued Customer," +
- "\n\nYour account has been Activated" +
- "\n\nBest Regards," +
- "\nYour MFI");
+ "customerActivatedEvent",
+ payload);
}
});
}
@@ -182,12 +173,9 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
- emailAddress, "Account locked",
- "Dear Valued Customer," +
- "\n\nYour account has been Locked" +
- "\n\nBest Regards," +
- "\nYour MFI");
+ notificationService.sendEmail(
+ emailAddress, "customerLockedEvent",
+ payload);
}
});
}
@@ -213,13 +201,10 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
+ notificationService.sendEmail(
emailAddress,
- "Account created",
- "Dear Valued Customer," +
- "\n\nYour account has been Unlocked" +
- "\n\nBest Regards," +
- "\nYour MFI");
+ "customerUnlockedEvent",
+ payload);
}
});
}
@@ -245,12 +230,9 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
- emailAddress, "Account closed",
- "Dear Valued Customer," +
- "\n\nYour account has been Closed" +
- "\n\nBest Regards," +
- "\nYour MFI");
+ notificationService.sendEmail(
+ emailAddress, "customerClosedEvent",
+ payload);
}
});
}
@@ -276,13 +258,10 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
+ notificationService.sendEmail(
emailAddress,
- "Account Reopened",
- "Dear Valued Customer," +
- "\n\nYour account has been reopened" +
- "\n\nBest Regards," +
- "\nYour MFI");
+ "customerReopenedEvent",
+ payload);
}
});
}
@@ -309,14 +288,10 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
+ notificationService.sendEmail(
emailAddress,
- "Contact Details Changed",
- "Dear Valued Customer," +
- "\n\nYour contact has been changed successfully" +
- "\n\tNew Contact: "+emailAddress+
- "\n\nBest Regards" +
- "\nYour MFI");
+ "contactDetailsChangedEvent",
+ payload);
}
});
}
@@ -347,19 +322,11 @@
String emailAddress = contact.getValue();
// TODO: Localize message
// TODO: Pass message to template
- notificationService.sendEmail("fineractcnnotificationdemo@gmail.com",
+ notificationService.sendEmail(
emailAddress,
- "Contact Details Changed" +
- "New Contact: "+emailAddress,
- "Dear Valued Customer," +
- "\n\nYour address has been changed successfully" +
- "\nStreet: "+ customer.getAddress().getStreet() +
- "\nCity: "+ customer.getAddress().getCity() +
- "\nState: "+ customer.getAddress().getRegion() +
- "\nCountry: "+ customer.getAddress().getCountry() +
- "\n\nBest Regards" +
- "\nYour MFI");
+ "addressChangedEvent",
+ payload);
}
});
}
- }
\ No newline at end of file
+ }
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/rest/EmailServiceRestController.java b/service/src/main/java/org/apache/fineract/cn/notification/service/rest/EmailServiceRestController.java
index 2823b6d..d1859a2 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/rest/EmailServiceRestController.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/rest/EmailServiceRestController.java
@@ -24,11 +24,9 @@ import org.apache.fineract.cn.command.gateway.CommandGateway;
import org.apache.fineract.cn.lang.ServiceException;
import org.apache.fineract.cn.notification.api.v1.PermittableGroupIds;
import org.apache.fineract.cn.notification.api.v1.domain.EmailConfiguration;
-import org.apache.fineract.cn.notification.api.v1.domain.SMSConfiguration;
import org.apache.fineract.cn.notification.service.ServiceConstants;
import org.apache.fineract.cn.notification.service.internal.command.*;
import org.apache.fineract.cn.notification.service.internal.service.EmailService;
-import org.apache.fineract.cn.notification.service.internal.service.NotificationService;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -95,7 +93,7 @@ public class EmailServiceRestController {
public
@ResponseBody
List<EmailConfiguration> findAllActiveEmailConfigurationEntities() {
- return this.emailService.findAllActiveEmailConfigurationEntities();
+ return this.emailService.findAllEmailConfigurationEntities();
}
@Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.SELF_MANAGEMENT)
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/rest/SMSServiceRestController.java b/service/src/main/java/org/apache/fineract/cn/notification/service/rest/SMSServiceRestController.java
index b9a6e83..26106bc 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/rest/SMSServiceRestController.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/rest/SMSServiceRestController.java
@@ -24,10 +24,8 @@ import org.apache.fineract.cn.command.gateway.CommandGateway;
import org.apache.fineract.cn.lang.ServiceException;
import org.apache.fineract.cn.notification.api.v1.PermittableGroupIds;
import org.apache.fineract.cn.notification.api.v1.domain.SMSConfiguration;
-import org.apache.fineract.cn.notification.api.v1.domain.SMSConfiguration;
import org.apache.fineract.cn.notification.service.ServiceConstants;
import org.apache.fineract.cn.notification.service.internal.command.*;
-import org.apache.fineract.cn.notification.service.internal.service.NotificationService;
import org.apache.fineract.cn.notification.service.internal.service.SMSService;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
@@ -69,7 +67,7 @@ public class SMSServiceRestController {
public
@ResponseBody
List<SMSConfiguration> findAllActiveSMSConfigurationEntities() {
- return this.smsService.findAllActiveSMSConfigurationEntities();
+ return this.smsService.findAllSMSConfigurationEntities();
}
@Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.SELF_MANAGEMENT)
diff --git a/service/src/main/java/org/apache/fineract/cn/notification/service/rest/SMSServiceRestController.java b/service/src/main/java/org/apache/fineract/cn/notification/service/rest/TemplateRestController.java
similarity index 63%
copy from service/src/main/java/org/apache/fineract/cn/notification/service/rest/SMSServiceRestController.java
copy to service/src/main/java/org/apache/fineract/cn/notification/service/rest/TemplateRestController.java
index b9a6e83..aed08f2 100644
--- a/service/src/main/java/org/apache/fineract/cn/notification/service/rest/SMSServiceRestController.java
+++ b/service/src/main/java/org/apache/fineract/cn/notification/service/rest/TemplateRestController.java
@@ -24,11 +24,14 @@ import org.apache.fineract.cn.command.gateway.CommandGateway;
import org.apache.fineract.cn.lang.ServiceException;
import org.apache.fineract.cn.notification.api.v1.PermittableGroupIds;
import org.apache.fineract.cn.notification.api.v1.domain.SMSConfiguration;
-import org.apache.fineract.cn.notification.api.v1.domain.SMSConfiguration;
+import org.apache.fineract.cn.notification.api.v1.domain.Template;
import org.apache.fineract.cn.notification.service.ServiceConstants;
-import org.apache.fineract.cn.notification.service.internal.command.*;
-import org.apache.fineract.cn.notification.service.internal.service.NotificationService;
+import org.apache.fineract.cn.notification.service.internal.command.CreateSMSConfigurationCommand;
+import org.apache.fineract.cn.notification.service.internal.command.CreateTemplateCommand;
+import org.apache.fineract.cn.notification.service.internal.command.DeleteSMSConfigurationCommand;
+import org.apache.fineract.cn.notification.service.internal.command.UpdateSMSConfigurationCommand;
import org.apache.fineract.cn.notification.service.internal.service.SMSService;
+import org.apache.fineract.cn.notification.service.internal.service.TemplateService;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -42,34 +45,21 @@ import java.util.List;
@SuppressWarnings("unused")
@RestController
-@RequestMapping("/configuration/sms/")
-public class SMSServiceRestController {
+@RequestMapping("/template")
+public class TemplateRestController {
private final Logger logger;
private final CommandGateway commandGateway;
- private final SMSService smsService;
+ private final TemplateService templateService;
@Autowired
- public SMSServiceRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
- final CommandGateway commandGateway,
- final SMSService smsService) {
+ public TemplateRestController(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+ final CommandGateway commandGateway,
+ final TemplateService templateService) {
super();
this.logger = logger;
this.commandGateway = commandGateway;
- this.smsService = smsService;
- }
-
- @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.SELF_MANAGEMENT)
- @RequestMapping(
- value = "/active",
- method = RequestMethod.GET,
- consumes = MediaType.ALL_VALUE,
- produces = MediaType.APPLICATION_JSON_VALUE
- )
- public
- @ResponseBody
- List<SMSConfiguration> findAllActiveSMSConfigurationEntities() {
- return this.smsService.findAllActiveSMSConfigurationEntities();
+ this.templateService = templateService;
}
@Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.SELF_MANAGEMENT)
@@ -81,10 +71,10 @@ public class SMSServiceRestController {
)
public
@ResponseBody
- ResponseEntity<SMSConfiguration> findSMSConfigurationByIdentifier(@PathVariable("identifier") final String identifier) {
- return this.smsService.findSMSConfigurationByIdentifier(identifier)
+ ResponseEntity<Template> findTemplateByIdentifier(@PathVariable("identifier") final String identifier) {
+ return this.templateService.findTemplateWithIdentifier(identifier)
.map(ResponseEntity::ok)
- .orElseThrow(() -> ServiceException.notFound("SMS Gateway Configuration with identifier " + identifier + " doesn't exist."));
+ .orElseThrow(() -> ServiceException.notFound("Template with identifier " + identifier + " doesn't exist."));
}
@Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.SELF_MANAGEMENT)
@@ -96,12 +86,12 @@ public class SMSServiceRestController {
)
public
@ResponseBody
- ResponseEntity<Void> createSMSConfiguration(@RequestBody @Valid final SMSConfiguration smsConfiguration) throws InterruptedException {
- if (this.smsService.smsConfigurationExists(smsConfiguration.getIdentifier())) {
- throw ServiceException.conflict("Configuration {0} already exists.", smsConfiguration.getIdentifier());
+ ResponseEntity<Void> createTemplate(@RequestBody @Valid final Template template) throws InterruptedException {
+ if (this.templateService.templateExists(template.getTemplateIdentifier())) {
+ throw ServiceException.conflict("Template {0} already exists.", template.getTemplateIdentifier());
}
- this.commandGateway.process(new CreateSMSConfigurationCommand(smsConfiguration));
+ this.commandGateway.process(new CreateTemplateCommand(template));
return new ResponseEntity<>(HttpStatus.CREATED);
}
@@ -113,8 +103,8 @@ public class SMSServiceRestController {
)
public
@ResponseBody
- ResponseEntity<Void> updateSMSConfiguration(@RequestBody @Valid final SMSConfiguration smsConfiguration) {
- this.commandGateway.process(new UpdateSMSConfigurationCommand(smsConfiguration));
+ ResponseEntity<Void> updateTemplate(@RequestBody @Valid final Template template) {
+ this.commandGateway.process(template);
return ResponseEntity.accepted().build();
}
@@ -126,8 +116,8 @@ public class SMSServiceRestController {
)
public
@ResponseBody
- ResponseEntity<Void> deleteSMSConfiguration(@PathVariable @Valid final String identifier) {
- this.commandGateway.process(new DeleteSMSConfigurationCommand(identifier));
+ ResponseEntity<Void> deleteTemplate(@PathVariable @Valid final String identifier) {
+ this.commandGateway.process(identifier);
return ResponseEntity.ok().build();
}
}
diff --git a/service/src/main/resources/application.yml b/service/src/main/resources/application.yml
index d1c0460..23054cf 100644
--- a/service/src/main/resources/application.yml
+++ b/service/src/main/resources/application.yml
@@ -85,3 +85,7 @@ flyway:
notification:
user: operator
password: init1@l
+
+activemq:
+ brokerUrl: vm://localhost?broker.persistent=false
+ concurrency: 1-1
diff --git a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
index 9e1aba3..19f43bd 100644
--- a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
+++ b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql
@@ -33,7 +33,7 @@ CREATE TABLE wada_sms_gateway_configurations (
-- Table wada_email_gateway_configurations
-- -----------------------------------------------------
CREATE TABLE wada_email_gateway_configurations (
- id INT(11) NOT NULL AUTO_INCREMENT,
+ id INT NOT NULL AUTO_INCREMENT,
identifier VARCHAR(45) NULL DEFAULT NULL,
host VARCHAR(45) NOT NULL,
port VARCHAR(45) NOT NULL,
@@ -49,22 +49,14 @@ CREATE TABLE wada_email_gateway_configurations (
-- Table wada_templates
-- -----------------------------------------------------
CREATE TABLE wada_templates (
- id INT(11) NOT NULL AUTO_INCREMENT,
- identifier VARCHAR(45) NULL DEFAULT NULL,
- event VARCHAR(45) NULL DEFAULT NULL,
+ id INT NOT NULL AUTO_INCREMENT,
+ template_identifier VARCHAR(45) NULL DEFAULT NULL,
+ sender_email VARCHAR(255) NULL DEFAULT NULL,
+ subject VARCHAR(255) NULL DEFAULT NULL,
+ message VARCHAR(1024) NULL DEFAULT NULL,
url VARCHAR(255) NOT NULL,
PRIMARY KEY (id));
--- -----------------------------------------------------
--- Table wada_data_source_application
--- -----------------------------------------------------
- CREATE TABLE wada_data_source_application (
- id BIGINT NOT NULL AUTO_INCREMENT,
- tenant_identifier VARCHAR(32) NOT NULL,
- application_identifier VARCHAR(32) NOT NULL,
- permittable_identifier VARCHAR(32) NOT NULL,
- PRIMARY KEY (id)
-);
-INSERT INTO wada_sms_gateway_configurations VALUES ('1', 'Twilio', 'ACdc00866577a42133e16d98456ad15592', '0b2f78b1c083eb71599d014d1af5748e', '+12055486680', 'ACTIVE');
-INSERT INTO wada_email_gateway_configurations VALUES ('1', 'Gmail', 'smtp.gmail.com', '587','fineractcnnotificationdemo@gmail.com', 'pnuugpwmcibipdpw', 'smtp', 'true', 'true', 'ACTIVE');
+INSERT INTO wada_sms_gateway_configurations VALUES ('1', 'DEFAULT', 'ACdc00866577a42133e16d98456ad15592', '0b2f78b1c083eb71599d014d1af5748e', '+12055486680', 'ACTIVE');
+INSERT INTO wada_email_gateway_configurations VALUES ('1', 'DEFAULT', 'smtp.gmail.com', '587','fineractcnnotificationdemo@gmail.com', 'pnuugpwmcibipdpw', 'smtp', 'true', 'true', 'ACTIVE');
diff --git a/service/src/main/resources/templatedetails/templates.csv b/service/src/main/resources/templatedetails/templates.csv
new file mode 100644
index 0000000..ddc76ef
--- /dev/null
+++ b/service/src/main/resources/templatedetails/templates.csv
@@ -0,0 +1,28 @@
+-
+- 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.
+-
+template_identifier,sender_email,subject,message,url
+customerCreatedEvent,DEFAULT,Account created,"Your account has been created",template
+customerUpdatedEvents,DEFAULT,Account updated,Your account has been Updated,template
+customerActivatedEvent,DEFAULT,Account Activated,Your account has been Activated,template
+customerLockedEvent,DEFAULT,Account Locked,Your account has been Locked,template
+customerUnlockedEvent,DEFAULT,Account unlocked,Your account has been Unlocked,template
+customerClosedEvent,DEFAULT,Account closed successfully,Your account has been Closed,template
+customerReopenedEvent,DEFAULT,Account Reopened,Your account has been reopened,template
+contactDetailsChangedEvent,DEFAULT,Contact details has been updated,Your contact has been changed successfully,template
+addressChangedEvent,DEFAULT,Residence address has been changed,Your address has been changed successfully,template
diff --git a/service/src/main/resources/templates/template.html b/service/src/main/resources/templates/template.html
new file mode 100644
index 0000000..d6a4d1c
--- /dev/null
+++ b/service/src/main/resources/templates/template.html
@@ -0,0 +1,457 @@
+<!--
+ 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.
+-->
+
+<!doctype html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:th="http://www.thymeleaf.org">
+<head>
+ <meta name="viewport" content="width=device-width"/>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>Simple Email</title>
+ <link href="https://fonts.googleapis.com/css?family=helvatica" rel="stylesheet"/>
+ <style>
+ /* -------------------------------------
+ GLOBAL RESETS
+ ------------------------------------- */
+
+ /*All the styling goes here*/
+
+ img {
+ border: none;
+ -ms-interpolation-mode: bicubic;
+ max-width: 100%;
+ }
+
+ body {
+ background-color: #f6f6f6;
+ font-family: sans-serif;
+ -webkit-font-smoothing: antialiased;
+ font-size: 14px;
+ line-height: 1.4;
+ margin: 0;
+ padding: 0;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ }
+
+ table {
+ border-collapse: separate;
+ mso-table-lspace: 0pt;
+ mso-table-rspace: 0pt;
+ width: 100%;
+ }
+
+ table td {
+ font-family: sans-serif;
+ font-size: 14px;
+ vertical-align: top;
+ }
+
+ /* -------------------------------------
+ CONTAINER
+ ------------------------------------- */
+
+ .body {
+ background-color: #f6f6f6;
+ width: 100%;
+ }
+
+ /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
+ .container {
+ display: block;
+ margin: 0 auto !important;
+ /* makes it centered */
+ max-width: 580px;
+ padding: 10px;
+ width: 580px;
+ }
+
+ /* This should also be a block element, so that it will fill 100% of the .container */
+ .content {
+ box-sizing: border-box;
+ display: block;
+ margin: 0 auto;
+ max-width: 580px;
+ padding: 10px;
+ }
+
+ /* -------------------------------------
+ HEADER, FOOTER, MAIN
+ ------------------------------------- */
+ .main {
+ background: #ffffff;
+ border-radius: 3px;
+ width: 100%;
+ }
+
+ .wrapper {
+ box-sizing: border-box;
+ padding: 20px;
+ }
+
+ .content-block {
+ padding-bottom: 10px;
+ padding-top: 10px;
+ }
+
+ .footer {
+ clear: both;
+ margin-top: 10px;
+ text-align: center;
+ width: 100%;
+ }
+
+ .footer td,
+ .footer p,
+ .footer span,
+ .footer a {
+ color: #999999;
+ font-size: 12px;
+ text-align: center;
+ }
+
+ /* -------------------------------------
+ TYPOGRAPHY
+ ------------------------------------- */
+ h1,
+ h2,
+ h3,
+ h4 {
+ color: #000000;
+ font-family: sans-serif;
+ font-weight: 400;
+ line-height: 1.4;
+ margin: 0;
+ margin-bottom: 30px;
+ }
+
+ h1 {
+ font-size: 35px;
+ font-weight: 300;
+ text-align: center;
+ text-transform: capitalize;
+ }
+
+ p,
+ ul,
+ ol {
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: normal;
+ margin: 0;
+ margin-bottom: 15px;
+ }
+
+ p li,
+ ul li,
+ ol li {
+ list-style-position: inside;
+ margin-left: 5px;
+ }
+
+ a {
+ color: #a92334;
+ text-decoration: underline;
+ }
+
+ /* -------------------------------------
+ BUTTONS
+ ------------------------------------- */
+ .btn {
+ box-sizing: border-box;
+ width: 100%;
+ }
+
+ .btn > tbody > tr > td {
+ padding-bottom: 15px;
+ }
+
+ .btn table {
+ width: auto;
+ }
+
+ .btn table td {
+ background-color: #ffffff;
+ border-radius: 5px;
+ text-align: center;
+ }
+
+ .btn a {
+ background-color: #ffffff;
+ border: solid 1px #a92334;
+ border-radius: 5px;
+ box-sizing: border-box;
+ color: #a92334;
+ cursor: pointer;
+ display: inline-block;
+ font-size: 14px;
+ font-weight: bold;
+ margin: 0;
+ padding: 12px 25px;
+ text-decoration: none;
+ text-transform: capitalize;
+ }
+
+ .btn-primary table td {
+ background-color: #a92334;
+ }
+
+ .btn-primary a {
+ background-color: #a92334;
+ border-color: #a92334;
+ color: #ffffff;
+ }
+
+ /* -------------------------------------
+ OTHER STYLES THAT MIGHT BE USEFUL
+ ------------------------------------- */
+ .last {
+ margin-bottom: 0;
+ }
+
+ .first {
+ margin-top: 0;
+ }
+
+ .align-center {
+ text-align: center;
+ }
+
+ .align-right {
+ text-align: right;
+ }
+
+ .align-left {
+ text-align: left;
+ }
+
+ .clear {
+ clear: both;
+ }
+
+ .mt0 {
+ margin-top: 0;
+ }
+
+ .mb0 {
+ margin-bottom: 0;
+ }
+
+ .preheader {
+ color: transparent;
+ display: none;
+ height: 0;
+ max-height: 0;
+ max-width: 0;
+ opacity: 0;
+ overflow: hidden;
+ mso-hide: all;
+ visibility: hidden;
+ width: 0;
+ }
+
+ .powered-by a {
+ text-decoration: none;
+ }
+
+ hr {
+ border: 0;
+ border-bottom: 1px solid #f6f6f6;
+ margin: 20px 0;
+ }
+
+ /* -------------------------------------
+ RESPONSIVE AND MOBILE FRIENDLY STYLES
+ ------------------------------------- */
+ @media only screen and (max-width: 620px) {
+ table[class=body] h1 {
+ font-size: 28px !important;
+ margin-bottom: 10px !important;
+ }
+
+ table[class=body] p,
+ table[class=body] ul,
+ table[class=body] ol,
+ table[class=body] td,
+ table[class=body] span,
+ table[class=body] a {
+ font-size: 16px !important;
+ }
+
+ table[class=body] .wrapper,
+ table[class=body] .article {
+ padding: 10px !important;
+ }
+
+ table[class=body] .content {
+ padding: 0 !important;
+ }
+
+ table[class=body] .container {
+ padding: 0 !important;
+ width: 100% !important;
+ }
+
+ table[class=body] .main {
+ border-left-width: 0 !important;
+ border-radius: 0 !important;
+ border-right-width: 0 !important;
+ }
+
+ table[class=body] .btn table {
+ width: 100% !important;
+ }
+
+ table[class=body] .btn a {
+ width: 100% !important;
+ }
+
+ table[class=body] .img-responsive {
+ height: auto !important;
+ max-width: 100% !important;
+ width: auto !important;
+ }
+ }
+
+ /* -------------------------------------
+ PRESERVE THESE STYLES IN THE HEAD
+ ------------------------------------- */
+ @media all {
+ .headTab{
+ height: 60px;
+ width: auto;
+ max-width: 540px;
+ padding: 10px;
+ margin: 0 auto;
+ background-color: #ffffff;
+ /*-webkit-box-shadow: 0px -1px 139px -25px rgba(0,0,0,0.69);
+ -moz-box-shadow: 0px -1px 139px -25px rgba(0,0,0,0.69);
+ box-shadow: 0px -1px 139px -25px rgba(0,0,0,0.69);*/
+ }
+ .ExternalClass {
+ width: 100%;
+ }
+
+ .ExternalClass,
+ .ExternalClass p,
+ .ExternalClass span,
+ .ExternalClass font,
+ .ExternalClass td,
+ .ExternalClass div {
+ line-height: 100%;
+ }
+
+ .apple-link a {
+ color: inherit !important;
+ font-family: inherit !important;
+ font-size: inherit !important;
+ font-weight: inherit !important;
+ line-height: inherit !important;
+ text-decoration: none !important;
+ }
+
+ .btn-primary table td:hover {
+ background-color: #a92334 !important;
+ }
+
+ .btn-primary a:hover {
+ background-color: #a92334 !important;
+ border-color: #a92334 !important;
+ }
+ }
+
+ </style>
+</head>
+<body>
+<span class="preheader">Header Message</span>
+<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
+
+ <tr>
+ <td> </td>
+
+ <td class="container">
+ <div class="headTab"><img style="height:40px; margin:10px;" class="img-responsive" src="http://fineract.apache.org/images/apache-fineract-logo.png" alt="Logo"></div>
+
+ <div class="content">
+
+ <!-- START CENTERED WHITE CONTAINER -->
+ <table role="presentation" class="main">
+
+ <!-- START MAIN CONTENT AREA -->
+ <tr>
+ <td class="wrapper">
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
+ <tr>
+ <td>
+ <p>Dear Valued Customer,</p>
+
+ <p>This is a sample message an account update</p>
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
+ <tbody>
+ <tr>
+ <td align="left">
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
+ <tbody>
+ <tr>
+ <td> <a href="http://fineract.apache.org" target="_blank">Call To Action</a> </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <p>No reply required. This is a computer generated mail.</p>
+ <p>Best Regards,</p>
+ <p>MFI</p>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+
+ <!-- END MAIN CONTENT AREA -->
+ </table>
+ <!-- END CENTERED WHITE CONTAINER -->
+
+ <!-- START FOOTER -->
+ <div class="footer">
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="content-block">
+ <span class="apple-link">Apache Software Foundation, Forest Hill, Maryland, United States</span>
+ <p> Don't like these emails? <a href="">Unsubscribe</a>.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="content-block powered-by">
+ Powered by <a href="http://fineract.apache.org">Apache Fineract</a>.
+ </td>
+ </tr>
+ </table>
+ </div>
+ <!-- END FOOTER -->
+
+ </div>
+ </td>
+ <td> </td>
+ </tr>
+</table>
+</body>
+</html>
diff --git a/shared.gradle b/shared.gradle
index c270323..7ecfdc6 100644
--- a/shared.gradle
+++ b/shared.gradle
@@ -39,7 +39,9 @@ ext.versions = [
validator : '5.3.0.Final',
springjavamail : '1.4.1.RELEASE',
twilioapi : '7.17.+',
- junit : '4.12'
+ junit : '4.12',
+ apachecsvreader : '1.4'
+
]
apply plugin: 'java'