From b87578f82864239cf905a8b34ccc94418390f484 Mon Sep 17 00:00:00 2001 From: Anh3h Date: Thu, 13 Jul 2017 08:39:37 +0100 Subject: [PATCH] Notification Subsystem --- fineract-provider/dependencies.gradle | 8 +- fineract-provider/dev-dependencies.gradle | 6 +- .../integrationtests/NotificationApiTest.java | 57 ++ .../common/NotificationHelper.java | 43 ++ .../TenantAwareBasicAuthenticationFilter.java | 12 +- .../api/NotificationApiResource.java | 88 +++ .../CacheNotificationResponseHeader.java | 49 ++ .../notification/data/NotificationData.java | 142 +++++ .../data/NotificationMapperData.java | 80 +++ .../fineract/notification/data/TopicData.java | 76 +++ .../data/TopicSubscriberData.java | 60 ++ .../notification/domain/Notification.java | 122 ++++ .../domain/NotificationMapper.java | 82 +++ .../domain/NotificationMapperRepository.java | 24 + .../domain/NotificationRepository.java | 24 + .../fineract/notification/domain/Topic.java | 122 ++++ .../notification/domain/TopicRepository.java | 24 + .../notification/domain/TopicSubscriber.java | 80 +++ .../domain/TopicSubscriberRepository.java | 24 + .../eventandlistener/NotificationEvent.java | 51 ++ .../NotificationEventListener.java | 74 +++ .../exception/TopicNotFoundException.java | 32 + .../service/NotificationDomainService.java | 23 + .../NotificationDomainServiceImpl.java | 599 ++++++++++++++++++ ...ificationGeneratorReadPlatformService.java | 33 + ...rReadPlatformServiceJpaRepositoryImpl.java | 53 ++ ...ficationGeneratorWritePlatformService.java | 25 + ...WritePlatformServiceJpaRepositoryImpl.java | 42 ++ ...NotificationMapperReadPlatformService.java | 33 + ...rReadPlatformServiceJpaRepositoryImpl.java | 55 ++ ...otificationMapperWritePlatformService.java | 26 + ...WritePlatformServiceJpaRepositoryImpl.java | 44 ++ .../NotificationReadPlatformService.java | 35 + .../NotificationReadPlatformServiceImpl.java | 203 ++++++ .../NotificationWritePlatformService.java | 30 + .../NotificationWritePlatformServiceImpl.java | 126 ++++ .../service/TopicReadPlatformService.java | 32 + .../service/TopicReadPlatformServiceImpl.java | 98 +++ .../TopicSubscriberReadPlatformService.java | 29 + ...opicSubscriberReadPlatformServiceImpl.java | 78 +++ .../TopicSubscriberWritePlatformService.java | 27 + ...WritePlatformServiceJpaRepositoryImpl.java | 42 ++ .../service/TopicWritePlatformService.java | 27 + ...WritePlatformServiceJpaRepositoryImpl.java | 42 ++ .../organisation/office/domain/Office.java | 6 +- ...WritePlatformServiceJpaRepositoryImpl.java | 29 +- .../BusinessEventNotificationConstants.java | 13 +- ...WritePlatformServiceJpaRepositoryImpl.java | 30 +- ...WritePlatformServiceJpaRepositoryImpl.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 19 +- ...WritePlatformServiceJpaRepositoryImpl.java | 20 +- ...WritePlatformServiceJpaRepositoryImpl.java | 5 + ...WritePlatformServiceJpaRepositoryImpl.java | 6 + ...WritePlatformServiceJpaRepositoryImpl.java | 23 +- ...WritePlatformServiceJpaRepositoryImpl.java | 19 +- .../useradministration/domain/Role.java | 6 +- ...WritePlatformServiceJpaRepositoryImpl.java | 33 +- .../resources/META-INF/spring/appContext.xml | 3 + .../META-INF/spring/notificationContext.xml | 48 ++ .../V328__notification_module_tables.sql | 45 ++ .../core_db/V331__topic_module_table.sql | 45 ++ .../fineract/notification/Listener.java | 36 ++ .../fineract/notification/ListenerTest.java | 52 ++ .../fineract/notification/SenderTest.java | 77 +++ .../fineract/notification/StorageTest.java | 125 ++++ .../META-INF/testNotificationContext.xml | 53 ++ 66 files changed, 3658 insertions(+), 21 deletions(-) create mode 100644 fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/NotificationApiTest.java create mode 100644 fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/NotificationHelper.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/api/NotificationApiResource.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/cache/CacheNotificationResponseHeader.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/data/NotificationData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/data/NotificationMapperData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/data/TopicData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/data/TopicSubscriberData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/domain/Notification.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationMapper.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationMapperRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/domain/Topic.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicSubscriber.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicSubscriberRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/eventandlistener/NotificationEvent.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/eventandlistener/NotificationEventListener.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/exception/TopicNotFoundException.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorReadPlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorReadPlatformServiceJpaRepositoryImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorWritePlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorWritePlatformServiceJpaRepositoryImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperReadPlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperReadPlatformServiceJpaRepositoryImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperWritePlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperWritePlatformServiceJpaRepositoryImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationWritePlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationWritePlatformServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicReadPlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicReadPlatformServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberReadPlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberReadPlatformServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberWritePlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberWritePlatformServiceJpaRepositoryImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicWritePlatformService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicWritePlatformServiceJpaRepositoryImpl.java create mode 100644 fineract-provider/src/main/resources/META-INF/spring/notificationContext.xml create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V328__notification_module_tables.sql create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V331__topic_module_table.sql create mode 100644 fineract-provider/src/test/java/org/apache/fineract/notification/Listener.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/notification/ListenerTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/notification/SenderTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/notification/StorageTest.java create mode 100644 fineract-provider/src/test/resources/META-INF/testNotificationContext.xml diff --git a/fineract-provider/dependencies.gradle b/fineract-provider/dependencies.gradle index f3310164485..9c325fa0f41 100644 --- a/fineract-provider/dependencies.gradle +++ b/fineract-provider/dependencies.gradle @@ -91,11 +91,15 @@ dependencies { [group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.5'], // Once we've switched to Java 8 this dep can be removed. //[group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.0'] + [group: 'org.springframework', name:'spring-jms'], + [group: 'org.apache.activemq', name:'activemq-broker'] ) testCompile 'junit:junit:4.11', 'junit:junit-dep:4.11', 'org.mockito:mockito-core:1.9.5', 'com.jayway.restassured:rest-assured:2.3.3', - [group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion] - + [group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion], + [group: 'com.mockrunner', name: 'mockrunner-jms', version: '1.0.6'], + [group: 'com.mockrunner', name: 'mockrunner-jdbc', version: '1.0.6'] + } diff --git a/fineract-provider/dev-dependencies.gradle b/fineract-provider/dev-dependencies.gradle index 949b6788fe4..c8878950dc5 100644 --- a/fineract-provider/dev-dependencies.gradle +++ b/fineract-provider/dev-dependencies.gradle @@ -90,11 +90,15 @@ dependencies { [group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.5'], // Once we've switched to Java 8 this dep can be removed. //[group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.0'] + [group: 'org.springframework', name:'spring-jms'], + [group: 'org.apache.activemq', name:'activemq-broker'] ) testCompile 'junit:junit:4.11', 'junit:junit-dep:4.11', 'org.mockito:mockito-core:1.9.5', 'com.jayway.restassured:rest-assured:2.3.3', - [group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion] + [group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion], + [group: 'com.mockrunner', name: 'mockrunner-jms', version: '1.0.6'], + [group: 'com.mockrunner', name: 'mockrunner-jdbc', version: '1.0.6'] } diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/NotificationApiTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/NotificationApiTest.java new file mode 100644 index 00000000000..96dd4969642 --- /dev/null +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/NotificationApiTest.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.integrationtests; + +import java.util.HashMap; + +import org.apache.fineract.integrationtests.common.NotificationHelper; +import org.apache.fineract.integrationtests.common.Utils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.builder.ResponseSpecBuilder; +import com.jayway.restassured.http.ContentType; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +public class NotificationApiTest { + + private ResponseSpecification responseSpec; + private RequestSpecification requestSpec; + + + @Before + public void setup() { + Utils.initializeRESTAssured(); + this.requestSpec = new RequestSpecBuilder().setContent(ContentType.JSON).build(); + this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + } + + @SuppressWarnings("unchecked") + @Test + public void testNotificationRetrieval() { + HashMap response = (HashMap) NotificationHelper.getNotifications(this.requestSpec, + this.responseSpec, ""); + System.out.println("Response : " + response.toString()); + Assert.assertNotNull(response); + } +} diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/NotificationHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/NotificationHelper.java new file mode 100644 index 00000000000..33eaa6fabc0 --- /dev/null +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/NotificationHelper.java @@ -0,0 +1,43 @@ +/** + * 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.integrationtests.common; + +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +public class NotificationHelper { + + private final RequestSpecification requestSpec; + private final ResponseSpecification reponseSpec; + + private static final String NOTIFICATION_API_URL = "/fineract-provider/api/v1/notifications?" + Utils.TENANT_IDENTIFIER; + + public NotificationHelper(RequestSpecification requestSpec, ResponseSpecification reponseSpec) { + this.requestSpec = requestSpec; + this.reponseSpec = reponseSpec; + } + + public static Object getNotifications(final RequestSpecification requestSpec,final ResponseSpecification responseSpec, + final String jsonString) { + final String GET_NOTIFICATIONS_URL = NOTIFICATION_API_URL; + System.out.println("-----------------------------GET NOTIFICATIONS-----------------------------------"); + return Utils.performServerGet(requestSpec, responseSpec, GET_NOTIFICATIONS_URL, ""); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java index abe243ef6f7..22b58b51aa1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java @@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.time.StopWatch; +import org.apache.fineract.notification.service.NotificationReadPlatformService; import org.apache.fineract.infrastructure.cache.domain.CacheType; import org.apache.fineract.infrastructure.cache.service.CacheWritePlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; @@ -78,6 +79,7 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil private final ConfigurationDomainService configurationDomainService; private final CacheWritePlatformService cacheWritePlatformService; + private final NotificationReadPlatformService notificationReadPlatformService; private final String tenantRequestHeader = "Fineract-Platform-TenantId"; private final boolean exceptionIfHeaderMissing = true; @@ -85,12 +87,14 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil public TenantAwareBasicAuthenticationFilter(final AuthenticationManager authenticationManager, final AuthenticationEntryPoint authenticationEntryPoint, final BasicAuthTenantDetailsService basicAuthTenantDetailsService, final ToApiJsonSerializer toApiJsonSerializer, final ConfigurationDomainService configurationDomainService, - final CacheWritePlatformService cacheWritePlatformService) { + final CacheWritePlatformService cacheWritePlatformService, + final NotificationReadPlatformService notificationReadPlatformService) { super(authenticationManager, authenticationEntryPoint); this.basicAuthTenantDetailsService = basicAuthTenantDetailsService; this.toApiJsonSerializer = toApiJsonSerializer; this.configurationDomainService = configurationDomainService; this.cacheWritePlatformService = cacheWritePlatformService; + this.notificationReadPlatformService = notificationReadPlatformService; } @Override @@ -168,6 +172,12 @@ protected void onSuccessfulAuthentication(HttpServletRequest request, super.onSuccessfulAuthentication(request, response, authResult); AppUser user = (AppUser) authResult.getPrincipal(); + if(notificationReadPlatformService.hasUnreadNotifications(user.getId())) { + response.addHeader("X-Notification-Refresh", "true"); + } else { + response.addHeader("X-Notification-Refresh", "false"); + } + String pathURL = request.getRequestURI(); boolean isSelfServiceRequest = (pathURL != null && pathURL.contains("/self/")); diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/api/NotificationApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/notification/api/NotificationApiResource.java new file mode 100644 index 00000000000..4826dd8d7b8 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/api/NotificationApiResource.java @@ -0,0 +1,88 @@ +/** + * 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.notification.api; + +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.apache.fineract.notification.data.NotificationData; +import org.apache.fineract.notification.service.NotificationReadPlatformService; +import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; +import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/notifications") +@Component +@Scope("singleton") +public class NotificationApiResource { + + private final PlatformSecurityContext context; + private final NotificationReadPlatformService notificationReadPlatformService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final ToApiJsonSerializer toApiJsonSerializer; + + @Autowired + public NotificationApiResource(PlatformSecurityContext context, + NotificationReadPlatformService notificationReadPlatformService, + ApiRequestParameterHelper apiRequestParameterHelper, + ToApiJsonSerializer toApiJsonSerializer) { + this.context = context; + this.notificationReadPlatformService = notificationReadPlatformService; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.toApiJsonSerializer = toApiJsonSerializer; + } + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String getAllNotifications(@Context final UriInfo uriInfo, @QueryParam("orderBy") final String orderBy, + @QueryParam("limit") final Integer limit, + @QueryParam("offset") final Integer offset, + @QueryParam("sortOrder") final String sortOrder, + @QueryParam("isRead") final boolean isRead) { + + this.context.authenticatedUser(); + final Page notificationData; + final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder); + if (!isRead) { + notificationData = this.notificationReadPlatformService.getAllUnreadNotifications(searchParameters); + } else { + notificationData = this.notificationReadPlatformService.getAllNotifications(searchParameters); + } + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, notificationData); + } + + @PUT + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public void update(){ + this.context.authenticatedUser(); + this.notificationReadPlatformService.updateNotificationReadStatus(); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/cache/CacheNotificationResponseHeader.java b/fineract-provider/src/main/java/org/apache/fineract/notification/cache/CacheNotificationResponseHeader.java new file mode 100644 index 00000000000..c250e93f104 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/cache/CacheNotificationResponseHeader.java @@ -0,0 +1,49 @@ +/** + * 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.notification.cache; + +public class CacheNotificationResponseHeader { + + private boolean hasNotifications; + private Long lastFetch; + + public CacheNotificationResponseHeader() { + } + + public CacheNotificationResponseHeader(boolean hasNotifications, Long lastFetch) { + this.hasNotifications = hasNotifications; + this.lastFetch = lastFetch; + } + + public boolean hasNotifications() { + return hasNotifications; + } + + public void setHasNotifications(boolean hasNotifications) { + this.hasNotifications = hasNotifications; + } + + public Long getLastFetch() { + return lastFetch; + } + + public void setLastFetch(Long lastFetch) { + this.lastFetch = lastFetch; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/data/NotificationData.java b/fineract-provider/src/main/java/org/apache/fineract/notification/data/NotificationData.java new file mode 100644 index 00000000000..54c632182c2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/data/NotificationData.java @@ -0,0 +1,142 @@ +/** + * 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.notification.data; + +import java.io.Serializable; +import java.util.List; + +public class NotificationData implements Serializable { + + private Long id; + private String objectType; + private Long objectId; + private String action; + private Long actorId; + private String content; + private boolean isSystemGenerated; + private String tenantIdentifier; + private String createdAt; + private Long officeId; + private List userIds; + + public NotificationData() { + } + + public NotificationData(String objectType, Long objectId, String action, Long actorId, String content, + boolean isSystemGenerated, String tenantIdentifier, Long officeId, List userIds) { + this.objectType = objectType; + this.objectId = objectId; + this.action = action; + this.actorId = actorId; + this.content = content; + this.isSystemGenerated = isSystemGenerated; + this.tenantIdentifier = tenantIdentifier; + this.officeId = officeId; + this.userIds = userIds; + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getObjectType() { + return this.objectType; + } + + public void setObjectType(String objectType) { + this.objectType = objectType; + } + + public Long getObjectIdentfier() { + return this.objectId; + } + + public void entifier(Long objectId) { + this.objectId = objectId; + } + + public String getAction() { + return this.action; + } + + public void setAction(String action) { + this.action = action; + } + + public Long getActorId() { + return this.actorId; + } + + public void setActorId(Long actorId) { + this.actorId = actorId; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + + public boolean isSystemGenerated() { + return this.isSystemGenerated; + } + + public void setSystemGenerated(boolean isSystemGenerated) { + this.isSystemGenerated = isSystemGenerated; + } + + public String getTenantIdentifier() { + return this.tenantIdentifier; + } + + public void setTenantIdentifier(String tenantIdentifier) { + this.tenantIdentifier = tenantIdentifier; + } + + public String getCreatedAt() { + return this.createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + public Long getOfficeId() { + return this.officeId; + } + + public void setOfficeId(Long officeId) { + this.officeId = officeId; + } + + public List getUserIds() { + return this.userIds; + } + + public void setUserIds(List userIds) { + this.userIds = userIds; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/data/NotificationMapperData.java b/fineract-provider/src/main/java/org/apache/fineract/notification/data/NotificationMapperData.java new file mode 100644 index 00000000000..ee3ca3c8fae --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/data/NotificationMapperData.java @@ -0,0 +1,80 @@ +/** + * 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.notification.data; + +public class NotificationMapperData { + + private Long id; + private Long notificationId; + private Long userId; + private boolean isRead; + private String createdAt; + + public NotificationMapperData() { + } + + public NotificationMapperData(Long id, Long notificationId, Long userId, boolean isRead, String createdAt) { + this.id = id; + this.notificationId = notificationId; + this.userId = userId; + this.isRead = isRead; + this.createdAt = createdAt; + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getNotificationId() { + return this.notificationId; + } + + public void setNotificationId(Long notificationId) { + this.notificationId = notificationId; + } + + public Long getUserId() { + return this.userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public boolean isRead() { + return this.isRead; + } + + public void setRead(boolean isRead) { + this.isRead = isRead; + } + + public String getCreatedAt() { + return this.createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/data/TopicData.java b/fineract-provider/src/main/java/org/apache/fineract/notification/data/TopicData.java new file mode 100644 index 00000000000..9e070fbd2df --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/data/TopicData.java @@ -0,0 +1,76 @@ +/** + * 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.notification.data; + +import java.io.Serializable; + +public class TopicData implements Serializable { + + private final Long id; + private final String title; + private final boolean enabled; + private final Long entityId; + private final String entityType; + private final String memberType; + + public TopicData(Long id, String title, boolean enabled, Long entityId, String entityType, + String memberType) { + this.id = id; + this.title = title; + this.enabled = enabled; + this.entityId = entityId; + this.entityType = entityType; + this.memberType = memberType; + } + + public TopicData(Long id, String title, Long entityId, String entityType, + String memberType) { + this.id = id; + this.title = title; + this.enabled = true; + this.entityId = entityId; + this.entityType = entityType; + this.memberType = memberType; + } + + public Long getId() { + return this.id; + } + + public String getTitle() { + return this.title; + } + + public boolean isEnabled() { + return this.enabled; + } + + public Long getEntityId() { + return this.entityId; + } + + public String getEntityType() { + return this.entityType; + } + + public String getMemberType() { + return this.memberType; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/data/TopicSubscriberData.java b/fineract-provider/src/main/java/org/apache/fineract/notification/data/TopicSubscriberData.java new file mode 100644 index 00000000000..76916947214 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/data/TopicSubscriberData.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.notification.data; + +import org.joda.time.LocalDate; + +public class TopicSubscriberData { + + private final Long id; + private final Long topicId; + private final Long userId; + private final LocalDate subscriptionDate; + + public TopicSubscriberData(Long id, Long topicId, Long userId, LocalDate subscriptionDate) { + this.id = id; + this.topicId = topicId; + this.userId = userId; + this.subscriptionDate = subscriptionDate; + } + + public TopicSubscriberData(Long id, Long topicId, Long userId) { + this.id = id; + this.topicId = topicId; + this.userId = userId; + this.subscriptionDate = new LocalDate(); + } + + public Long getId() { + return this.id; + } + + public Long getTopicId() { + return this.topicId; + } + + public Long getUserId() { + return this.userId; + } + + public LocalDate getSubscriptionDate() { + return this.subscriptionDate; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/domain/Notification.java b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/Notification.java new file mode 100644 index 00000000000..8be99272dc2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/Notification.java @@ -0,0 +1,122 @@ +/** + * 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.notification.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; + +@Entity +@Table(name = "notification_generator") +public class Notification extends AbstractPersistableCustom { + + @Column(name = "object_type") + private String objectType; + + @Column(name = "object_identifier") + private Long objectIdentifier; + + @Column(name = "action") + private String action; + + @Column(name = "actor") + private Long actorId; + + @Column(name = "is_system_generated") + private boolean isSystemGenerated; + + @Column(name = "notification_content") + private String notificationContent; + + @Column(name = "created_at") + private String createdAt; + + public Notification() { + } + + public Notification(String objectType, Long objectIdentifier, String action, Long actorId, + boolean isSystemGenerated, String notificationContent, String createdAt) { + this.objectType = objectType; + this.objectIdentifier = objectIdentifier; + this.action = action; + this.actorId = actorId; + this.isSystemGenerated = isSystemGenerated; + this.notificationContent = notificationContent; + this.createdAt = createdAt; + } + + public String getObjectType() { + return this.objectType; + } + + public void setObjectType(String objectType) { + this.objectType = objectType; + } + + public Long getObjectIdentifier() { + return this.objectIdentifier; + } + + public void setObjectIdentifier(Long objectIdentifier) { + this.objectIdentifier = objectIdentifier; + } + + public String getAction() { + return this.action; + } + + public void setAction(String action) { + this.action = action; + } + + public Long getActorId() { + return this.actorId; + } + + public void setActorId(Long actorId) { + this.actorId = actorId; + } + + public boolean isSystemGenerated() { + return this.isSystemGenerated; + } + + public void setSystemGenerated(boolean isSystemGenerated) { + this.isSystemGenerated = isSystemGenerated; + } + + public String getNotificationContent() { + return this.notificationContent; + } + + public void setNotificationContent(String notificationContent) { + this.notificationContent = notificationContent; + } + + public String getCreatedAt() { + return this.createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationMapper.java b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationMapper.java new file mode 100644 index 00000000000..388166952d7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationMapper.java @@ -0,0 +1,82 @@ +/** + * 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.notification.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.useradministration.domain.AppUser; + +@Entity +@Table(name = "notification_mapper") +public class NotificationMapper extends AbstractPersistableCustom { + + @ManyToOne + @JoinColumn(name = "notification_id") + private Notification notification; + + @ManyToOne + @JoinColumn(name = "user_id") + private AppUser userId; + + @Column(name = "is_read") + private boolean isRead; + + @Column(name = "created_at") + private String createdAt; + + public NotificationMapper() { + } + + public NotificationMapper(Notification notification, AppUser userId, boolean isRead, String createdAt) { + this.notification = notification; + this.userId = userId; + this.isRead = isRead; + this.createdAt = createdAt; + } + + public Notification getNotification() { + return this.notification; + } + + public void setNotification(Notification notification) { + this.notification = notification; + } + + public AppUser getUserId() { + return this.userId; + } + + public void setUserId(AppUser userId) { + this.userId = userId; + } + + public boolean isRead() { + return this.isRead; + } + + public void setRead(boolean isRead) { + this.isRead = isRead; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationMapperRepository.java b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationMapperRepository.java new file mode 100644 index 00000000000..143180b5d89 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationMapperRepository.java @@ -0,0 +1,24 @@ +/** + * 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.notification.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NotificationMapperRepository extends JpaRepository { +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationRepository.java b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationRepository.java new file mode 100644 index 00000000000..4a53737eae9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/NotificationRepository.java @@ -0,0 +1,24 @@ +/** + * 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.notification.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NotificationRepository extends JpaRepository { +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/domain/Topic.java b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/Topic.java new file mode 100644 index 00000000000..0c9ab7ce43c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/Topic.java @@ -0,0 +1,122 @@ +/** + * 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.notification.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; + +@Entity +@Table(name = "topic") +public class Topic extends AbstractPersistableCustom { + + @Column(name = "title", unique = true, nullable = false, length = 100) + private String title; + + @Column(name = "enabled", nullable = false) + private Boolean enabled; + + @Column(name = "entity_id", nullable = false) + private Long entityId; + + @Column(name = "entity_type") + private String entityType; + + @Column(name = "member_type") + private String memberType; + + public Topic() { + } + + public Topic(String title, Boolean enabled, Long entityId, String entityType, String memberType) { + this.title = title.trim(); + this.enabled = enabled; + this.entityId = entityId; + this.entityType = entityType.trim(); + this.memberType = memberType.trim(); + } + + public static Topic fromJson(final JsonCommand command) { + String title = ""; + Boolean enabled = null; + Long entityId = 0L; + String entityType = ""; + String memberType = ""; + + if (command.hasParameter("title")) { + title = command.stringValueOfParameterNamed("title"); + } + if (command.hasParameter("enabled")) { + enabled = command.booleanPrimitiveValueOfParameterNamed("enabled"); + } + if (command.hasParameter("entityId")) { + entityId = command.longValueOfParameterNamed("entityId"); + } + if (command.hasParameter("entityType")) { + entityType = command.stringValueOfParameterNamed("entityType"); + } + if (command.hasParameter("memberType")) { + memberType = command.stringValueOfParameterNamed("memberType"); + } + return new Topic(title, enabled, entityId, entityType, memberType); + } + + public String getTitle() { + return this.title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Boolean getEnabled() { + return this.enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public Long getEntityId() { + return this.entityId; + } + + public void setEntityId(Long entityId) { + this.entityId = entityId; + } + + public String getEntityType() { + return this.entityType; + } + + public void setEntityType(String entityType) { + this.entityType = entityType; + } + + public String getMemberType() { + return this.memberType; + } + + public void setMemberType(String memberType) { + this.memberType = memberType; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicRepository.java b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicRepository.java new file mode 100644 index 00000000000..dfb9b220780 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicRepository.java @@ -0,0 +1,24 @@ +/** + * 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.notification.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TopicRepository extends JpaRepository { +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicSubscriber.java b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicSubscriber.java new file mode 100644 index 00000000000..74ef96021f6 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicSubscriber.java @@ -0,0 +1,80 @@ +/** + * 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.notification.domain; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.useradministration.domain.AppUser; + +@Entity +@Table(name = "topic_subscriber") +public class TopicSubscriber extends AbstractPersistableCustom { + + @ManyToOne + @JoinColumn(name = "topic_id") + private Topic topic; + + @ManyToOne + @JoinColumn(name = "user_id") + private AppUser subscriber; + + @Column(name = "subscription_date") + private Date subscriptionDate; + + public TopicSubscriber() { + } + + public TopicSubscriber(Topic topic, AppUser subscriber, Date subscriptionDate) { + this.topic = topic; + this.subscriber = subscriber; + this.subscriptionDate = subscriptionDate; + } + + public Topic getTopic() { + return this.topic; + } + + public void setTopic(Topic topic) { + this.topic = topic; + } + + public AppUser getSubscriber() { + return this.subscriber; + } + + public void setSubscriber(AppUser subscriber) { + this.subscriber = subscriber; + } + + public Date getSubscriptionDate() { + return this.subscriptionDate; + } + + public void setSubscriptionDate(Date subscriptionDate) { + this.subscriptionDate = subscriptionDate; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicSubscriberRepository.java b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicSubscriberRepository.java new file mode 100644 index 00000000000..a7ea3c122e6 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/domain/TopicSubscriberRepository.java @@ -0,0 +1,24 @@ +/** + * 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.notification.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TopicSubscriberRepository extends JpaRepository { +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/eventandlistener/NotificationEvent.java b/fineract-provider/src/main/java/org/apache/fineract/notification/eventandlistener/NotificationEvent.java new file mode 100644 index 00000000000..9df5215cd31 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/eventandlistener/NotificationEvent.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.notification.eventandlistener; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +import org.apache.fineract.notification.data.NotificationData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.core.JmsTemplate; +import org.springframework.jms.core.MessageCreator; +import org.springframework.stereotype.Service; + +@Service +public class NotificationEvent { + + private final JmsTemplate jmsTemplate; + + @Autowired + public NotificationEvent(JmsTemplate jmsTemplate) { + this.jmsTemplate = jmsTemplate; + } + + public void broadcastNotification(final Destination destination, final NotificationData notificationData) { + this.jmsTemplate.send(destination, new MessageCreator() { + @Override + public Message createMessage(Session session) throws JMSException { + return session.createObjectMessage(notificationData); + } + }); + } + +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/eventandlistener/NotificationEventListener.java b/fineract-provider/src/main/java/org/apache/fineract/notification/eventandlistener/NotificationEventListener.java new file mode 100644 index 00000000000..a7aee967c33 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/eventandlistener/NotificationEventListener.java @@ -0,0 +1,74 @@ +/** + * 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.notification.eventandlistener; + +import java.util.List; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.ObjectMessage; +import javax.jms.Session; + +import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; +import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.security.service.BasicAuthTenantDetailsService; +import org.apache.fineract.notification.data.NotificationData; +import org.apache.fineract.notification.service.NotificationWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.listener.SessionAwareMessageListener; + +public class NotificationEventListener implements SessionAwareMessageListener { + + private final BasicAuthTenantDetailsService basicAuthTenantDetailsService; + private final NotificationWritePlatformService notificationWritePlatformService; + + @Autowired + public NotificationEventListener(BasicAuthTenantDetailsService basicAuthTenantDetailsService, + NotificationWritePlatformService notificationWritePlatformService) { + this.basicAuthTenantDetailsService = basicAuthTenantDetailsService; + this.notificationWritePlatformService = notificationWritePlatformService; + } + + @Override + public void onMessage(Message message, Session session) throws JMSException { + if (message instanceof ObjectMessage) { + NotificationData notificationData = (NotificationData) ((ObjectMessage) message).getObject(); + + final FineractPlatformTenant tenant = this.basicAuthTenantDetailsService.loadTenantById(notificationData.getTenantIdentifier(), false); + ThreadLocalContextUtil.setTenant(tenant); + + Long appUserId = notificationData.getActorId(); + List userIds = notificationData.getUserIds(); + + if (userIds.contains(appUserId)) { + userIds.remove(appUserId); + } + + notificationWritePlatformService.notify( + userIds, + notificationData.getObjectType(), + notificationData.getObjectIdentfier(), + notificationData.getAction(), + notificationData.getActorId(), + notificationData.getContent(), + notificationData.isSystemGenerated() + ); + + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/exception/TopicNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/notification/exception/TopicNotFoundException.java new file mode 100644 index 00000000000..857ef4c4fa4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/exception/TopicNotFoundException.java @@ -0,0 +1,32 @@ +/** + * 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.notification.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when topic resources are not found. + */ +public class TopicNotFoundException extends AbstractPlatformResourceNotFoundException { + + public TopicNotFoundException(final Long id) { + super("error.msg.topic.id.invalid", "Topic with identifier " + id + " does not exist", id); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainService.java new file mode 100644 index 00000000000..7044aa90e69 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainService.java @@ -0,0 +1,23 @@ +/** + * 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.notification.service; + +public interface NotificationDomainService { + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainServiceImpl.java new file mode 100644 index 00000000000..9b6a48ee5de --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainServiceImpl.java @@ -0,0 +1,599 @@ +/** + * 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.notification.service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import javax.annotation.PostConstruct; +import javax.jms.Queue; + +import org.apache.activemq.command.ActiveMQQueue; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.notification.data.NotificationData; +import org.apache.fineract.notification.data.TopicSubscriberData; +import org.apache.fineract.notification.eventandlistener.NotificationEvent; +import org.apache.fineract.organisation.office.domain.OfficeRepository; +import org.apache.fineract.portfolio.client.domain.Client; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.apache.fineract.portfolio.common.service.BusinessEventListner; +import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; +import org.apache.fineract.portfolio.savings.DepositAccountType; +import org.apache.fineract.portfolio.savings.domain.FixedDepositAccount; +import org.apache.fineract.portfolio.savings.domain.RecurringDepositAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction; +import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount; +import org.apache.fineract.useradministration.domain.Role; +import org.apache.fineract.useradministration.domain.RoleRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class NotificationDomainServiceImpl implements NotificationDomainService { + + private final BusinessEventNotifierService businessEventNotifierService; + private final NotificationEvent notificationEvent; + private final PlatformSecurityContext context; + private final RoleRepository roleRepository; + private final OfficeRepository officeRepository; + private final TopicSubscriberReadPlatformService topicSubscriberReadPlatformService; + + @Autowired + public NotificationDomainServiceImpl(final BusinessEventNotifierService businessEventNotifierService, + final NotificationEvent notificationEvent, + final PlatformSecurityContext context, final RoleRepository roleRepository, + final TopicSubscriberReadPlatformService topicSubscriberReadPlatformService, + final OfficeRepository officeRepository) { + + this.businessEventNotifierService = businessEventNotifierService; + this.notificationEvent = notificationEvent; + this.context = context; + this.roleRepository = roleRepository; + this.topicSubscriberReadPlatformService = topicSubscriberReadPlatformService; + this.officeRepository = officeRepository; + } + + @PostConstruct + public void addListeners() { + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.CLIENTS_CREATE, + new ClientCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.SAVINGS_APPROVE, + new SavingsAccountApprovedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.CENTERS_CREATE, + new CenterCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.GROUPS_CREATE, + new GroupCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.SAVINGS_DEPOSIT, + new SavingsAccountDepositListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.SHARE_PRODUCT_DIVIDENDS_CREATE, + new ShareProductDividendCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.FIXED_DEPOSIT_ACCOUNT_CREATE, + new FixedDepositAccountCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.RECURRING_DEPOSIT_ACCOUNT_CREATE, + new RecurringDepositAccountCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.SAVINGS_POST_INTEREST, + new SavingsPostInterestListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_CREATE, + new LoanCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_APPROVED, + new LoanApprovedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_CLOSE, + new LoanClosedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_CLOSE_AS_RESCHEDULE, + new LoanCloseAsRescheduledListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT, + new LoanMakeRepaymentListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_PRODUCT_CREATE, + new LoanProductCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.SAVINGS_CREATE, + new SavingsAccountCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.SAVINGS_CLOSE, + new SavingsAccountClosedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.SHARE_ACCOUNT_CREATE, + new ShareAccountCreatedListener()); + businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.SHARE_ACCOUNT_APPROVE, + new ShareAccountApprovedListener()); + } + + private abstract class NotificationBusinessEventAdapter implements BusinessEventListner { + @Override + public void businessEventToBeExecuted(Map businessEventEntity) { + } + } + + private class ClientCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + Client client; + Object entity = businessEventEntity.get(BusinessEventNotificationConstants.BUSINESS_ENTITY.CLIENT); + if (entity != null) { + client = (Client) entity; + buildNotification( + "ACTIVATE_CLIENT", + "client", + client.getId(), + "New client created", + "created", + context.authenticatedUser().getId(), + client.getOffice().getId() + ); + } + } + } + + private class CenterCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + CommandProcessingResult commandProcessingResult; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.GROUP); + if (entity != null) { + commandProcessingResult = (CommandProcessingResult) entity; + buildNotification( + "ACTIVATE_CENTER", + "center", + commandProcessingResult.getGroupId(), + "New center created", + "created", + context.authenticatedUser().getId(), + commandProcessingResult.getOfficeId() + ); + } + } + } + + private class GroupCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + CommandProcessingResult commandProcessingResult; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.GROUP); + if (entity != null) { + commandProcessingResult = (CommandProcessingResult) entity; + buildNotification( + "ACTIVATE_GROUP", + "group", + commandProcessingResult.getGroupId(), + "New group created", + "created", + context.authenticatedUser().getId(), + commandProcessingResult.getOfficeId() + ); + } + } + } + + private class SavingsAccountDepositListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + SavingsAccountTransaction savingsAccountTransaction; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.SAVINGS_TRANSACTION); + if (entity != null) { + savingsAccountTransaction = (SavingsAccountTransaction) entity; + buildNotification( + "READ_SAVINGSACCOUNT", + "savingsAccount", + savingsAccountTransaction.getSavingsAccount().getId(), + "Deposit made", + "depositMade", + context.authenticatedUser().getId(), + savingsAccountTransaction.getSavingsAccount().officeId() + ); + } + } + } + + private class ShareProductDividendCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + Long shareProductId; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.SHARE_PRODUCT); + if (entity != null) { + shareProductId = (Long) entity; + buildNotification( + "READ_DIVIDEND_SHAREPRODUCT", + "shareProduct", + shareProductId, + "Dividend posted to account", + "dividendPosted", + context.authenticatedUser().getId(), + context.authenticatedUser().getOffice().getId() + ); + } + } + } + + private class FixedDepositAccountCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + FixedDepositAccount fixedDepositAccount; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.DEPOSIT_ACCOUNT); + if (entity != null) { + fixedDepositAccount = (FixedDepositAccount) entity; + buildNotification( + "APPROVE_FIXEDDEPOSITACCOUNT", + "fixedDeposit", + fixedDepositAccount.getId(), + "New fixed deposit account created", + "created", + context.authenticatedUser().getId(), + fixedDepositAccount.officeId() + ); + } + } + } + + private class RecurringDepositAccountCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + RecurringDepositAccount recurringDepositAccount; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.DEPOSIT_ACCOUNT); + if (entity != null) { + recurringDepositAccount = (RecurringDepositAccount) entity; + buildNotification( + "APPROVE_RECURRINGDEPOSITACCOUNT", + "recurringDepositAccount", + recurringDepositAccount.getId(), + "New recurring deposit account created", + "created", + context.authenticatedUser().getId(), + recurringDepositAccount.officeId() + ); + } + } + } + + private class SavingsAccountApprovedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + SavingsAccount savingsAccount; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.SAVING); + if (entity != null) { + savingsAccount = (SavingsAccount) entity; + if (savingsAccount.depositAccountType().equals(DepositAccountType.FIXED_DEPOSIT)) { + + buildNotification( + "ACTIVATE_FIXEDDEPOSITACCOUNT", + "fixedDeposit", + savingsAccount.getId(), + "Fixed deposit account approved", + "approved", + context.authenticatedUser().getId(), + savingsAccount.officeId() + ); + } else if (savingsAccount.depositAccountType().equals(DepositAccountType.RECURRING_DEPOSIT)) { + + buildNotification( + "ACTIVATE_RECURRINGDEPOSITACCOUNT", + "recurringDepositAccount", + savingsAccount.getId(), + "Recurring deposit account approved", + "approved", + context.authenticatedUser().getId(), + savingsAccount.officeId() + ); + } else if (savingsAccount.depositAccountType().equals(DepositAccountType.SAVINGS_DEPOSIT)) { + + buildNotification( + "ACTIVATE_SAVINGSACCOUNT", + "savingsAccount", + savingsAccount.getId(), + "Savings account approved", + "approved", + context.authenticatedUser().getId(), + savingsAccount.officeId() + ); + } + } + } + } + + private class SavingsPostInterestListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + SavingsAccount savingsAccount; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.SAVING); + if (entity != null) { + savingsAccount = (SavingsAccount) entity; + buildNotification( + "READ_SAVINGSACCOUNT", + "savingsAccount", + savingsAccount.getId(), + "Interest posted to account", + "interestPosted", + context.authenticatedUser().getId(), + savingsAccount.officeId() + ); + } + } + } + + private class LoanCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + Loan loan; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.LOAN); + if (entity != null) { + loan = (Loan) entity; + buildNotification( + "APPROVE_LOAN", + "loan", + loan.getId(), + "New loan created", + "created", + context.authenticatedUser().getId(), + loan.getOfficeId() + ); + } + + } + } + + private class LoanApprovedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + Loan loan; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.LOAN); + if (entity != null) { + loan = (Loan) entity; + buildNotification( + "DISBURSE_LOAN", + "loan", + loan.getId(), + "New loan approved", + "approved", + context.authenticatedUser().getId(), + loan.getOfficeId() + ); + } + } + } + + private class LoanClosedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + + Loan loan; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.LOAN); + if (entity != null) { + loan = (Loan) entity; + buildNotification( + "READ_LOAN", + "loan", + loan.getId(), + "Loan closed", + "loanClosed", + context.authenticatedUser().getId(), + loan.getOfficeId() + ); + } + } + } + + private class LoanCloseAsRescheduledListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + Loan loan; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.LOAN); + if (entity != null) { + loan = (Loan) entity; + buildNotification( + "READ_Rescheduled Loans", + "loan", + loan.getId(), + "Loan has been rescheduled", + "loanRescheduled", + context.authenticatedUser().getId(), + loan.getOfficeId() + ); + } + } + } + + private class LoanMakeRepaymentListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + Loan loan; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.LOAN); + if (entity != null) { + loan = (Loan) entity; + buildNotification( + "READ_LOAN", + "loan", + loan.getId(), + "Repayment made", + "repaymentMade", + context.authenticatedUser().getId(), + loan.getOfficeId() + ); + } + } + } + + private class LoanProductCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + + LoanProduct loanProduct; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.LOAN_PRODUCT); + if (entity != null) { + loanProduct = (LoanProduct) entity; + buildNotification( + "READ_LOANPRODUCT", + "loanProduct", + loanProduct.getId(), + "New loan product created", + "created", + context.authenticatedUser().getId(), + context.authenticatedUser().getOffice().getId() + ); + } + } + } + + private class SavingsAccountCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + SavingsAccount savingsAccount; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.SAVING); + if (entity != null) { + savingsAccount = (SavingsAccount) entity; + buildNotification( + "APPROVE_SAVINGSACCOUNT", + "savingsAccount", + savingsAccount.getId(), + "New savings account created", + "created", + context.authenticatedUser().getId(), + savingsAccount.officeId() + ); + } + } + } + + private class SavingsAccountClosedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + SavingsAccount savingsAccount; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.SAVING); + if (entity != null) { + savingsAccount = (SavingsAccount) entity; + buildNotification( + "READ_SAVINGSACCOUNT", + "savingsAccount", + savingsAccount.getId(), + "Savings has gone into dormant", + "closed", + context.authenticatedUser().getId(), + savingsAccount.officeId() + ); + } + } + } + + private class ShareAccountCreatedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + ShareAccount shareAccount; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.SHARE_ACCOUNT); + if (entity != null) { + shareAccount = (ShareAccount) entity; + buildNotification( + "APPROVE_SHAREACCOUNT", + "shareAccount", + shareAccount.getId(), + "New share account created", + "created", + context.authenticatedUser().getId(), + shareAccount.getOfficeId() + ); + } + } + } + + private class ShareAccountApprovedListener extends NotificationBusinessEventAdapter { + + @Override + public void businessEventWasExecuted(Map businessEventEntity) { + ShareAccount shareAccount; + Object entity = businessEventEntity.get(BUSINESS_ENTITY.SHARE_ACCOUNT); + if (entity != null) { + shareAccount = (ShareAccount) entity; + buildNotification( + "ACTIVATE_SHAREACCOUNT", + "shareAccount", + shareAccount.getId(), + "Share account approved", + "approved", + context.authenticatedUser().getId(), + shareAccount.getOfficeId() + ); + } + } + } + + private void buildNotification(String permission, String objectType, Long objectIdentifier, + String notificationContent, String eventType, Long appUserId, Long officeId) { + + String tenantIdentifier = ThreadLocalContextUtil.getTenant().getTenantIdentifier(); + Queue queue = new ActiveMQQueue("NotificationQueue"); + List userIds = retrieveSubscribers(officeId, permission); + NotificationData notificationData = new NotificationData( + objectType, + objectIdentifier, + eventType, + appUserId, + notificationContent, + false, + tenantIdentifier, + officeId, + userIds + ); + notificationEvent.broadcastNotification(queue, notificationData); + } + + private List retrieveSubscribers(Long officeId, String permission) { + + Collection topicSubscribers = new ArrayList<>(); + List subscriberIds = new ArrayList<>(); + Long entityId = officeId; + String entityType= ""; + if (officeRepository.findOne(entityId).getParent() == null) { + entityType = "OFFICE"; + } else { + entityType = "BRANCH"; + } + List allRoles = roleRepository.findAll(); + for (Role curRole : allRoles) { + if (curRole.hasPermissionTo(permission)) { + String memberType = curRole.getName(); + topicSubscribers = topicSubscriberReadPlatformService.getSubscribers(entityId, entityType, memberType); + } + } + + for (TopicSubscriberData topicSubscriber : topicSubscribers) { + subscriberIds.add(topicSubscriber.getUserId()); + } + return subscriberIds; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorReadPlatformService.java new file mode 100644 index 00000000000..455958f6106 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorReadPlatformService.java @@ -0,0 +1,33 @@ +/** + * 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.notification.service; + +import java.util.List; + +import org.apache.fineract.notification.domain.Notification; + +public interface NotificationGeneratorReadPlatformService { + + Notification findById(Long id); + + List fetchAllNotifications(); + + void delete(Long id); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorReadPlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorReadPlatformServiceJpaRepositoryImpl.java new file mode 100644 index 00000000000..b693a25777e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorReadPlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,53 @@ +/** + * 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.notification.service; + +import java.util.List; + +import org.apache.fineract.notification.domain.Notification; +import org.apache.fineract.notification.domain.NotificationRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class NotificationGeneratorReadPlatformServiceJpaRepositoryImpl implements NotificationGeneratorReadPlatformService { + + private final NotificationRepository notificationRepository; + + @Autowired + public NotificationGeneratorReadPlatformServiceJpaRepositoryImpl(NotificationRepository notificationRepository) { + this.notificationRepository = notificationRepository; + } + + @Override + public Notification findById(Long id) { + return this.notificationRepository.findOne(id); + } + + @Override + public List fetchAllNotifications() { + return this.notificationRepository.findAll(); + } + + @Override + public void delete(Long id) { + this.notificationRepository.delete(id); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorWritePlatformService.java new file mode 100644 index 00000000000..8a0cb180cab --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorWritePlatformService.java @@ -0,0 +1,25 @@ +/** + * 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.notification.service; + +import org.apache.fineract.notification.domain.Notification; + +public interface NotificationGeneratorWritePlatformService { + Long create(Notification notification); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 00000000000..95f224880fe --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationGeneratorWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,42 @@ +/** + * 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.notification.service; + +import org.apache.fineract.notification.domain.Notification; +import org.apache.fineract.notification.domain.NotificationRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class NotificationGeneratorWritePlatformServiceJpaRepositoryImpl implements NotificationGeneratorWritePlatformService { + + private final NotificationRepository notificationRepository; + + @Autowired + public NotificationGeneratorWritePlatformServiceJpaRepositoryImpl(NotificationRepository notificationRepository) { + this.notificationRepository = notificationRepository; + } + + @Override + public Long create(Notification notification) { + this.notificationRepository.save(notification); + return notification.getId(); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperReadPlatformService.java new file mode 100644 index 00000000000..e5e0786263a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperReadPlatformService.java @@ -0,0 +1,33 @@ +/** + * 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.notification.service; + +import java.util.List; + +import org.apache.fineract.notification.domain.NotificationMapper; + +public interface NotificationMapperReadPlatformService { + + NotificationMapper findById(Long id); + + List fetchAllNotifications(); + + void delete(Long id); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperReadPlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperReadPlatformServiceJpaRepositoryImpl.java new file mode 100644 index 00000000000..bc3d7ff451e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperReadPlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.notification.service; + +import java.util.List; + +import org.apache.fineract.notification.domain.NotificationMapper; +import org.apache.fineract.notification.domain.NotificationMapperRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class NotificationMapperReadPlatformServiceJpaRepositoryImpl implements NotificationMapperReadPlatformService { + + private final NotificationMapperRepository notificationMapperRepository; + + @Autowired + public NotificationMapperReadPlatformServiceJpaRepositoryImpl( + NotificationMapperRepository notificationMapperRepository) { + + this.notificationMapperRepository = notificationMapperRepository; + } + + @Override + public NotificationMapper findById(Long id) { + return this.notificationMapperRepository.findOne(id); + } + + @Override + public List fetchAllNotifications() { + return this.notificationMapperRepository.findAll(); + } + + @Override + public void delete(Long id) { + this.notificationMapperRepository.delete(id); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperWritePlatformService.java new file mode 100644 index 00000000000..dfa72c59674 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperWritePlatformService.java @@ -0,0 +1,26 @@ +/** + * 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.notification.service; + +import org.apache.fineract.notification.domain.NotificationMapper; + +public interface NotificationMapperWritePlatformService { + + Long create(NotificationMapper notificationMapper); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 00000000000..556ac27ed12 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationMapperWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,44 @@ +/** + * 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.notification.service; + +import org.apache.fineract.notification.domain.NotificationMapper; +import org.apache.fineract.notification.domain.NotificationMapperRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class NotificationMapperWritePlatformServiceJpaRepositoryImpl implements NotificationMapperWritePlatformService { + + private final NotificationMapperRepository notificationMapperRepository; + + @Autowired + public NotificationMapperWritePlatformServiceJpaRepositoryImpl( + NotificationMapperRepository notificationMapperRepository) { + + this.notificationMapperRepository = notificationMapperRepository; + } + + @Override + public Long create(NotificationMapper notificationMapper) { + this.notificationMapperRepository.save(notificationMapper); + return notificationMapper.getId(); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformService.java new file mode 100644 index 00000000000..9d214833a99 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformService.java @@ -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. + */ +package org.apache.fineract.notification.service; + +import org.apache.fineract.notification.data.NotificationData; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.SearchParameters; + +public interface NotificationReadPlatformService { + + public boolean hasUnreadNotifications(Long id); + + public Page getAllUnreadNotifications(SearchParameters searchParameters); + + public Page getAllNotifications(SearchParameters searchParameters); + + public void updateNotificationReadStatus(); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformServiceImpl.java new file mode 100644 index 00000000000..eab45dadc16 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationReadPlatformServiceImpl.java @@ -0,0 +1,203 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.notification.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; + +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.core.service.SearchParameters; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.notification.cache.CacheNotificationResponseHeader; +import org.apache.fineract.notification.data.NotificationData; +import org.apache.fineract.notification.data.NotificationMapperData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class NotificationReadPlatformServiceImpl implements NotificationReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final PlatformSecurityContext context; + private final PaginationHelper paginationHelper = new PaginationHelper<>(); + private final NotificationDataRow notificationDataRow = new NotificationDataRow(); + private final NotificationMapperRow notificationMapperRow = new NotificationMapperRow(); + private HashMap notificationResponseHeaderCache = new HashMap<>(); + + @Autowired + public NotificationReadPlatformServiceImpl(final RoutingDataSource dataSource, PlatformSecurityContext context) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.context = context; + } + + @Override + public boolean hasUnreadNotifications(Long appUserId) { + Long now = System.currentTimeMillis()/1000L; + if (this.notificationResponseHeaderCache.containsKey(appUserId)) { + Long lastFetch = this.notificationResponseHeaderCache.get(appUserId).getLastFetch(); + if ((now - lastFetch) > 1) { + return createUpdateCacheValue(appUserId, now); + } + return this.notificationResponseHeaderCache.get(appUserId).hasNotifications(); + } + return createUpdateCacheValue(appUserId, now); + } + + private boolean createUpdateCacheValue(Long appUserId, Long now) { + boolean hasNotifications; + CacheNotificationResponseHeader cacheNotificationResponseHeader; + hasNotifications = checkForUnreadNotifications(appUserId); + cacheNotificationResponseHeader = new CacheNotificationResponseHeader(hasNotifications, now); + notificationResponseHeaderCache.put(appUserId, cacheNotificationResponseHeader); + return hasNotifications; + } + + private boolean checkForUnreadNotifications(Long appUserId) { + String sql = "SELECT id, notification_id as notificationId, user_id as userId, is_read as isRead, created_at " + + "as createdAt FROM notification_mapper WHERE user_id = ? AND is_read = false"; + List notificationMappers = this.jdbcTemplate.query( + sql, + notificationMapperRow, + appUserId); + return notificationMappers.size() > 0; + } + + @Override + public Page getAllUnreadNotifications(SearchParameters searchParameters) { + final Long appUserId = context.authenticatedUser().getId(); + String sql = "SELECT SQL_CALC_FOUND_ROWS ng.id as id, nm.user_id as userId, ng.object_type as objectType, " + + "ng.object_identifier as objectId, ng.actor as actor, ng.action action, ng.notification_content " + + "as content, ng.is_system_generated as isSystemGenerated, nm.created_at as createdAt " + + "FROM notification_mapper nm INNER JOIN notification_generator ng ON nm.notification_id = ng.id " + + "WHERE nm.user_id = ? AND nm.is_read = false order by nm.created_at desc"; + + return getNotificationDataPage(searchParameters, appUserId, sql); + } + + @Override + public Page getAllNotifications(SearchParameters searchParameters) { + final Long appUserId = context.authenticatedUser().getId(); + String sql = "SELECT SQL_CALC_FOUND_ROWS ng.id as id, nm.user_id as userId, ng.object_type as objectType, " + + "ng.object_identifier as objectId, ng.actor as actor, ng.action action, ng.notification_content " + + "as content, ng.is_system_generated as isSystemGenerated, nm.created_at as createdAt " + + "FROM notification_mapper nm INNER JOIN notification_generator ng ON nm.notification_id = ng.id " + + "WHERE nm.user_id = ? order by nm.created_at desc"; + + return getNotificationDataPage(searchParameters, appUserId, sql); + } + + private Page getNotificationDataPage(SearchParameters searchParameters, Long appUserId, String sql) { + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append(sql); + if (searchParameters.isOrderByRequested()) { + sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); + if (searchParameters.isSortOrderProvided()) { + sqlBuilder.append(' ').append(searchParameters.getSortOrder()); + } + } + + if (searchParameters.isLimited()) { + sqlBuilder.append(" limit ").append(searchParameters.getLimit()); + if (searchParameters.isOffset()) { + sqlBuilder.append(" offset ").append(searchParameters.getOffset()); + } + } + + final String sqlCountRows = "SELECT FOUND_ROWS()"; + Object[] params = new Object[]{appUserId}; + return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), + params, this.notificationDataRow); + } + + @Override + public void updateNotificationReadStatus() { + final Long appUserId = context.authenticatedUser().getId(); + String sql = "UPDATE notification_mapper SET is_read = true WHERE is_read = false and user_id = ?"; + this.jdbcTemplate.update(sql, appUserId); + + } + + private static final class NotificationMapperRow implements RowMapper { + + @Override + public NotificationMapperData mapRow(ResultSet rs, int rowNum) throws SQLException { + NotificationMapperData notificationMapperData = new NotificationMapperData(); + + final Long id = rs.getLong("id"); + notificationMapperData.setId(id); + + final Long notificationId = rs.getLong("notificationId"); + notificationMapperData.setNotificationId(notificationId); + + final Long userId = rs.getLong("userId"); + notificationMapperData.setUserId(userId); + + final boolean isRead = rs.getBoolean("isRead"); + notificationMapperData.setRead(isRead); + + final String createdAt = rs.getString("createdAt"); + notificationMapperData.setCreatedAt(createdAt); + + return notificationMapperData; + } + + } + + private static final class NotificationDataRow implements RowMapper { + + @Override + public NotificationData mapRow(ResultSet rs, int rowNum) throws SQLException { + NotificationData notificationData = new NotificationData(); + + final Long id = rs.getLong("id"); + notificationData.setId(id); + + final String objectType = rs.getString("objectType"); + notificationData.setObjectType(objectType); + + final Long objectId = rs.getLong("objectId"); + notificationData.entifier(objectId); + + final Long actorId = rs.getLong("actor"); + notificationData.setActorId(actorId); + + final String action = rs.getString("action"); + notificationData.setAction(action); + + final String content = rs.getString("content"); + notificationData.setContent(content); + + final boolean isSystemGenerated = rs.getBoolean("isSystemGenerated"); + notificationData.setSystemGenerated(isSystemGenerated); + + final String createdAt = rs.getString("createdAt"); + notificationData.setCreatedAt(createdAt); + + return notificationData; + } + + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationWritePlatformService.java new file mode 100644 index 00000000000..2570d9fbc36 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationWritePlatformService.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.notification.service; + +import java.util.List; + +public interface NotificationWritePlatformService { + + public Long notify(Long userId, String objectType, Long objectId, String action, + Long actorId, String notificationContent, boolean isSystemGenerated); + + public Long notify(List userIds, String objectType, Long objectId, String action, + Long actorId, String notificationContent, boolean isSystemGenerated); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationWritePlatformServiceImpl.java new file mode 100644 index 00000000000..8d654707792 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationWritePlatformServiceImpl.java @@ -0,0 +1,126 @@ +/** + * 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.notification.service; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.apache.fineract.notification.domain.Notification; +import org.apache.fineract.notification.domain.NotificationMapper; +import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.domain.AppUserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class NotificationWritePlatformServiceImpl implements NotificationWritePlatformService { + + private final NotificationGeneratorWritePlatformService notificationGeneratorWritePlatformService; + private final NotificationGeneratorReadPlatformService notificationGeneratorReadPlatformService; + private final AppUserRepository appUserRepository; + private final NotificationMapperWritePlatformService notificationMapperWritePlatformService; + + @Autowired + public NotificationWritePlatformServiceImpl( + final NotificationGeneratorWritePlatformService notificationGeneratorWritePlatformService, + final NotificationGeneratorReadPlatformService notificationGeneratorReadPlatformService, + final AppUserRepository appUserRepository, + final NotificationMapperWritePlatformService notificationMapperWritePlatformService) { + + this.notificationGeneratorWritePlatformService = notificationGeneratorWritePlatformService; + this.notificationGeneratorReadPlatformService = notificationGeneratorReadPlatformService; + this.appUserRepository = appUserRepository; + this.notificationMapperWritePlatformService = notificationMapperWritePlatformService; + } + + @Override + public Long notify(Long userId, String objectType, Long objectIdentifier, String action, Long actorId, + String notificationContent, boolean isSystemGenerated) { + + Long generatedNotificationId = insertIntoNotificationGenerator(objectType, objectIdentifier, action, + actorId, notificationContent, isSystemGenerated); + insertIntoNotificationMapper(userId, generatedNotificationId); + return generatedNotificationId; + } + + private Long insertIntoNotificationMapper(Long userId, Long generatedNotificationId) { + AppUser appUser = this.appUserRepository.findOne(userId); + NotificationMapper notificationMapper = new NotificationMapper( + this.notificationGeneratorReadPlatformService.findById(generatedNotificationId), + appUser, + false, + getCurrentDateTime() + ); + + this.notificationMapperWritePlatformService.create(notificationMapper); + return notificationMapper.getId(); + } + + private Long insertIntoNotificationGenerator(String objectType, Long objectIdentifier, String action, + Long actorId, String notificationContent, boolean isSystemGenerated) { + + Notification notification = new Notification( + objectType, + objectIdentifier, + action, + actorId, + isSystemGenerated, + notificationContent, + getCurrentDateTime() + ); + + return this.notificationGeneratorWritePlatformService.create(notification); + } + + @Override + public Long notify(List userIds, String objectType, Long objectId, String action, Long actorId, + String notificationContent, boolean isSystemGenerated) { + Long generatedNotificationId = insertIntoNotificationGenerator(objectType, objectId, action, + actorId, notificationContent, isSystemGenerated); + + insertIntoNotificationMapper(userIds, generatedNotificationId); + return generatedNotificationId; + + } + + private List insertIntoNotificationMapper(List userIds, Long generatedNotificationId) { + List mappedIds = new ArrayList<>(); + for (Long userId : userIds) { + AppUser appUser = this.appUserRepository.findOne(userId); + NotificationMapper notificationMapper = new NotificationMapper( + this.notificationGeneratorReadPlatformService.findById(generatedNotificationId), + appUser, + false, + getCurrentDateTime() + ); + this.notificationMapperWritePlatformService.create(notificationMapper); + mappedIds.add(notificationMapper.getId()); + } + return mappedIds; + } + + private String getCurrentDateTime() { + Date date = new Date(); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return formatter.format(date); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicReadPlatformService.java new file mode 100644 index 00000000000..e6c9ea5f827 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicReadPlatformService.java @@ -0,0 +1,32 @@ +/** + * 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.notification.service; + +import java.util.Collection; +import org.apache.fineract.notification.data.TopicData; + +public interface TopicReadPlatformService { + + Collection getAllTopics(); + + Collection getAllEnabledTopics(); + + TopicData findById(Long id); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicReadPlatformServiceImpl.java new file mode 100644 index 00000000000..948f9310d1e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicReadPlatformServiceImpl.java @@ -0,0 +1,98 @@ +/** + * 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.notification.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.notification.data.TopicData; +import org.apache.fineract.notification.exception.TopicNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class TopicReadPlatformServiceImpl implements TopicReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public TopicReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class TopicMapper implements RowMapper { + + private final String schema; + + public TopicMapper() { + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("t.id as id, t.title as title, t.enabled as enabled, "); + sqlBuilder.append("t.entity_id as entityId, t.entity_type as entityType, "); + sqlBuilder.append("t.member_type as memberType, from topic t"); + this.schema = sqlBuilder.toString(); + } + + public String schema() { + return this.schema; + } + + @Override + public TopicData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final String title = rs.getString("title"); + final Boolean enabled = rs.getBoolean("enabled"); + final Long entityId = rs.getLong("entityId"); + final String entityType = rs.getString("entityType"); + final String memberType = rs.getString("memberType"); + + return new TopicData(id, title, enabled, entityId, entityType, memberType); + } + + } + + @Override + public Collection getAllTopics() { + final TopicMapper tm = new TopicMapper(); + String sql = "select " + tm.schema(); + return this.jdbcTemplate.query(sql, tm, new Object[] {}); + } + + @Override + public Collection getAllEnabledTopics() { + final TopicMapper tm = new TopicMapper(); + final String sql = "select " + tm.schema() + " where t.is_active = ?"; + return this.jdbcTemplate.query(sql, tm, new Object[] { true }); + } + + @Override + public TopicData findById(Long topicId) { + try { + final TopicMapper tm = new TopicMapper(); + final String sql = "select " + tm.schema() + " where t.id = ?"; + return this.jdbcTemplate.queryForObject(sql, tm, new Object[] { topicId }); + } catch (final EmptyResultDataAccessException e) { + throw new TopicNotFoundException(topicId); + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberReadPlatformService.java new file mode 100644 index 00000000000..834b5747965 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberReadPlatformService.java @@ -0,0 +1,29 @@ +/** + * 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.notification.service; + +import java.util.Collection; + +import org.apache.fineract.notification.data.TopicSubscriberData; + +public interface TopicSubscriberReadPlatformService { + + Collection getSubscribers(Long entityId, String entityType, String memberType); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberReadPlatformServiceImpl.java new file mode 100644 index 00000000000..4d597a015f7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberReadPlatformServiceImpl.java @@ -0,0 +1,78 @@ +/** + * 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.notification.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.notification.data.TopicSubscriberData; +import org.joda.time.LocalDate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class TopicSubscriberReadPlatformServiceImpl implements TopicSubscriberReadPlatformService{ + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public TopicSubscriberReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class TopicSubscriberMapper implements RowMapper { + + private final String schema; + + public TopicSubscriberMapper() { + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("ts.id as id, ts.topic_id as topicId, ts.user_id as userId, "); + sqlBuilder.append("ts.subscription_date as subscriptionDate from topic_subscriber ts "); + sqlBuilder.append("WHERE ts.topic_id = ( SELECT id from topic WHERE entity_id = ? "); + sqlBuilder.append("AND entity_type = ? AND member_type = ? )"); + this.schema = sqlBuilder.toString(); + } + + public String schema() { + return this.schema; + } + + @Override + public TopicSubscriberData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final Long id = rs.getLong("id"); + final Long topicId = rs.getLong("topicId"); + final Long userId = rs.getLong("userId"); + final LocalDate subscriptionDate = JdbcSupport.getLocalDate(rs, "subscriptionDate"); + + return new TopicSubscriberData(id, topicId, userId, subscriptionDate); + } + + } + + @Override + public Collection getSubscribers(Long entityId, String entityType, String memberType) { + final TopicSubscriberMapper tsm = new TopicSubscriberMapper(); + String sql = "SELECT " + tsm.schema(); + return this.jdbcTemplate.query(sql, tsm, new Object[] { entityId, entityType, memberType }); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberWritePlatformService.java new file mode 100644 index 00000000000..95cbd9fc859 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberWritePlatformService.java @@ -0,0 +1,27 @@ +/** + * 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.notification.service; + +import org.apache.fineract.notification.domain.TopicSubscriber; + +public interface TopicSubscriberWritePlatformService { + + Long create(TopicSubscriber topicSubscriber); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 00000000000..541fd1ad4f9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicSubscriberWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,42 @@ +/** + * 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.notification.service; + +import org.apache.fineract.notification.domain.TopicSubscriber; +import org.apache.fineract.notification.domain.TopicSubscriberRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class TopicSubscriberWritePlatformServiceJpaRepositoryImpl implements TopicSubscriberWritePlatformService { + +private final TopicSubscriberRepository topicSubscriberRepository; + + @Autowired + public TopicSubscriberWritePlatformServiceJpaRepositoryImpl(TopicSubscriberRepository topicSubscriberRepository) { + this.topicSubscriberRepository = topicSubscriberRepository; + } + + @Override + public Long create(TopicSubscriber topicSubscriber) { + topicSubscriberRepository.save(topicSubscriber); + return topicSubscriber.getId(); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicWritePlatformService.java new file mode 100644 index 00000000000..d48206319df --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicWritePlatformService.java @@ -0,0 +1,27 @@ +/** + * 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.notification.service; + +import org.apache.fineract.notification.domain.Topic; + +public interface TopicWritePlatformService { + + Long create(Topic topic); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 00000000000..63cb911af2d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/TopicWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,42 @@ +/** + * 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.notification.service; + +import org.apache.fineract.notification.domain.Topic; +import org.apache.fineract.notification.domain.TopicRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class TopicWritePlatformServiceJpaRepositoryImpl implements TopicWritePlatformService { + + private final TopicRepository topicRepository; + + @Autowired + public TopicWritePlatformServiceJpaRepositoryImpl(TopicRepository topicRepository) { + this.topicRepository = topicRepository; + } + + @Override + public Long create(Topic topic) { + topicRepository.save(topic); + return topic.getId(); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/office/domain/Office.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/office/domain/Office.java index c64e856ce63..e4f13e45d6c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/office/domain/Office.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/office/domain/Office.java @@ -205,7 +205,11 @@ public String getHierarchy() { return this.hierarchy; } - public boolean hasParentOf(final Office office) { + public Office getParent() { + return this.parent; + } + + public boolean hasParentOf(final Office office) { boolean isParent = false; if (this.parent != null) { isParent = this.parent.equals(office); diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java index ba12c4131c0..4a701cb17ae 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeWritePlatformServiceJpaRepositoryImpl.java @@ -18,6 +18,8 @@ */ package org.apache.fineract.organisation.office.service; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import javax.persistence.PersistenceException; @@ -29,6 +31,8 @@ import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.notification.domain.Topic; +import org.apache.fineract.notification.domain.TopicRepository; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; @@ -40,6 +44,8 @@ import org.apache.fineract.organisation.office.serialization.OfficeCommandFromApiJsonDeserializer; import org.apache.fineract.organisation.office.serialization.OfficeTransactionCommandFromApiJsonDeserializer; import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.domain.Role; +import org.apache.fineract.useradministration.domain.RoleRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -60,19 +66,23 @@ public class OfficeWritePlatformServiceJpaRepositoryImpl implements OfficeWriteP private final OfficeRepositoryWrapper officeRepositoryWrapper; private final OfficeTransactionRepository officeTransactionRepository; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository; + private final RoleRepository roleRepository; + private final TopicRepository topicRepository; @Autowired public OfficeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final OfficeCommandFromApiJsonDeserializer fromApiJsonDeserializer, final OfficeTransactionCommandFromApiJsonDeserializer moneyTransferCommandFromApiJsonDeserializer, final OfficeRepositoryWrapper officeRepositoryWrapper, final OfficeTransactionRepository officeMonetaryTransferRepository, - final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository) { + final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, RoleRepository roleRepository, TopicRepository topicRepository) { this.context = context; this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.moneyTransferCommandFromApiJsonDeserializer = moneyTransferCommandFromApiJsonDeserializer; this.officeRepositoryWrapper = officeRepositoryWrapper; this.officeTransactionRepository = officeMonetaryTransferRepository; this.applicationCurrencyRepository = applicationCurrencyRepository; + this.roleRepository = roleRepository; + this.topicRepository = topicRepository; } @Transactional @@ -101,6 +111,23 @@ public CommandProcessingResult createOffice(final JsonCommand command) { office.generateHierarchy(); this.officeRepositoryWrapper.save(office); + + Long entityId = office.getId(); + String entityType = ""; + if (office.getParent() == null) { + entityType = "OFFICE"; + } else { + entityType = "BRANCH"; + } + + List allRoles = roleRepository.findAll(); + for(Role curRole : allRoles) { + String memberType = curRole.getName().toUpperCase(); + String title = curRole.getName() + " of " + office.getName(); + Topic newTopic = new Topic(title, true, entityId, entityType, memberType); + topicRepository.save(newTopic); + } + return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java index 0556f9c53fe..3937e72c67a 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java @@ -33,9 +33,12 @@ public static enum BUSINESS_EVENTS { "loan_charge_payment"), LOAN_INITIATE_TRANSFER("loan_initiate_transfer"), LOAN_ACCEPT_TRANSFER("loan_accept_transfer"), LOAN_WITHDRAW_TRANSFER( "loan_withdraw_transfer"), LOAN_REJECT_TRANSFER("loan_reject_transfer"), LOAN_REASSIGN_OFFICER("loan_reassign_officer"), LOAN_REMOVE_OFFICER( "loan_remove_officer"), LOAN_APPLY_OVERDUE_CHARGE("loan_apply_overdue_charge"), LOAN_INTEREST_RECALCULATION( - "loan_interest_recalculation"), LOAN_REFUND("loan_refund"), LOAN_FORECLOSURE("loan_foreclosure"), SAVINGS_ACTIVATE( - "savings_activated"), SAVINGS_REJECT("savings_reject"), SAVINGS_DEPOSIT("savings_deposit"), SAVINGS_WITHDRAWAL( - "savings_withdrawal"), CLIENTS_ACTIVATE("clients_activate"), CLIENTS_REJECT("clients_reject"); + "loan_interest_recalculation"), LOAN_REFUND("loan_refund"), LOAN_FORECLOSURE("loan_foreclosure"), LOAN_CREATE("loan_create"), LOAN_PRODUCT_CREATE( + "loan_product_create"), SAVINGS_ACTIVATE("savings_activated"), SAVINGS_REJECT("savings_reject"), SAVINGS_POST_INTEREST("savings_post_interest"), SAVINGS_DEPOSIT( + "savings_deposit"), SAVINGS_CLOSE("savings_close"), SAVINGS_WITHDRAWAL("savings_withdrawal"), SAVINGS_APPROVE("savings_approve"), SAVINGS_CREATE( + "savings_create"), CLIENTS_ACTIVATE("clients_activate"), SHARE_ACCOUNT_CREATE("share_account_create"), CLIENTS_REJECT("clients_reject"), CLIENTS_CREATE("clients_create"), CENTERS_CREATE( + "centers_create"), GROUPS_CREATE("groups_create"), SHARE_PRODUCT_DIVIDENDS_CREATE("share_product_dividends_create"), FIXED_DEPOSIT_ACCOUNT_CREATE( + "fixed_deposit_account_create"), SHARE_ACCOUNT_APPROVE("share_account_approve"), RECURRING_DEPOSIT_ACCOUNT_CREATE("recurring_deposit_account_create"); private final String value; @@ -61,7 +64,9 @@ public String getValue() { public static enum BUSINESS_ENTITY { LOAN("loan"), LOAN_TRANSACTION("loan_transaction"), LOAN_CHARGE("loan_charge"), LOAN_ADJUSTED_TRANSACTION( - "loan_adjusted_transaction"), SAVING("saving"), CLIENT("client"), SAVINGS_TRANSACTION("Savings Transaction"); + "loan_adjusted_transaction"), SAVING("saving"), CLIENT("client"), SAVINGS_TRANSACTION("Savings Transaction"), + GROUP("group"), SHARE_ACCOUNT("share_account"), SHARE_PRODUCT("share_product"), DEPOSIT_ACCOUNT("deposit_account"), + LOAN_PRODUCT("loan_product"); private final String value; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java index a3837c06496..e2fe0400f51 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java @@ -52,6 +52,10 @@ import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; import org.apache.fineract.portfolio.client.service.LoanStatusMapper; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.group.api.GroupingTypesApiConstants; import org.apache.fineract.portfolio.group.domain.*; import org.apache.fineract.portfolio.group.exception.*; @@ -95,6 +99,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository; private final AccountNumberGenerator accountNumberGenerator; private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService; + private final BusinessEventNotifierService businessEventNotifierService; @Autowired public GroupingTypesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -106,7 +111,8 @@ public GroupingTypesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurity final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService, final LoanRepositoryWrapper loanRepositoryWrapper, final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, final AccountNumberGenerator accountNumberGenerator, - final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService) { + final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, + final BusinessEventNotifierService businessEventNotifierService) { this.context = context; this.groupRepository = groupRepository; this.clientRepositoryWrapper = clientRepositoryWrapper; @@ -124,6 +130,7 @@ public GroupingTypesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurity this.accountNumberFormatRepository = accountNumberFormatRepository; this.accountNumberGenerator = accountNumberGenerator; this.entityDatatableChecksWritePlatformService = entityDatatableChecksWritePlatformService; + this.businessEventNotifierService = businessEventNotifierService; } private CommandProcessingResult createGroupingType(final JsonCommand command, final GroupTypes groupingType, final Long centerId) { @@ -263,7 +270,13 @@ public CommandProcessingResult createCenter(final JsonCommand command) { this.fromApiJsonDeserializer.validateForCreateCenter(command); final Long centerId = null; - return createGroupingType(command, GroupTypes.CENTER, centerId); + + CommandProcessingResult commandProcessingResult = createGroupingType(command, GroupTypes.CENTER, centerId); + + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.CENTERS_CREATE, + constructEntityMap(BUSINESS_ENTITY.GROUP, commandProcessingResult)); + + return commandProcessingResult; } @Transactional @@ -276,7 +289,12 @@ public CommandProcessingResult createGroup(final Long centerId, final JsonComman this.fromApiJsonDeserializer.validateForCreateGroup(command); } - return createGroupingType(command, GroupTypes.GROUP, centerId); + CommandProcessingResult commandProcessingResult = createGroupingType(command, GroupTypes.GROUP, centerId); + + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BusinessEventNotificationConstants.BUSINESS_EVENTS.GROUPS_CREATE, + constructEntityMap(BUSINESS_ENTITY.GROUP, commandProcessingResult)); + + return commandProcessingResult; } @Transactional @@ -961,4 +979,10 @@ else if (ceneterCalendar != null && groupCalendar != null) { } } } + + private Map constructEntityMap(final BUSINESS_ENTITY entityEvent, Object entity) { + Map map = new HashMap<>(1); + map.put(entityEvent, entity); + return map; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 8757e013a49..158e503c139 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -394,7 +394,9 @@ public CommandProcessingResult submitApplication(final JsonCommand command) { EntityTables.LOAN.getName(), StatusEnum.CREATE.getCode().longValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), newLoanApplication.productId()); - + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_CREATE, + constructEntityMap(BUSINESS_ENTITY.LOAN, newLoanApplication)); + return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(newLoanApplication.getId()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java index 424b3ac0c5c..606711102e5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java @@ -19,6 +19,7 @@ package org.apache.fineract.portfolio.loanproduct.service; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,6 +36,10 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate; import org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepositoryWrapper; import org.apache.fineract.portfolio.fund.domain.Fund; @@ -78,6 +83,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro private final FineractEntityAccessUtil fineractEntityAccessUtil; private final FloatingRateRepositoryWrapper floatingRateRepository; private final LoanRepositoryWrapper loanRepositoryWrapper; + private final BusinessEventNotifierService businessEventNotifierService; @Autowired public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -88,7 +94,8 @@ public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityCo final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, final FineractEntityAccessUtil fineractEntityAccessUtil, final FloatingRateRepositoryWrapper floatingRateRepository, - final LoanRepositoryWrapper loanRepositoryWrapper) { + final LoanRepositoryWrapper loanRepositoryWrapper, + final BusinessEventNotifierService businessEventNotifierService) { this.context = context; this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.loanProductRepository = loanProductRepository; @@ -100,6 +107,7 @@ public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityCo this.fineractEntityAccessUtil = fineractEntityAccessUtil; this.floatingRateRepository = floatingRateRepository; this.loanRepositoryWrapper = loanRepositoryWrapper; + this.businessEventNotifierService = businessEventNotifierService; } @Transactional @@ -140,6 +148,9 @@ public CommandProcessingResult createLoanProduct(final JsonCommand command) { FineractEntityAccessType.OFFICE_ACCESS_TO_LOAN_PRODUCTS, loanproduct.getId()); + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_PRODUCT_CREATE, + constructEntityMap(BUSINESS_ENTITY.LOAN_PRODUCT, loanproduct)); + return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(loanproduct.getId()) // @@ -332,4 +343,10 @@ private void validateInputDates(final JsonCommand command) { private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) { logger.error(dve.getMessage(), dve); } + + private Map constructEntityMap(final BusinessEventNotificationConstants.BUSINESS_ENTITY entityEvent, Object entity) { + Map map = new HashMap<>(1); + map.put(entityEvent, entity); + return map; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java index 1da6e8f4741..751ac3597b7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl.java @@ -25,6 +25,7 @@ import java.math.MathContext; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -65,7 +66,11 @@ import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; import org.apache.fineract.portfolio.client.exception.ClientNotActiveException; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; +import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.group.domain.Group; import org.apache.fineract.portfolio.group.domain.GroupRepository; import org.apache.fineract.portfolio.group.exception.CenterNotActiveException; @@ -122,6 +127,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl private final CalendarInstanceRepository calendarInstanceRepository; private final ConfigurationDomainService configurationDomainService; private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository; + private final BusinessEventNotifierService businessEventNotifierService; @Autowired public DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -136,7 +142,8 @@ public DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl(final Plat final RecurringDepositAccountRepository recurringDepositAccountRepository, final AccountAssociationsRepository accountAssociationsRepository, final FromJsonHelper fromJsonHelper, final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService, - final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository) { + final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, + final BusinessEventNotifierService businessEventNotifierService) { this.context = context; this.savingAccountRepository = savingAccountRepository; this.depositAccountAssembler = depositAccountAssembler; @@ -156,6 +163,7 @@ public DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl(final Plat this.calendarInstanceRepository = calendarInstanceRepository; this.configurationDomainService = configurationDomainService; this.accountNumberFormatRepository = accountNumberFormatRepository; + this.businessEventNotifierService = businessEventNotifierService; } /* @@ -226,6 +234,8 @@ public CommandProcessingResult submitFDApplication(final JsonCommand command) { } final Long savingsId = account.getId(); + this.businessEventNotifierService.notifyBusinessEventWasExecuted( BUSINESS_EVENTS.FIXED_DEPOSIT_ACCOUNT_CREATE, + constructEntityMap(BUSINESS_ENTITY.DEPOSIT_ACCOUNT, account)); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // @@ -284,6 +294,8 @@ public CommandProcessingResult submitRDApplication(final JsonCommand command) { financialYearBeginningMonth); account.validateApplicableInterestRate(); this.savingAccountRepository.save(account); + this.businessEventNotifierService.notifyBusinessEventWasExecuted( BUSINESS_EVENTS.RECURRING_DEPOSIT_ACCOUNT_CREATE, + constructEntityMap(BUSINESS_ENTITY.DEPOSIT_ACCOUNT, account)); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // @@ -769,4 +781,10 @@ private void checkClientOrGroupActive(final SavingsAccount account) { } } } + + private Map constructEntityMap(final BUSINESS_ENTITY entityEvent, Object entity) { + Map map = new HashMap<>(1); + map.put(entityEvent, entity); + return map; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index f9e52b2876c..6694c817411 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -382,6 +382,9 @@ public CommandProcessingResult postInterest(final JsonCommand command) { } postInterest(account,postInterestAs,transactionDate); + + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.SAVINGS_POST_INTEREST, + constructEntityMap(BUSINESS_ENTITY.SAVING, account)); return new CommandProcessingResultBuilder() // .withEntityId(savingsId) // .withOfficeId(account.officeId()) // @@ -682,6 +685,8 @@ public CommandProcessingResult close(final Long savingsId, final JsonCommand com } + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.SAVINGS_CLOSE, + constructEntityMap(BUSINESS_ENTITY.SAVING, account)); // disable all standing orders linked to the savings account this.disableStandingInstructionsLinkedToClosedSavings(account); return new CommandProcessingResultBuilder() // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java index 45056ca2a47..d0bd80bcc84 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java @@ -181,6 +181,9 @@ public CommandProcessingResult submitApplication(final JsonCommand command) { EntityTables.SAVING.getName(), StatusEnum.CREATE.getCode().longValue(), EntityTables.SAVING.getForeignKeyColumnNameOnDatatable(), account.productId()); + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.SAVINGS_CREATE, + constructEntityMap(BUSINESS_ENTITY.SAVING, account)); + return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(savingsId) // @@ -362,6 +365,9 @@ public CommandProcessingResult approveApplication(final Long savingsId, final Js } } + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.SAVINGS_APPROVE, + constructEntityMap(BUSINESS_ENTITY.SAVING, savingsAccount)); + return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(savingsId) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java index 7a2a4223562..d322264ccc7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/service/ShareAccountWritePlatformServiceJpaRepositoryImpl.java @@ -44,6 +44,10 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants; import org.apache.fineract.portfolio.client.domain.AccountNumberGenerator; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.note.domain.Note; import org.apache.fineract.portfolio.note.domain.NoteRepository; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountTransactionEnumData; @@ -76,6 +80,8 @@ public class ShareAccountWritePlatformServiceJpaRepositoryImpl implements ShareA private final NoteRepository noteRepository; + private final BusinessEventNotifierService businessEventNotifierService; + @Autowired public ShareAccountWritePlatformServiceJpaRepositoryImpl(final ShareAccountDataSerializer accountDataSerializer, final ShareAccountRepositoryWrapper shareAccountRepository, @@ -83,7 +89,8 @@ public ShareAccountWritePlatformServiceJpaRepositoryImpl(final ShareAccountDataS final AccountNumberGenerator accountNumberGenerator, final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, final JournalEntryWritePlatformService journalEntryWritePlatformService, - final NoteRepository noteRepository) { + final NoteRepository noteRepository, + final BusinessEventNotifierService businessEventNotifierService) { this.accountDataSerializer = accountDataSerializer; this.shareAccountRepository = shareAccountRepository; this.shareProductRepository = shareProductRepository ; @@ -91,6 +98,7 @@ public ShareAccountWritePlatformServiceJpaRepositoryImpl(final ShareAccountDataS this.accountNumberFormatRepository = accountNumberFormatRepository; this.journalEntryWritePlatformService = journalEntryWritePlatformService; this.noteRepository = noteRepository ; + this.businessEventNotifierService = businessEventNotifierService; } @Override @@ -101,6 +109,9 @@ public CommandProcessingResult createShareAccount(JsonCommand jsonCommand) { generateAccountNumber(account); journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, account.getPendingForApprovalSharePurchaseTransactions())); + + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.SHARE_ACCOUNT_CREATE, + constructEntityMap(BUSINESS_ENTITY.SHARE_ACCOUNT, account)); return new CommandProcessingResultBuilder() // .withCommandId(jsonCommand.commandId()) // .withEntityId(account.getId()) // @@ -273,6 +284,10 @@ public CommandProcessingResult approveShareAccount(Long accountId, JsonCommand j this.shareProductRepository.save(shareProduct); this.journalEntryWritePlatformService.createJournalEntriesForShares(populateJournalEntries(account, journalTransactions)); + + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.SHARE_ACCOUNT_APPROVE, + constructEntityMap(BUSINESS_ENTITY.SHARE_ACCOUNT, account)); + return new CommandProcessingResultBuilder() // .withCommandId(jsonCommand.commandId()) // .withEntityId(accountId) // @@ -516,4 +531,10 @@ private void handleDataIntegrityIssues(final JsonCommand command, final Throwabl throw new PlatformDataIntegrityException("error.msg.shareaccount.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); } + + private Map constructEntityMap(final BusinessEventNotificationConstants.BUSINESS_ENTITY entityEvent, Object entity) { + Map map = new HashMap<>(1); + map.put(entityEvent, entity); + return map; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java index 2a310a232b7..a7d0c18ef75 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java @@ -19,6 +19,7 @@ package org.apache.fineract.portfolio.shareproducts.service; import java.math.BigDecimal; +import java.util.HashMap; import java.util.Map; import javax.persistence.PersistenceException; @@ -30,6 +31,10 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY; +import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; +import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants; import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct; import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails; @@ -53,19 +58,22 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP private final ShareProductDividentPayOutDetailsRepositoryWrapper shareProductDividentPayOutDetailsRepository; private final ShareProductDividendAssembler shareProductDividendAssembler; private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; + private final BusinessEventNotifierService businessEventNotifierService; @Autowired public ShareProductWritePlatformServiceJpaRepositoryImpl(final ShareProductRepositoryWrapper repository, final ShareProductDataSerializer serializer, final FromJsonHelper fromApiJsonHelper, final ShareProductDividentPayOutDetailsRepositoryWrapper shareProductDividentPayOutDetailsRepositor, final ShareProductDividendAssembler shareProductDividendAssembler, - final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService) { + final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, + final BusinessEventNotifierService businessEventNotifierService) { this.repository = repository; this.serializer = serializer; this.fromApiJsonHelper = fromApiJsonHelper; this.shareProductDividentPayOutDetailsRepository = shareProductDividentPayOutDetailsRepositor; this.shareProductDividendAssembler = shareProductDividendAssembler; this.accountMappingWritePlatformService = accountMappingWritePlatformService; + this.businessEventNotifierService = businessEventNotifierService; } @Override @@ -140,6 +148,9 @@ public CommandProcessingResult createShareProductDividend(final Long productId, "No eligible shares for creating dividends"); } this.shareProductDividentPayOutDetailsRepository.save(dividendPayOutDetails); + this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.SHARE_PRODUCT_DIVIDENDS_CREATE, + constructEntityMap(BUSINESS_ENTITY.SHARE_PRODUCT, productId)); + return new CommandProcessingResultBuilder() // .withCommandId(jsonCommand.commandId()) // .withEntityId(productId) // @@ -203,5 +214,11 @@ private void handleDataIntegrityIssues(final JsonCommand command, final Throwabl throw new PlatformDataIntegrityException("error.msg.shareproduct.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); } + + private Map constructEntityMap(final BusinessEventNotificationConstants.BUSINESS_ENTITY entityEvent, Object entity) { + Map map = new HashMap<>(1); + map.put(entityEvent, entity); + return map; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/Role.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/Role.java index 19b3d98dff1..319c38880e3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/Role.java +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/Role.java @@ -129,7 +129,11 @@ public RoleData toData() { return new RoleData(getId(), this.name, this.description, this.disabled); } - public void disableRole() { + public String getName() { + return this.name; + } + + public void disableRole() { this.disabled = true; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/AppUserWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/AppUserWritePlatformServiceJpaRepositoryImpl.java index acda463e8c9..657003af177 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/AppUserWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/AppUserWritePlatformServiceJpaRepositoryImpl.java @@ -20,12 +20,12 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import javax.persistence.EntityExistsException; import javax.persistence.PersistenceException; import org.apache.commons.lang.exception.ExceptionUtils; @@ -39,6 +39,10 @@ import org.apache.fineract.infrastructure.core.service.PlatformEmailSendException; import org.apache.fineract.infrastructure.security.service.PlatformPasswordEncoder; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.notification.domain.Topic; +import org.apache.fineract.notification.domain.TopicRepository; +import org.apache.fineract.notification.domain.TopicSubscriber; +import org.apache.fineract.notification.domain.TopicSubscriberRepository; import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper; import org.apache.fineract.organisation.staff.domain.Staff; @@ -50,6 +54,7 @@ import org.apache.fineract.useradministration.domain.AppUserPreviousPassword; import org.apache.fineract.useradministration.domain.AppUserPreviousPasswordRepository; import org.apache.fineract.useradministration.domain.AppUserRepository; +import org.apache.fineract.useradministration.domain.Permission; import org.apache.fineract.useradministration.domain.Role; import org.apache.fineract.useradministration.domain.RoleRepository; import org.apache.fineract.useradministration.domain.UserDomainService; @@ -87,13 +92,16 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit private final AppUserPreviousPasswordRepository appUserPreviewPasswordRepository; private final StaffRepositoryWrapper staffRepositoryWrapper; private final ClientRepositoryWrapper clientRepositoryWrapper; + private final TopicRepository topicRepository; + private final TopicSubscriberRepository topicSubscriberRepository; @Autowired public AppUserWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final AppUserRepository appUserRepository, final UserDomainService userDomainService, final OfficeRepositoryWrapper officeRepositoryWrapper, final RoleRepository roleRepository, final PlatformPasswordEncoder platformPasswordEncoder, final UserDataValidator fromApiJsonDeserializer, final AppUserPreviousPasswordRepository appUserPreviewPasswordRepository, final StaffRepositoryWrapper staffRepositoryWrapper, - final ClientRepositoryWrapper clientRepositoryWrapper) { + final ClientRepositoryWrapper clientRepositoryWrapper, final TopicRepository topicRepository, + final TopicSubscriberRepository topicSubscriberRepository) { this.context = context; this.appUserRepository = appUserRepository; this.userDomainService = userDomainService; @@ -104,6 +112,8 @@ public AppUserWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContex this.appUserPreviewPasswordRepository = appUserPreviewPasswordRepository; this.staffRepositoryWrapper = staffRepositoryWrapper; this.clientRepositoryWrapper = clientRepositoryWrapper; + this.topicRepository = topicRepository; + this.topicSubscriberRepository = topicSubscriberRepository; } @Transactional @@ -150,6 +160,25 @@ public CommandProcessingResult createUser(final JsonCommand command) { final Boolean sendPasswordToEmail = command.booleanObjectValueOfParameterNamed("sendPasswordToEmail"); this.userDomainService.create(appUser, sendPasswordToEmail); + + List allTopics = topicRepository.findAll(); + List possibleTopics = new ArrayList<>(); + for (Topic curTopic : allTopics) { + if (curTopic.getEntityId() == appUser.getOffice().getId()) { + possibleTopics.add(curTopic); + } + } + if (!possibleTopics.isEmpty()) { + Set userRoles = appUser.getRoles(); + for (Role curRole : userRoles) { + for (Topic curTopic : possibleTopics) { + if(curRole.getName().compareToIgnoreCase(curTopic.getMemberType()) == 0) { + TopicSubscriber topicSubscriber = new TopicSubscriber(curTopic, appUser, new Date()); + topicSubscriberRepository.save(topicSubscriber); + } + } + } + } return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // diff --git a/fineract-provider/src/main/resources/META-INF/spring/appContext.xml b/fineract-provider/src/main/resources/META-INF/spring/appContext.xml index 9fe95d447e1..6b16d58e719 100644 --- a/fineract-provider/src/main/resources/META-INF/spring/appContext.xml +++ b/fineract-provider/src/main/resources/META-INF/spring/appContext.xml @@ -52,6 +52,7 @@ org.apache.fineract.portfolio.*, org.apache.fineract.useradministration.*, org.apache.fineract.mix.*, + org.apache.fineract.notification.*, org.apache.fineract.template.*, org.apache.fineract.template.service.*, org.apache.fineract.useradministration.*, @@ -80,6 +81,7 @@ + @@ -94,4 +96,5 @@ + diff --git a/fineract-provider/src/main/resources/META-INF/spring/notificationContext.xml b/fineract-provider/src/main/resources/META-INF/spring/notificationContext.xml new file mode 100644 index 00000000000..c33ba2bcccc --- /dev/null +++ b/fineract-provider/src/main/resources/META-INF/spring/notificationContext.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V328__notification_module_tables.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V328__notification_module_tables.sql new file mode 100644 index 00000000000..18bafce278a --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V328__notification_module_tables.sql @@ -0,0 +1,45 @@ +-- +-- 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. +-- + +CREATE TABLE IF NOT EXISTS `notification_generator` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `object_type` text, + `object_identifier` bigint(20) DEFAULT NULL, + `action` bigint(20), + `actor` text, + `is_system_generated` tinyint(1) DEFAULT '0', + `notification_content` text, + `created_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ; + +CREATE TABLE IF NOT EXISTS `notification_mapper` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `notification_id` bigint(20) DEFAULT NULL, + `user_id` bigint(20) DEFAULT NULL, + `is_read` tinyint(1) DEFAULT '0', + `created_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `notification_mapper` (`user_id`), + KEY `notification_id` (`notification_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ; + +ALTER TABLE `notification_mapper` + ADD CONSTRAINT `notification_mapper_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `m_appuser` (`id`), + ADD CONSTRAINT `notification_mapper_ibfk_3` FOREIGN KEY (`notification_id`) REFERENCES `notification_generator` (`id`); diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V331__topic_module_table.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V331__topic_module_table.sql new file mode 100644 index 00000000000..92562ef0543 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V331__topic_module_table.sql @@ -0,0 +1,45 @@ +-- +-- 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. +-- + +CREATE TABLE IF NOT EXISTS `topic` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `title` VARCHAR(100) NOT NULL, + `enabled` TINYINT(1) NULL, + `entity_id` BIGINT(20) NOT NULL, + `entity_type` TEXT NOT NULL, + `member_type` TEXT NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `title_UNIQUE` (`title` ASC) +)ENGINE = InnoDB; + +CREATE TABLE IF NOT EXISTS `topic_subscriber` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `topic_id` BIGINT(20) NOT NULL, + `user_id` BIGINT(20) NOT NULL, + `subscription_date` DATE NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_topic_has_m_appuser_topic` FOREIGN KEY (`topic_id`) REFERENCES `topic` (`id`), + CONSTRAINT `fk_topic_has_m_appuser_m_appuser1` FOREIGN KEY (`user_id`) REFERENCES `m_appuser` (`id`) +) ENGINE = InnoDB; + +INSERT INTO topic (enabled, entity_type, entity_id, member_type, title) SELECT true, 'OFFICE', o.id as entity_id, UPPER(r.name) as member_type, CONCAT(r.name, ' of ', o.name) as title FROM m_office o, m_role r WHERE o.parent_id IS NULL AND CONCAT(r.name, ' of ', o.name) NOT IN (SELECT title FROM topic); + +INSERT INTO topic (enabled, entity_type, entity_id, member_type, title) SELECT true, 'BRANCH', o.id as entity_id, UPPER(r.name) as member_type, CONCAT(r.name, ' of ', o.name) as title FROM m_office o, m_role r WHERE o.parent_id IS NOT NULL AND CONCAT(r.name, ' of ', o.name) NOT IN (SELECT title FROM topic); + +INSERT INTO topic_subscriber( user_id, topic_id, subscription_date ) SELECT u.id AS user_id, t.id AS topic_id, NOW() FROM topic t, m_appuser u, m_appuser_role ur, m_role r WHERE u.office_id = t.entity_id AND u.id = ur.appuser_id AND ur.role_id = r.id AND r.name = t.member_type AND NOT EXISTS (SELECT user_id, topic_id FROM topic_subscriber WHERE user_id = u.id AND topic_id = t.id); diff --git a/fineract-provider/src/test/java/org/apache/fineract/notification/Listener.java b/fineract-provider/src/test/java/org/apache/fineract/notification/Listener.java new file mode 100644 index 00000000000..0244a420a05 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/notification/Listener.java @@ -0,0 +1,36 @@ +/** + * 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.notification; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.springframework.jms.listener.SessionAwareMessageListener; + +public class Listener implements SessionAwareMessageListener { + + @Override + public void onMessage(Message message, Session session) throws JMSException { + TextMessage msg = (TextMessage) message; + System.out.println("Received: " + msg.getText()); + } + +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/notification/ListenerTest.java b/fineract-provider/src/test/java/org/apache/fineract/notification/ListenerTest.java new file mode 100644 index 00000000000..dc8f1992d0d --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/notification/ListenerTest.java @@ -0,0 +1,52 @@ +/** + * 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.notification; + +import javax.jms.JMSException; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ListenerTest { + + private Listener listener; + private Session session; + private TextMessage textMessageMock; + + @Before + public void setUp() { + listener = new Listener(); + session = Mockito.mock(Session.class); + textMessageMock = Mockito.mock(TextMessage.class); + } + + @Test + public void testListener() throws JMSException { + Mockito.when(textMessageMock.getText()).thenReturn("content"); + listener.onMessage(textMessageMock, session); + Mockito.verify(textMessageMock).getText(); + } + +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/notification/SenderTest.java b/fineract-provider/src/test/java/org/apache/fineract/notification/SenderTest.java new file mode 100644 index 00000000000..d6e43520b37 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/notification/SenderTest.java @@ -0,0 +1,77 @@ +/** + * 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.notification; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +import org.apache.fineract.notification.data.NotificationData; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.core.JmsTemplate; +import org.springframework.jms.core.MessageCreator; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.mockrunner.mock.jms.MockQueue; + +@ContextConfiguration(locations = { + "classpath:META-INF/testNotificationContext.xml", +}) +@RunWith(SpringJUnit4ClassRunner.class) +public class SenderTest { + + @Autowired + private JmsTemplate jmsTemplate; + + @Autowired + private MockQueue mockQueue; + + @Test + public void notificationCreation() { + String objectType = "CLIENT"; + Long objectIdentifier = 1L; + String action = "created"; + Long actorId = 1L; + String notificationContent = "A client was created"; + + NotificationData notificationData = new NotificationData( + objectType, + objectIdentifier, + action, + actorId, + notificationContent, + false, + null, + null, + null + ); + + jmsTemplate.send(mockQueue, new MessageCreator() { + @Override + public Message createMessage(Session session) throws JMSException { + System.out.println("Message send successfully"); + return session.createObjectMessage(notificationData); + } + }); + } + +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/notification/StorageTest.java b/fineract-provider/src/test/java/org/apache/fineract/notification/StorageTest.java new file mode 100644 index 00000000000..f81cae20872 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/notification/StorageTest.java @@ -0,0 +1,125 @@ +/** + * 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.notification; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.refEq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.fineract.notification.domain.Notification; +import org.apache.fineract.notification.domain.NotificationMapper; +import org.apache.fineract.notification.service.NotificationGeneratorReadPlatformService; +import org.apache.fineract.notification.service.NotificationGeneratorWritePlatformService; +import org.apache.fineract.notification.service.NotificationMapperWritePlatformService; +import org.apache.fineract.notification.service.NotificationWritePlatformServiceImpl; +import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.domain.AppUserRepository; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +public class StorageTest { + + private NotificationWritePlatformServiceImpl notificationWritePlatformServiceImpl; + + @Mock + private NotificationGeneratorReadPlatformService notificationGeneratorReadPlatformService; + + @Mock + private NotificationGeneratorWritePlatformService notificationGeneratorWritePlatformService; + + @Mock + private NotificationMapperWritePlatformService notificationMapperWritePlatformService; + + @Mock + private AppUserRepository appUserRepository; + + @Before + public void setUp() { + notificationWritePlatformServiceImpl = new NotificationWritePlatformServiceImpl( + notificationGeneratorWritePlatformService, + notificationGeneratorReadPlatformService, + appUserRepository, + notificationMapperWritePlatformService); + } + + @Test + public void testNotificationStorage() { + Long userId = 1L; + String objectType = "CLIENT"; + Long objectIdentifier = 1L; + String action = "created"; + Long actor = 1L; + String notificationContent = "A client was created"; + boolean isSystemGenerated = false; + + Notification notification = new Notification( + objectType, + objectIdentifier, + action, + actor, + isSystemGenerated, + notificationContent, + getCurrentDateTime() + ); + + + AppUser appUser = this.appUserRepository.findOne(1L); + + NotificationMapper notificationMapper = new NotificationMapper( + notification, + appUser, + false, + getCurrentDateTime() + ); + + when(this.notificationGeneratorWritePlatformService.create(refEq(notification))).thenReturn(1L); + when(this.appUserRepository.findOne(userId)).thenReturn(appUser); + when(this.notificationGeneratorReadPlatformService.findById(1L)).thenReturn(notification); + when(this.notificationMapperWritePlatformService.create(refEq(notificationMapper))).thenReturn(1L); + + Long actualGeneratedNotificationId = + notificationWritePlatformServiceImpl.notify( + userId, + objectType, + objectIdentifier, + action, + actor, + notificationContent, + isSystemGenerated + ); + + verify(this.notificationGeneratorWritePlatformService, times(1)).create(refEq(notification)); + verify(this.notificationMapperWritePlatformService, times(1)).create(refEq(notificationMapper)); + verify(this.notificationGeneratorReadPlatformService, times(1)).findById(1L); + assertEquals(actualGeneratedNotificationId, new Long(1)); + } + + private String getCurrentDateTime() { + Date date = new Date(); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return formatter.format(date); + } + +} diff --git a/fineract-provider/src/test/resources/META-INF/testNotificationContext.xml b/fineract-provider/src/test/resources/META-INF/testNotificationContext.xml new file mode 100644 index 00000000000..ba9db41c631 --- /dev/null +++ b/fineract-provider/src/test/resources/META-INF/testNotificationContext.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file