Notification sub-system

This commit is contained in:
Anh3h
2017-12-13 18:04:36 +05:30
committed by Avik Ganguly
parent 58184a5219
commit 7395d6c543
49 changed files with 1794 additions and 588 deletions
-1
View File
@@ -93,7 +93,6 @@ dependencies {
//[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',
@@ -1,5 +1,3 @@
package org.apache.fineract.integrationtests;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -18,7 +16,7 @@ package org.apache.fineract.integrationtests;
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.integrationtests;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder;
@@ -54,4 +52,5 @@ public class NotificationApiTest {
System.out.println("Response : " + response.toString());
Assert.assertNotNull(response);
}
}
@@ -1,5 +1,3 @@
package org.apache.fineract.integrationtests.common;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -18,6 +16,7 @@ package org.apache.fineract.integrationtests.common;
* 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;
@@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletRequest;
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 ToApiJsonSerializer<PlatformRequestLog> toApiJsonSerializer;
private final ConfigurationDomainService configurationDomainService;
private final CacheWritePlatformService cacheWritePlatformService;
private final NotificationReadPlatformService notificationReadPlatformService;
private final String tenantRequestHeader = "Fineract-Platform-TenantId";
private final boolean exceptionIfHeaderMissing = true;
@@ -177,6 +179,12 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil
response.addHeader("X-Notification-Refresh", "false");
}
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/"));
@@ -18,7 +18,6 @@
*/
package org.apache.fineract.notification.api;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
@@ -86,4 +85,5 @@ public class NotificationApiResource {
this.context.authenticatedUser();
this.notificationReadPlatformService.updateNotificationReadStatus();
}
}
@@ -18,7 +18,6 @@
*/
package org.apache.fineract.notification.cache;
public class CacheNotificationResponseHeader {
private boolean hasNotifications;
@@ -29,6 +29,7 @@ public class NotificationData implements Serializable {
private String action;
private Long actorId;
private String content;
private boolean isRead;
private boolean isSystemGenerated;
private String tenantIdentifier;
private String createdAt;
@@ -40,12 +41,13 @@ public class NotificationData implements Serializable {
}
public NotificationData(String objectType, Long objectId, String action, Long actorId, String content, boolean isSystemGenerated,
String tenantIdentifier, Long officeId, List<Long> userIds) {
boolean isRead, String tenantIdentifier, Long officeId, List<Long> userIds) {
this.objectType = objectType;
this.objectId = objectId;
this.action = action;
this.actorId = actorId;
this.content = content;
this.isRead = isRead;
this.isSystemGenerated = isSystemGenerated;
this.tenantIdentifier = tenantIdentifier;
this.officeId = officeId;
@@ -123,6 +125,14 @@ public class NotificationData implements Serializable {
public void setContent(String content) {
this.content = content;
}
public boolean isRead() {
return this.isRead;
}
public void setRead(boolean isRead) {
this.isRead = isRead;
}
public boolean isSystemGenerated() {
return isSystemGenerated;
@@ -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;
}
}
@@ -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;
}
}
@@ -20,9 +20,7 @@ package org.apache.fineract.notification.domain;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.*;
@Entity
@Table(name = "notification_generator")
@@ -90,7 +88,7 @@ public class Notification extends AbstractPersistableCustom<Long> {
return actorId;
}
public void setActor(Long actor) {
public void setActor(Long actorId) {
this.actorId = actorId;
}
@@ -18,7 +18,6 @@
*/
package org.apache.fineract.notification.domain;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.useradministration.domain.AppUser;
@@ -20,5 +20,4 @@ package org.apache.fineract.notification.domain;
import org.springframework.data.jpa.repository.JpaRepository;
public interface NotificationMapperRepository extends JpaRepository<NotificationMapper, Long> {}
@@ -20,5 +20,4 @@ package org.apache.fineract.notification.domain;
import org.springframework.data.jpa.repository.JpaRepository;
public interface NotificationRepository extends JpaRepository<Notification, Long> {}
@@ -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<Long> {
@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;
}
}
@@ -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.domain;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface TopicRepository extends JpaRepository<Topic, Long>, JpaSpecificationExecutor<Topic> {
List<Topic> findByEntityId(Long entityId);
List<Topic> findByMemberType(String memberType);
}
@@ -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<Long> {
@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;
}
}
@@ -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.domain;
import java.util.List;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface TopicSubscriberRepository extends JpaRepository<TopicSubscriber, Long>, JpaSpecificationExecutor<TopicSubscriber> {
List<TopicSubscriber> findBySubscriber(AppUser subscriber);
}
@@ -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);
}
});
}
}
@@ -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);
}
}
@@ -20,7 +20,6 @@ package org.apache.fineract.notification.service;
import org.apache.fineract.notification.domain.NotificationMapper;
public interface NotificationMapperWritePlatformService {
Long create(NotificationMapper notificationMapper);
@@ -61,16 +61,12 @@ public class NotificationReadPlatformServiceImpl implements NotificationReadPlat
Long lastFetch = notificationResponseHeaderCache.get(appUserId).getLastFetch();
if ((now - lastFetch) > 1) {
return this.createUpdateCacheValue(appUserId, now, notificationResponseHeaderCache);
} else {
return notificationResponseHeaderCache.get(appUserId).hasNotifications();
}
} else {
return this.createUpdateCacheValue(appUserId, now, notificationResponseHeaderCache);
return notificationResponseHeaderCache.get(appUserId).hasNotifications();
}
} else {
return this.initializeTenantNotificationResponseHeaderCache(tenantId, now, appUserId);
return this.createUpdateCacheValue(appUserId, now, notificationResponseHeaderCache);
}
return this.initializeTenantNotificationResponseHeaderCache(tenantId, now, appUserId);
}
private boolean initializeTenantNotificationResponseHeaderCache(Long tenantId, Long now, Long appUserId) {
@@ -26,4 +26,5 @@ public interface NotificationWritePlatformService {
Long notify(List<Long> userIds, String objectType, Long objectId, String action,
Long actorId, String notificationContent, boolean isSystemGenerated);
}
@@ -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<TopicData> getAllTopics();
Collection<TopicData> getAllEnabledTopics();
TopicData findById(Long id);
}
@@ -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<TopicData> {
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<TopicData> getAllTopics() {
final TopicMapper tm = new TopicMapper();
String sql = "select " + tm.schema();
return this.jdbcTemplate.query(sql, tm, new Object[] {});
}
@Override
public Collection<TopicData> 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);
}
}
}
@@ -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<TopicSubscriberData> getSubscribers(Long entityId, String entityType, String memberType);
}
@@ -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<TopicSubscriberData> {
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<TopicSubscriberData> 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 });
}
}
@@ -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);
}
@@ -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();
}
}
@@ -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);
}
@@ -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();
}
}
@@ -205,6 +205,10 @@ public class Office extends AbstractPersistableCustom<Long> {
return this.hierarchy;
}
public Office getParent() {
return this.parent;
}
public boolean hasParentOf(final Office office) {
boolean isParent = false;
if (this.parent != null) {
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.organisation.office.service;
import java.util.List;
import java.util.Map;
import javax.persistence.PersistenceException;
@@ -29,6 +30,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
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 +43,8 @@ import org.apache.fineract.organisation.office.domain.OfficeTransactionRepositor
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 +65,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 +110,23 @@ public class OfficeWritePlatformServiceJpaRepositoryImpl implements OfficeWriteP
office.generateHierarchy();
this.officeRepositoryWrapper.save(office);
Long entityId = office.getId();
String entityType = "";
if (office.getParent() == null) {
entityType = "OFFICE";
} else {
entityType = "BRANCH";
}
List<Role> 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()) //
@@ -142,6 +168,27 @@ public class OfficeWritePlatformServiceJpaRepositoryImpl implements OfficeWriteP
if (changes.containsKey("parentId")) {
final Office parent = validateUserPriviledgeOnOfficeAndRetrieve(currentUser, parentId);
office.update(parent);
String entityType = "";
if (office.getParent() == null) {
entityType = "OFFICE";
} else {
entityType = "BRANCH";
}
List<Topic> entityTopics = topicRepository.findByEntityId(office.getId());
for (Topic topic : entityTopics) {
topic.setEntityType(entityType);
topicRepository.save(topic);
}
}
if (changes.containsKey("name")) {
List<Topic> entityTopics = topicRepository.findByEntityId(office.getId());
for (Topic topic: entityTopics) {
Role role = roleRepository.getRoleByName(topic.getMemberType());
String title = role.getName() + " of " + office.getName();
topic.setTitle(title);
topicRepository.save(topic);
}
}
if (!changes.isEmpty()) {
@@ -67,6 +67,7 @@ public class BusinessEventNotificationConstants {
"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;
private BUSINESS_ENTITY(final String value) {
@@ -53,6 +53,8 @@ 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.*;
@@ -37,6 +37,8 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
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;
@@ -151,6 +153,9 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_PRODUCT_CREATE,
constructEntityMap(BUSINESS_ENTITY.LOAN_PRODUCT, loanproduct));
this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_PRODUCT_CREATE,
constructEntityMap(BUSINESS_ENTITY.LOAN_PRODUCT, loanproduct));
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanproduct.getId()) //
@@ -62,6 +62,8 @@ 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;
@@ -229,6 +231,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
}
final Long savingsId = account.getId();
this.businessEventNotifierService.notifyBusinessEventWasExecuted( BUSINESS_EVENTS.FIXED_DEPOSIT_ACCOUNT_CREATE,
constructEntityMap(BUSINESS_ENTITY.DEPOSIT_ACCOUNT, account));
this.businessEventNotifierService.notifyBusinessEventWasExecuted( BUSINESS_EVENTS.FIXED_DEPOSIT_ACCOUNT_CREATE,
constructEntityMap(BUSINESS_ENTITY.DEPOSIT_ACCOUNT, account));
@@ -290,6 +294,8 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
financialYearBeginningMonth);
account.validateApplicableInterestRate();
this.savingAccountRepository.save(account);
this.businessEventNotifierService.notifyBusinessEventWasExecuted( BUSINESS_EVENTS.RECURRING_DEPOSIT_ACCOUNT_CREATE,
constructEntityMap(BUSINESS_ENTITY.DEPOSIT_ACCOUNT, account));
this.businessEventNotifierService.notifyBusinessEventWasExecuted( BUSINESS_EVENTS.RECURRING_DEPOSIT_ACCOUNT_CREATE,
constructEntityMap(BUSINESS_ENTITY.DEPOSIT_ACCOUNT, account));
@@ -215,11 +215,11 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP
throw new PlatformDataIntegrityException("error.msg.shareproduct.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
private Map<BusinessEventNotificationConstants.BUSINESS_ENTITY, Object> constructEntityMap(final BusinessEventNotificationConstants.BUSINESS_ENTITY entityEvent, Object entity) {
Map<BusinessEventNotificationConstants.BUSINESS_ENTITY, Object> map = new HashMap<>(1);
map.put(entityEvent, entity);
return map;
Map<BusinessEventNotificationConstants.BUSINESS_ENTITY, Object> map = new HashMap<>(1);
map.put(entityEvent, entity);
return map;
}
}
@@ -129,6 +129,10 @@ public class Role extends AbstractPersistableCustom<Long> {
return new RoleData(getId(), this.name, this.description, this.disabled);
}
public String getName() {
return this.name;
}
public void disableRole() {
this.disabled = true;
}
@@ -20,12 +20,12 @@ package org.apache.fineract.useradministration.service;
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.exception.PlatformDataIntegrityEx
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;
@@ -87,13 +91,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 +111,8 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
this.appUserPreviewPasswordRepository = appUserPreviewPasswordRepository;
this.staffRepositoryWrapper = staffRepositoryWrapper;
this.clientRepositoryWrapper = clientRepositoryWrapper;
this.topicRepository = topicRepository;
this.topicSubscriberRepository = topicSubscriberRepository;
}
@Transactional
@@ -150,6 +159,19 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
final Boolean sendPasswordToEmail = command.booleanObjectValueOfParameterNamed("sendPasswordToEmail");
this.userDomainService.create(appUser, sendPasswordToEmail);
List<Topic> possibleTopics = topicRepository.findByEntityId(appUser.getOffice().getId());
if (!possibleTopics.isEmpty()) {
Set<Role> 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()) //
@@ -201,8 +223,7 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
isSelfServiceUser = command.booleanPrimitiveValueOfParameterNamed(AppUserConstants.IS_SELF_SERVICE_USER);
}
if(isSelfServiceUser
&& command.hasParameter(AppUserConstants.CLIENTS)){
if(isSelfServiceUser && command.hasParameter(AppUserConstants.CLIENTS)){
JsonArray clientsArray = command.arrayOfParameterNamed(AppUserConstants.CLIENTS);
Collection<Long> clientIds = new HashSet<>();
for(JsonElement clientElement : clientsArray){
@@ -214,9 +235,32 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
final Map<String, Object> changes = userToUpdate.update(command, this.platformPasswordEncoder, clients);
if (changes.containsKey("officeId")) {
final Long officeId = (Long) changes.get("officeId");
final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
final Long oldOfficeId = userToUpdate.getOffice().getId();
final Long newOfficeId = (Long) changes.get("officeId");
final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(newOfficeId);
userToUpdate.changeOffice(office);
List<Topic> oldTopics = topicRepository.findByEntityId(oldOfficeId);
List<Topic> newTopics = topicRepository.findByEntityId(newOfficeId);
List<TopicSubscriber> oldSubscriptions = topicSubscriberRepository.findBySubscriber(userToUpdate);
for (TopicSubscriber subscriber : oldSubscriptions) {
for (Topic topic : oldTopics) {
if (subscriber.getTopic().getId() == topic.getId()) {
topicSubscriberRepository.delete(subscriber);
}
}
}
Set<Role> userRoles = userToUpdate.getRoles();
for (Role curRole : userRoles) {
for (Topic curTopic : newTopics) {
if (curRole.getName().compareToIgnoreCase(curTopic.getMemberType()) == 0) {
TopicSubscriber newSubscription = new TopicSubscriber(curTopic, userToUpdate, new Date());
topicSubscriberRepository.save(newSubscription);
}
}
}
}
if (changes.containsKey("staffId")) {
@@ -230,9 +274,34 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
if (changes.containsKey("roles")) {
final String[] roleIds = (String[]) changes.get("roles");
final Set<Role> allRoles = assembleSetOfRoles(roleIds);
userToUpdate.updateRoles(allRoles);
final Set<Role> oldRoles = userToUpdate.getRoles() ;
final Set<Role> tempOldRoles = new HashSet<>(oldRoles);
final Set<Role> updatedRoles = assembleSetOfRoles(roleIds);
final Set<Role> tempUpdatedRoles = new HashSet<>(updatedRoles);
tempOldRoles.removeAll(updatedRoles);
List<TopicSubscriber> oldSubscriptions = topicSubscriberRepository.findBySubscriber(userToUpdate);
for (TopicSubscriber subscriber : oldSubscriptions) {
Topic topic = subscriber.getTopic();
for (Role role : tempOldRoles) {
if (role.getName().compareToIgnoreCase(topic.getMemberType()) == 0) {
topicSubscriberRepository.delete(subscriber);
}
}
}
tempUpdatedRoles.removeAll(oldRoles);
List<Topic> newTopics = topicRepository.findByEntityId(userToUpdate.getOffice().getId());
for (Topic topic : newTopics) {
for (Role role : tempUpdatedRoles) {
if (role.getName().compareToIgnoreCase(topic.getMemberType()) == 0) {
TopicSubscriber topicSubscriber = new TopicSubscriber(topic, userToUpdate, new Date());
topicSubscriberRepository.save(topicSubscriber);
}
}
}
userToUpdate.updateRoles(updatedRoles);
}
if (!changes.isEmpty()) {
@@ -324,6 +393,10 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit
if (user == null || user.isDeleted()) { throw new UserNotFoundException(userId); }
user.delete();
List<TopicSubscriber> subscriptions = topicSubscriberRepository.findBySubscriber(user);
for (TopicSubscriber subscription : subscriptions) {
topicSubscriberRepository.delete(subscription);
}
this.appUserRepository.save(user);
return new CommandProcessingResultBuilder().withEntityId(userId).withOfficeId(user.getOffice().getId()).build();
@@ -20,6 +20,7 @@ package org.apache.fineract.useradministration.service;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.PersistenceException;
@@ -30,6 +31,10 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.notification.domain.Topic;
import org.apache.fineract.notification.domain.TopicRepository;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepository;
import org.apache.fineract.useradministration.command.PermissionsCommand;
import org.apache.fineract.useradministration.domain.Permission;
import org.apache.fineract.useradministration.domain.PermissionRepository;
@@ -55,18 +60,23 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
private final PlatformSecurityContext context;
private final RoleRepository roleRepository;
private final PermissionRepository permissionRepository;
private final TopicRepository topicRepository;
private final OfficeRepository officeRepository;
private final RoleDataValidator roleCommandFromApiJsonDeserializer;
private final PermissionsCommandFromApiJsonDeserializer permissionsFromApiJsonDeserializer;
@Autowired
public RoleWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final RoleRepository roleRepository,
final PermissionRepository permissionRepository, final RoleDataValidator roleCommandFromApiJsonDeserializer,
final PermissionsCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
final PermissionsCommandFromApiJsonDeserializer fromApiJsonDeserializer, TopicRepository topicRepository,
final OfficeRepository officeRepository) {
this.context = context;
this.roleRepository = roleRepository;
this.permissionRepository = permissionRepository;
this.roleCommandFromApiJsonDeserializer = roleCommandFromApiJsonDeserializer;
this.permissionsFromApiJsonDeserializer = fromApiJsonDeserializer;
this.topicRepository = topicRepository;
this.officeRepository = officeRepository;
}
@Transactional
@@ -78,10 +88,22 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
this.roleCommandFromApiJsonDeserializer.validateForCreate(command.json());
final Role entity = Role.fromJson(command);
this.roleRepository.save(entity);
final Role role = Role.fromJson(command);
List<Office> offices = officeRepository.findAll();
for (Office office : offices) {
String entityType = "";
if (office.getParent() == null) {
entityType = "OFFICE";
} else {
entityType = "BRANCH";
}
String title = role.getName() + " of " + office.getName();
Topic newTopic = new Topic(title, true, office.getId(), entityType, role.getName().toUpperCase());
topicRepository.save(newTopic);
}
this.roleRepository.save(role);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(entity.getId()).build();
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(role.getId()).build();
} catch (final DataIntegrityViolationException dve) {
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
return new CommandProcessingResultBuilder() //
@@ -129,8 +151,22 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
final Role role = this.roleRepository.findOne(roleId);
if (role == null) { throw new RoleNotFoundException(roleId); }
String oldMemberType = role.getName().toUpperCase();
final Map<String, Object> changes = role.update(command);
if (changes.containsKey("name")) {
String newMemberType = (String) changes.get("name");
List<Topic> entityTopics = topicRepository.findByMemberType(oldMemberType);
for (Topic topic : entityTopics) {
Office office = officeRepository.findOne(topic.getEntityId());
String title = role.getName() + " of " + office.getName();
topic.setTitle(title);
topic.setMemberType(newMemberType.toUpperCase());
topicRepository.save(topic);
}
}
if (!changes.isEmpty()) {
this.roleRepository.saveAndFlush(role);
}
@@ -222,6 +258,10 @@ public class RoleWritePlatformServiceJpaRepositoryImpl implements RoleWritePlatf
final Integer count = this.roleRepository.getCountOfRolesAssociatedWithUsers(roleId);
if (count > 0) { throw new RoleAssociatedException("error.msg.role.associated.with.users.deleted", roleId); }
List<Topic> topics = topicRepository.findByMemberType(role.getName().toUpperCase());
for (Topic topic : topics) {
topicRepository.delete(topic);
}
this.roleRepository.delete(role);
return new CommandProcessingResultBuilder().withEntityId(roleId).build();
} catch (final DataIntegrityViolationException e) {
@@ -101,6 +101,5 @@
</bean>
<import resource="spmContext.xml"/>
<import resource="notificationContext.xml"/>
</beans>
@@ -41,4 +41,5 @@
<property name="destinationName" value="NotificationQueue"/>
<property name="messageListener" ref="notificationEventListener" />
</bean>
</beans>
@@ -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);
@@ -1,5 +1,3 @@
package org.apache.fineract.notification;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -18,6 +16,7 @@ package org.apache.fineract.notification;
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.notification;
import org.springframework.jms.listener.SessionAwareMessageListener;
@@ -1,5 +1,3 @@
package org.apache.fineract.notification;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -18,6 +16,7 @@ package org.apache.fineract.notification;
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.notification;
import org.junit.Before;
import org.junit.Test;
@@ -1,5 +1,3 @@
package org.apache.fineract.notification;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -18,6 +16,7 @@ package org.apache.fineract.notification;
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.notification;
import com.mockrunner.mock.jms.MockQueue;
import org.apache.fineract.notification.data.NotificationData;
@@ -61,6 +60,7 @@ public class SenderTest {
actorId,
notificationContent,
false,
false,
null,
null,
null
@@ -1,5 +1,3 @@
package org.apache.fineract.notification;
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -19,9 +17,12 @@ package org.apache.fineract.notification;
* under the License.
*/
package org.apache.fineract.notification;
import org.apache.fineract.notification.domain.Notification;
import org.apache.fineract.notification.domain.NotificationMapper;
import org.apache.fineract.notification.service.NotificationGeneratorReadRepositoryWrapper;
import org.apache.fineract.notification.service.NotificationGeneratorWritePlatformService;
import org.apache.fineract.notification.service.NotificationMapperWritePlatformService;
import org.apache.fineract.notification.service.NotificationWritePlatformServiceImpl;
@@ -127,4 +128,5 @@ public class StorageTest {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return formatter.format(date);
}
}
@@ -0,0 +1,109 @@
/**
* 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.assertEquals;
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.util.Date;
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.service.TopicSubscriberWritePlatformService;
import org.apache.fineract.notification.service.TopicWritePlatformService;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepository;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepository;
import org.apache.fineract.useradministration.domain.Role;
import org.apache.fineract.useradministration.domain.RoleRepository;
import org.junit.Test;
import org.mockito.Mock;
public class TopicTest {
@Mock
private OfficeRepository officeRepository;
@Mock
private RoleRepository roleRepository;
@Mock
private TopicWritePlatformService topicWritePltfService;
@Mock
private TopicRepository topicRepository;
@Mock
private TopicSubscriberWritePlatformService topicSubscriberWritePltfService;
@Mock
private AppUserRepository appUserRepository;
@Test
public void testTopicStorage() {
Office office = officeRepository.getOne(1L);
Role role = new Role("New Member_Type", "Testing topic creation");
String title = role.getName() + " of " + office.getName();
Long entityId = office.getId();
String entityType = "";
if (office.getParent() == null) {
entityType = "OFFICE";
} else {
entityType = "BRANCH";
}
Topic topic = new Topic(title, true, entityId, entityType, role.getName().toUpperCase());
when(this.officeRepository.getOne(1L)).thenReturn(office);
when(this.roleRepository.save(role)).thenReturn(role);
when(this.topicWritePltfService.create(refEq(topic))).thenReturn(1L);
this.roleRepository.save(role);
Long topicId = this.topicWritePltfService.create(topic);
verify(this.roleRepository, times(1)).save(role);
verify(this.topicWritePltfService, times(1)).create(refEq(topic));
assertEquals(topicId, new Long(1));
}
@Test
public void testTopicSubscriberStorage() {
AppUser user = appUserRepository.findOne(1L);
Topic topic = topicRepository.findOne(1L);
TopicSubscriber topicSubscriber = new TopicSubscriber(topic, user, new Date());
when(this.appUserRepository.getOne(1L)).thenReturn(user);
when(this.topicRepository.getOne(1L)).thenReturn(topic);
when(this.topicSubscriberWritePltfService.create(refEq(topicSubscriber))).thenReturn(1L);
Long subscriberId = this.topicSubscriberWritePltfService.create(topicSubscriber);
verify(this.topicSubscriberWritePltfService, times(1)).create(refEq(topicSubscriber));
assertEquals(subscriberId, new Long(1));
}
}