Browse Source

Remove foreign keys for notification table

pull/9353/head
ViacheslavKlimov 3 years ago
parent
commit
6a3beb9213
  1. 26
      application/src/main/data/upgrade/3.6.0/schema_update.sql
  2. 53
      application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
  3. 4
      application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
  4. 3
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java
  5. 4
      dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java
  6. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java
  7. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java
  8. 3
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  9. 4
      dao/src/main/resources/sql/schema-entities-idx.sql
  10. 4
      dao/src/main/resources/sql/schema-entities.sql

26
application/src/main/data/upgrade/3.6.0/schema_update.sql

@ -0,0 +1,26 @@
--
-- Copyright © 2016-2023 The Thingsboard Authors
--
-- Licensed 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.
--
ALTER TABLE widget_type
ADD COLUMN IF NOT EXISTS tags text[];
ALTER TABLE api_usage_state ADD COLUMN IF NOT EXISTS tbel_exec varchar(32);
UPDATE api_usage_state SET tbel_exec = js_exec WHERE tbel_exec IS NULL;
ALTER TABLE notification DROP CONSTRAINT IF EXISTS fk_notification_request_id;
ALTER TABLE notification DROP CONSTRAINT IF EXISTS fk_notification_recipient_id;
CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id);
CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id ON notification_request(tenant_id);

53
application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java

@ -25,9 +25,11 @@ import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.NotificationRequestId;
import org.thingsboard.server.common.data.id.NotificationRuleId;
import org.thingsboard.server.common.data.id.NotificationTargetId;
import org.thingsboard.server.common.data.id.TenantId;
@ -62,6 +64,7 @@ import org.thingsboard.server.common.data.notification.template.NotificationTemp
import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.notification.DefaultNotifications;
import org.thingsboard.server.dao.notification.NotificationDao;
@ -74,6 +77,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@ -86,6 +90,7 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@DaoSqlTest
@Slf4j
@ -279,6 +284,49 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
assertThat(getMyNotifications(false, 10)).size().isZero();
}
@Test
public void whenTenantIsDeleted_thenDeleteNotifications() throws Exception {
createDifferentTenant();
NotificationTarget target = createNotificationTarget(savedDifferentTenantUser.getId());
int notificationsCount = 20;
for (int i = 0; i < notificationsCount; i++) {
NotificationRequest request = submitNotificationRequest(target.getId(), "Test " + i, NotificationDeliveryMethod.WEB);
awaitNotificationRequest(request.getId());
}
List<NotificationRequest> requests = notificationRequestService.findNotificationRequestsByTenantIdAndOriginatorType(differentTenantId, EntityType.USER, new PageLink(100)).getData();
assertThat(requests).size().isEqualTo(notificationsCount);
for (NotificationRequest request : requests) {
List<Notification> notifications = notificationDao.findByRequestId(differentTenantId, request.getId(), new PageLink(100)).getData();
assertThat(notifications).size().isNotZero();
}
deleteDifferentTenant();
assertThat(notificationRequestService.findNotificationRequestsByTenantIdAndOriginatorType(differentTenantId, EntityType.USER, new PageLink(1)).getTotalElements())
.isZero();
for (NotificationRequest request : requests) {
assertThat(notificationDao.findByRequestId(differentTenantId, request.getId(), new PageLink(100)).getTotalElements())
.isZero();
}
}
@Test
public void whenUserIsDeleted_thenDeleteNotifications() throws Exception {
NotificationTarget target = createNotificationTarget(customerUserId);
int notificationsCount = 20;
for (int i = 0; i < notificationsCount; i++) {
NotificationRequest request = submitNotificationRequest(target.getId(), "Test " + i, NotificationDeliveryMethod.WEB);
awaitNotificationRequest(request.getId());
}
List<Notification> notifications = notificationDao.findByRecipientIdAndPageLink(tenantId, customerUserId, new PageLink(100)).getData();
assertThat(notifications).size().isGreaterThanOrEqualTo(notificationsCount);
doDelete("/api/user/" + customerUserId).andExpect(status().isOk());
notifications = notificationDao.findByRecipientIdAndPageLink(tenantId, customerUserId, new PageLink(100)).getData();
assertThat(notifications).isEmpty();
}
@Test
public void testNotificationUpdatesForSeveralUsers() throws Exception {
int usersCount = 150;
@ -692,6 +740,11 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
return future.get(30, TimeUnit.SECONDS);
}
private NotificationRequestStats awaitNotificationRequest(NotificationRequestId requestId) {
return await().atMost(30, TimeUnit.SECONDS)
.until(() -> getStats(requestId), Objects::nonNull);
}
private void checkFullNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String... expectedNotifications) {
assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications);
assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getType).containsOnly(DEFAULT_NOTIFICATION_TYPE);

4
application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java

@ -456,7 +456,9 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
loginSysAdmin();
notifications = await().atMost(30, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10), list -> list.size() == 1);
.until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 1);
assertThat(notifications).allSatisfy(notification -> {
assertThat(notification.getSubject()).isEqualTo("Rate limits exceeded for tenant " + TEST_TENANT_NAME);
});

3
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java

@ -42,6 +42,7 @@ import java.util.Optional;
public class DefaultNotificationRequestService implements NotificationRequestService, EntityDaoService {
private final NotificationRequestDao notificationRequestDao;
private final NotificationDao notificationDao;
private final NotificationRequestValidator notificationRequestValidator = new NotificationRequestValidator();
@ -81,10 +82,10 @@ public class DefaultNotificationRequestService implements NotificationRequestSer
return notificationRequestDao.findByRuleIdAndOriginatorEntityId(tenantId, ruleId, originatorEntityId);
}
// ON DELETE CASCADE is used: notifications for request are deleted as well
@Override
public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId requestId) {
notificationRequestDao.removeById(tenantId, requestId.getId());
notificationDao.deleteByRequestId(tenantId, requestId);
}
@Override

4
dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java

@ -41,4 +41,8 @@ public interface NotificationDao extends Dao<Notification> {
int updateStatusByRecipientId(TenantId tenantId, UserId recipientId, NotificationStatus status);
void deleteByRequestId(TenantId tenantId, NotificationRequestId requestId);
void deleteByRecipientId(TenantId tenantId, UserId recipientId);
}

10
dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java

@ -104,6 +104,16 @@ public class JpaNotificationDao extends JpaAbstractDao<NotificationEntity, Notif
return notificationRepository.updateStatusByRecipientId(recipientId.getId(), status);
}
@Override
public void deleteByRequestId(TenantId tenantId, NotificationRequestId requestId) {
notificationRepository.deleteByRequestId(requestId.getId());
}
@Override
public void deleteByRecipientId(TenantId tenantId, UserId recipientId) {
notificationRepository.deleteByRecipientId(recipientId.getId());
}
@Override
protected Class<NotificationEntity> getEntityClass() {
return NotificationEntity.class;

6
dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java

@ -61,6 +61,12 @@ public interface NotificationRepository extends JpaRepository<NotificationEntity
@Transactional
int deleteByIdAndRecipientId(UUID id, UUID recipientId);
@Transactional
void deleteByRequestId(UUID requestId);
@Transactional
void deleteByRecipientId(UUID recipientId);
@Modifying
@Transactional
@Query("UPDATE NotificationEntity n SET n.status = :status " +

3
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -49,6 +49,7 @@ import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.notification.NotificationDao;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
@ -85,6 +86,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
private final UserDao userDao;
private final UserCredentialsDao userCredentialsDao;
private final UserAuthSettingsDao userAuthSettingsDao;
private final NotificationDao notificationDao;
private final DataValidator<User> userValidator;
private final DataValidator<UserCredentials> userCredentialsValidator;
private final ApplicationEventPublisher eventPublisher;
@ -255,6 +257,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId());
userCredentialsDao.removeById(tenantId, userCredentials.getUuidId());
userAuthSettingsDao.removeByUserId(userId);
notificationDao.deleteByRecipientId(tenantId, userId);
deleteEntityRelations(tenantId, userId);
userDao.removeById(tenantId, userId.getId());
eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(userId));

4
dao/src/main/resources/sql/schema-entities-idx.sql

@ -104,6 +104,8 @@ CREATE INDEX IF NOT EXISTS idx_notification_rule_tenant_id_trigger_type_created_
CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_user_created_time ON notification_request(tenant_id, created_time DESC)
WHERE originator_entity_type = 'USER';
CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id ON notification_request(tenant_id);
CREATE INDEX IF NOT EXISTS idx_notification_request_rule_id_originator_entity_id ON notification_request(rule_id, originator_entity_id)
WHERE originator_entity_type = 'ALARM';
@ -112,6 +114,8 @@ CREATE INDEX IF NOT EXISTS idx_notification_request_status ON notification_reque
CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id);
CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id);
CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_created_time ON notification(recipient_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_unread ON notification(recipient_id) WHERE status <> 'READ';

4
dao/src/main/resources/sql/schema-entities.sql

@ -851,8 +851,8 @@ CREATE TABLE IF NOT EXISTS notification_request (
CREATE TABLE IF NOT EXISTS notification (
id UUID NOT NULL,
created_time BIGINT NOT NULL,
request_id UUID NULL CONSTRAINT fk_notification_request_id REFERENCES notification_request(id) ON DELETE CASCADE,
recipient_id UUID NOT NULL CONSTRAINT fk_notification_recipient_id REFERENCES tb_user(id) ON DELETE CASCADE,
request_id UUID,
recipient_id UUID NOT NULL,
type VARCHAR(50) NOT NULL,
subject VARCHAR(255),
body VARCHAR(1000) NOT NULL,

Loading…
Cancel
Save