Browse Source

Merge pull request #15263 from thingsboard/rc

Merge rc into master
pull/15269/head
Viacheslav Klimov 3 months ago
committed by GitHub
parent
commit
0b3e736c0e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 19
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
  2. 2
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  3. 21
      application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java
  4. 10
      dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java
  5. 3
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.html

19
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.exception;
import jakarta.persistence.PersistenceException;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@ -52,6 +53,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.msg.tools.MaxPayloadSizeExceededException;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
import org.thingsboard.server.service.security.exception.JwtExpiredTokenException;
import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;
@ -162,8 +164,8 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
handleAuthenticationException(authenticationException, response);
} else if (exception instanceof MaxPayloadSizeExceededException maxPayloadSizeExceededException) {
handleMaxPayloadSizeExceededException(response, maxPayloadSizeExceededException);
} else if (exception instanceof DataAccessException e) {
handleDatabaseException(e, response);
} else if (exception instanceof DataAccessException || exception instanceof PersistenceException) {
handleDatabaseException(exception, response);
} else {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of(exception.getMessage(),
@ -217,8 +219,17 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
private void handleDatabaseException(Throwable databaseException, HttpServletResponse response) throws IOException {
ThingsboardErrorResponse errorResponse;
if (databaseException instanceof ConstraintViolationException) {
errorResponse = ThingsboardErrorResponse.of(ExceptionUtils.getRootCause(databaseException).getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST);
ConstraintViolationException constraintViolationException = DaoUtil.extractConstraintViolation(databaseException);
if (constraintViolationException != null) {
log.debug("Constraint violation: {}", ExceptionUtils.getRootCauseMessage(databaseException));
String constraintName = constraintViolationException.getConstraintName();
String userMessage;
if (constraintName != null && !constraintName.isEmpty()) {
userMessage = "Constraint violation: " + constraintName;
} else {
userMessage = "Constraint violation";
}
errorResponse = ThingsboardErrorResponse.of(userMessage, ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST);
} else {
log.warn("Database error: {} - {}", databaseException.getClass().getSimpleName(), ExceptionUtils.getRootCauseMessage(databaseException));
errorResponse = ThingsboardErrorResponse.of("Database error", ThingsboardErrorCode.DATABASE, HttpStatus.INTERNAL_SERVER_ERROR);

2
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -1162,7 +1162,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
assertThat(findRelationsByTo(entityTo)).hasSize(1);
doDelete(urlDelete)
.andExpect(status().isInternalServerError());
.andExpect(status().isBadRequest());
assertThat(findRelationsByTo(entityTo)).hasSize(1);
} finally {

21
application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java

@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.exception.TenantNotFoundException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -936,6 +937,26 @@ public class TenantControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService);
}
@Test
public void testSaveTenantWithNonExistentTenantProfileId() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
tenant.setTenantProfileId(new TenantProfileId(UUID.randomUUID()));
String responseBody = doPost("/api/tenant", tenant)
.andExpect(status().isBadRequest())
.andReturn().getResponse().getContentAsString();
// Verify sanitized message format
assertThat(responseBody).contains("Constraint violation: fk_tenant_profile");
// Verify raw SQL details are not returned
assertThat(responseBody).doesNotContain("could not execute statement");
assertThat(responseBody).doesNotContain("insert or update on table");
assertThat(responseBody).doesNotContain("tenant_profile_id");
assertThat(responseBody).doesNotContain("is not present in table");
}
private void testBroadcastEntityStateChangeEventNeverTenant() {
Mockito.verify(tbClusterService, never()).onTenantChange(Mockito.any(Tenant.class),
Mockito.isNull());

10
dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao;
import org.hibernate.exception.ConstraintViolationException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@ -202,4 +203,13 @@ public final class DaoUtil {
.collect(Collectors.toList());
}
public static ConstraintViolationException extractConstraintViolation(Throwable t) {
if (t instanceof ConstraintViolationException cve) {
return cve;
} else if (t != null && t.getCause() instanceof ConstraintViolationException cve) {
return cve;
}
return null;
}
}

3
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.html

@ -45,8 +45,7 @@
<tb-toggle-option value="markers" [error]="mapSettingsFormGroup.get('markers').invalid ? ('common.required-fields' | translate) : null">{{ 'widgets.maps.overlays.markers' | translate }}</tb-toggle-option>
<tb-toggle-option value="polygons" [error]="mapSettingsFormGroup.get('polygons').invalid ? ('common.required-fields' | translate) : null">{{ 'widgets.maps.overlays.polygons' | translate }}</tb-toggle-option>
<tb-toggle-option value="circles" [error]="mapSettingsFormGroup.get('circles').invalid ? ('common.required-fields' | translate) : null">{{ 'widgets.maps.overlays.circles' | translate }}</tb-toggle-option>
//todo translation
<tb-toggle-option value="polylines" [error]="mapSettingsFormGroup.get('polylines').invalid ? ('common.required-fields' | translate) : null"> Polylines </tb-toggle-option>
<tb-toggle-option value="polylines" [error]="mapSettingsFormGroup.get('polylines').invalid ? ('common.required-fields' | translate) : null">{{ 'widgets.maps.overlays.polylines' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
<tb-map-data-layers *ngIf="trip"

Loading…
Cancel
Save