Browse Source

Merge remote-tracking branch 'origin/rc' into master-rc-merge

# Conflicts:
#	ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html
pull/15424/head
Viacheslav Klimov 2 months ago
parent
commit
8dbadb754a
Failed to extract signature
  1. 2
      application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java
  2. 47
      application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java
  3. 13
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java
  4. 22
      dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java
  5. 20
      pom.xml
  6. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html
  7. 2
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  8. 4
      ui-ngx/src/assets/locale/locale.constant-en_US.json

2
application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java

@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentInfo;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId;
@ -77,6 +78,7 @@ public class AlarmCommentController extends BaseController {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmInfoId(alarmId, Operation.WRITE);
alarmComment.setAlarmId(alarmId);
alarmComment.setType(AlarmCommentType.OTHER);
return tbAlarmCommentService.saveAlarmComment(alarm, alarmComment, getCurrentUser());
}

47
application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java

@ -44,7 +44,9 @@ import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -226,6 +228,18 @@ public class AlarmCommentControllerTest extends AbstractControllerTest {
doDelete("/api/alarm/" + alarm.getId() + "/comment/" + alarmComment.getId())
.andExpect(status().isOk());
Optional<AlarmCommentInfo> systemCommentOpt = doGetTyped(
"/api/alarm/" + alarm.getId() + "/comment" + "?page=0&pageSize=10", new TypeReference<PageData<AlarmCommentInfo>>() {
}
).getData().stream().filter(alarmCommentInfo -> alarmCommentInfo.getType().equals(AlarmCommentType.SYSTEM)).findFirst();
assertThat(systemCommentOpt).isPresent();
AlarmCommentInfo systemComment = systemCommentOpt.get();
assertThat(systemComment.getId()).isEqualTo(alarmComment.getId());
assertThat(systemComment.getType()).isEqualTo(AlarmCommentType.SYSTEM);
assertThat(systemComment.getComment().get("text").asText()).isEqualTo(String.format("User %s deleted his comment",
TENANT_ADMIN_EMAIL));
AlarmComment expectedAlarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
@ -361,6 +375,39 @@ public class AlarmCommentControllerTest extends AbstractControllerTest {
Assert.assertTrue("Created alarm doesn't match the found one!", equals);
}
@Test
public void testShouldNotCreateOrUpdateSystemAlarmComment() throws Exception {
loginTenantAdmin();
AlarmComment alarmComment = AlarmComment.builder()
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().set("text", new TextNode("Acknowledged by tenant admin")))
.build();
AlarmComment created = doPost("/api/alarm/" + alarm.getId() + "/comment", alarmComment, AlarmComment.class);
assertThat(created.getType()).isEqualTo(AlarmCommentType.OTHER);
// acknowledge alarm to create system comment
doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isOk());
Optional<AlarmCommentInfo> systemCommentOpt = doGetTyped(
"/api/alarm/" + alarm.getId() + "/comment" + "?page=0&pageSize=10", new TypeReference<PageData<AlarmCommentInfo>>() {
}
).getData().stream().filter(alarmCommentInfo -> alarmCommentInfo.getType().equals(AlarmCommentType.SYSTEM)).findFirst();
assertThat(systemCommentOpt).isPresent();
AlarmCommentInfo systemComment = systemCommentOpt.get();
// system comment can't be updated with other type
systemComment.setType(AlarmCommentType.OTHER);
doPost("/api/alarm/" + alarm.getId() + "/comment", systemComment).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("System alarm comment can't be updated!")));
// system comment can't be updated with other text
systemComment.setType(AlarmCommentType.SYSTEM);
systemComment.setComment(JacksonUtil.newObjectNode().set("text", new TextNode("New system comment")));
doPost("/api/alarm/" + alarm.getId() + "/comment", systemComment).andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("System alarm comment can't be updated!")));
}
private AlarmComment createAlarmComment(AlarmId alarmId, String text) {
AlarmComment alarmComment = AlarmComment.builder()
.comment(JacksonUtil.newObjectNode().set("text", new TextNode(text)))

13
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java

@ -48,13 +48,13 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
@Override
public AlarmComment createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment) {
alarmCommentDataValidator.validate(alarmComment, c -> tenantId);
AlarmComment oldAlarmComment = alarmCommentDataValidator.validate(alarmComment, c -> tenantId);
boolean isCreated = alarmComment.getId() == null;
AlarmComment result;
if (isCreated) {
result = createAlarmComment(tenantId, alarmComment);
} else {
result = updateAlarmComment(tenantId, alarmComment);
result = updateAlarmComment(tenantId, alarmComment, oldAlarmComment);
}
if (result != null) {
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(result)
@ -101,18 +101,17 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
return alarmCommentDao.save(tenantId, alarmComment);
}
private AlarmComment updateAlarmComment(TenantId tenantId, AlarmComment newAlarmComment) {
private AlarmComment updateAlarmComment(TenantId tenantId, AlarmComment newAlarmComment, AlarmComment oldAlarmComment) {
log.debug("Update Alarm comment : {}", newAlarmComment);
AlarmComment existing = alarmCommentDao.findAlarmCommentById(tenantId, newAlarmComment.getId().getId());
if (existing != null) {
if (oldAlarmComment != null) {
if (newAlarmComment.getComment() != null) {
JsonNode comment = newAlarmComment.getComment();
((ObjectNode) comment).put("edited", "true");
((ObjectNode) comment).put("editedOn", System.currentTimeMillis());
existing.setComment(comment);
oldAlarmComment.setComment(comment);
}
return alarmCommentDao.save(tenantId, existing);
return alarmCommentDao.save(tenantId, oldAlarmComment);
}
return null;
}

22
dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java

@ -18,14 +18,18 @@ package org.thingsboard.server.dao.service.validator;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.exception.DataValidationException;
import org.thingsboard.server.dao.alarm.AlarmCommentDao;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.exception.DataValidationException;
@Component
@AllArgsConstructor
public class AlarmCommentDataValidator extends DataValidator<AlarmComment> {
private final AlarmCommentDao alarmCommentDao;
@Override
protected void validateDataImpl(TenantId tenantId, AlarmComment alarmComment) {
if (alarmComment.getComment() == null) {
@ -35,4 +39,20 @@ public class AlarmCommentDataValidator extends DataValidator<AlarmComment> {
throw new DataValidationException("Alarm id should be specified!");
}
}
@Override
protected AlarmComment validateUpdate(TenantId tenantId, AlarmComment alarmComment) {
AlarmComment oldAlarmComment = null;
if (alarmComment.getId() != null) {
oldAlarmComment = alarmCommentDao.findAlarmCommentById(tenantId, alarmComment.getId().getId());
if (oldAlarmComment == null) {
throw new DataValidationException("Can't update non existing alarm comment!");
}
if (oldAlarmComment.getType() == AlarmCommentType.SYSTEM) {
throw new DataValidationException("System alarm comment can't be updated!");
}
}
return oldAlarmComment;
}
}

20
pom.xml

@ -71,6 +71,7 @@
<metrics.version>4.2.25</metrics.version>
<cassandra-all.version>5.0.4</cassandra-all.version> <!-- tools -->
<guava.version>33.1.0-jre</guava.version>
<tomcat.version>10.1.54</tomcat.version> <!-- to fix CVE-2026-34487, CVE-2026-34486, CVE-2026-34483. TODO: remove when fixed in spring-boot-dependencies -->
<commons-lang3.version>3.18.0</commons-lang3.version> <!-- to fix CVE-2025-48924. TODO: remove when fixed in spring-boot-dependencies -->
<commons-io.version>2.16.1</commons-io.version>
<commons-logging.version>1.3.1</commons-logging.version>
@ -994,6 +995,25 @@
<dependencyManagement>
<dependencies>
<!-- Temporary tomcat version override to fix CVE-2026-34487, CVE-2026-34486, CVE-2026-34483.
Must be declared before the spring-boot-dependencies BOM import to take precedence.
TODO: remove when fixed in spring-boot-dependencies -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>${tomcat.version}</version>
</dependency>
<!-- End of tomcat version override -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>

2
ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html

@ -49,7 +49,7 @@
<span
[innerHTML]="getFullName(user) | highlight:searchText"></span>
}
<span [innerHTML]="user.email | highlight:searchText"></span>
<span [innerHTML]="user.email | highlight:searchText" tbTruncateWithTooltip></span>
</div>
</mat-option>
}

2
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -217,8 +217,6 @@ export class AddDeviceProfileDialogComponent extends
case 1:
return 'device-profile.transport-configuration';
case 2:
return 'device-profile.alarm-rules';
case 3:
return 'device-profile.device-provisioning';
}
}

4
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2402,7 +2402,7 @@
"search": "Search asset profiles",
"selected-asset-profiles": "{ count, plural, =1 {1 asset profile} other {# asset profiles} } selected",
"no-asset-profiles-matching": "No asset profile matching '{{entity}}' were found.",
"asset-profile-required": "Asset profile is required",
"asset-profile-required": "Asset profile is required.",
"idCopiedMessage": "Asset profile Id has been copied to clipboard",
"set-default": "Make asset profile default",
"delete": "Delete asset profile",
@ -2447,7 +2447,7 @@
"search": "Search device profiles",
"selected-device-profiles": "{ count, plural, =1 {1 device profile} other {# device profiles} } selected",
"no-device-profiles-matching": "No device profile matching '{{entity}}' were found.",
"device-profile-required": "Device profile is required",
"device-profile-required": "Device profile is required.",
"idCopiedMessage": "Device profile Id has been copied to clipboard",
"set-default": "Make device profile default",
"delete": "Delete device profile",

Loading…
Cancel
Save