Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard into feature/telemetry-service-refactoring

pull/12297/head
ViacheslavKlimov 2 years ago
parent
commit
aeb7427833
  1. 2
      application/pom.xml
  2. 4
      application/src/main/data/json/system/scada_symbols/filter-hp.svg
  3. 4
      application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg
  4. 4
      application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg
  5. 4
      application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg
  6. 4
      application/src/main/data/json/system/scada_symbols/pool-hp.svg
  7. 4
      application/src/main/data/json/system/scada_symbols/pump-hp.svg
  8. 4
      application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg
  9. 4
      application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg
  10. 4
      application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg
  11. 4
      application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg
  12. 1
      application/src/main/data/json/system/widget_bundles/buttons.json
  13. 1
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  14. 2
      application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json
  15. 2
      application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json
  16. 43
      application/src/main/data/json/system/widget_types/two_segment_button.json
  17. 21
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  18. 36
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  19. 39
      application/src/main/java/org/thingsboard/server/controller/ImageController.java
  20. 2
      application/src/main/java/org/thingsboard/server/controller/NotificationController.java
  21. 9
      application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java
  22. 11
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/HousekeeperTaskProcessor.java
  23. 2
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/LatestTsDeletionTaskProcessor.java
  24. 2
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/TsHistoryDeletionTaskProcessor.java
  25. 4
      application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java
  26. 4
      application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
  27. 15
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  28. 2
      application/src/main/resources/thingsboard.yml
  29. 20
      application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java
  30. 58
      application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java
  31. 2
      common/actor/pom.xml
  32. 2
      common/cache/pom.xml
  33. 2
      common/cluster-api/pom.xml
  34. 2
      common/coap-server/pom.xml
  35. 2
      common/dao-api/pom.xml
  36. 8
      common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java
  37. 2
      common/data/pom.xml
  38. 3
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java
  39. 2
      common/edge-api/pom.xml
  40. 2
      common/message/pom.xml
  41. 2
      common/pom.xml
  42. 2
      common/proto/pom.xml
  43. 2
      common/queue/pom.xml
  44. 2
      common/script/pom.xml
  45. 2
      common/script/remote-js-client/pom.xml
  46. 2
      common/script/script-api/pom.xml
  47. 2
      common/stats/pom.xml
  48. 2
      common/transport/coap/pom.xml
  49. 2
      common/transport/http/pom.xml
  50. 2
      common/transport/lwm2m/pom.xml
  51. 2
      common/transport/mqtt/pom.xml
  52. 2
      common/transport/pom.xml
  53. 2
      common/transport/snmp/pom.xml
  54. 2
      common/transport/transport-api/pom.xml
  55. 2
      common/util/pom.xml
  56. 2
      common/version-control/pom.xml
  57. 2
      dao/pom.xml
  58. 11
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  59. 2
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java
  60. 126
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java
  61. 14
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java
  62. 18
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java
  63. 9
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java
  64. 2
      monitoring/pom.xml
  65. 2
      msa/black-box-tests/pom.xml
  66. 6
      msa/js-executor/api/jsInvokeMessageProcessor.ts
  67. 2
      msa/js-executor/package.json
  68. 2
      msa/js-executor/pom.xml
  69. 2
      msa/monitoring/pom.xml
  70. 2
      msa/pom.xml
  71. 2
      msa/tb-node/pom.xml
  72. 3
      msa/tb/docker/install-tb.sh
  73. 13
      msa/tb/docker/upgrade-tb.sh
  74. 3
      msa/tb/pom.xml
  75. 2
      msa/transport/coap/pom.xml
  76. 2
      msa/transport/http/pom.xml
  77. 2
      msa/transport/lwm2m/pom.xml
  78. 2
      msa/transport/mqtt/pom.xml
  79. 2
      msa/transport/pom.xml
  80. 2
      msa/transport/snmp/pom.xml
  81. 2
      msa/vc-executor-docker/pom.xml
  82. 2
      msa/vc-executor/pom.xml
  83. 2
      msa/web-ui/package.json
  84. 2
      msa/web-ui/pom.xml
  85. 4
      netty-mqtt/pom.xml
  86. 32
      pom.xml
  87. 2
      rest-client/pom.xml
  88. 2
      rule-engine/pom.xml
  89. 2
      rule-engine/rule-engine-api/pom.xml
  90. 4
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java
  91. 2
      rule-engine/rule-engine-components/pom.xml
  92. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/SemaphoreWithTbMsgQueue.java
  93. 2
      tools/pom.xml
  94. 2
      transport/coap/pom.xml
  95. 2
      transport/http/pom.xml
  96. 2
      transport/lwm2m/pom.xml
  97. 2
      transport/mqtt/pom.xml
  98. 2
      transport/pom.xml
  99. 2
      transport/snmp/pom.xml
  100. 2
      ui-ngx/package.json

2
application/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>application</artifactId>

4
application/src/main/data/json/system/scada_symbols/filter-hp.svg

@ -163,8 +163,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

4
application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg

@ -306,8 +306,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

4
application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg

@ -247,8 +247,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

4
application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg

@ -272,8 +272,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

4
application/src/main/data/json/system/scada_symbols/pool-hp.svg

@ -247,8 +247,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

4
application/src/main/data/json/system/scada_symbols/pump-hp.svg

@ -206,8 +206,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

4
application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg

@ -279,8 +279,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

4
application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg

@ -247,8 +247,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

4
application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg

@ -247,8 +247,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

4
application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg

@ -272,8 +272,8 @@
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"name": "{i18n:scada.symbol.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

1
application/src/main/data/json/system/widget_bundles/buttons.json

@ -11,6 +11,7 @@
"action_button",
"command_button",
"toggle_button",
"two_segment_button",
"power_button"
]
}

1
application/src/main/data/json/system/widget_bundles/control_widgets.json

@ -11,6 +11,7 @@
"single_switch",
"command_button",
"toggle_button",
"two_segment_button",
"power_button",
"slider",
"control_widgets.switch_control",

2
application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json

@ -5,7 +5,7 @@
"image": "tb-image:aHBfc2NhZGFfZ2VuZXJhbF9zeW1ib2xzX3N5c3RlbV9idW5kbGVfaW1hZ2UucG5n:IkdlbmVyYWwgaGlnaC1wZXJmb3JtYW5jZSBTQ0FEQSBzdW1ib2xzIiBzeXN0ZW0gYnVuZGxlIGltYWdl:SU1BR0U=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAA8FBMVEXe3t7f39/f39/X19cAAADf39/e3t7d3d3e3t6VlZXI3/eZmZlmZmbGxsbr6+vc3Nytra3Y2Nikrri3xtZ9fX0aGho1NTW5ubnS0tKUlJS/v7+urq6zs7Pa2tqJiYm0tLR5eXnPz8/b29t8fHyhoaJxcXImJia+vr5WVlZjY2NNTU0wMDCpqamzx9zCx9GxsbGEhIRZWVlubm7Q09jJycmLmbhvg6tBQUG3ydvR0dEbPoW90+qzu8spSosNM3+nsMWZpb6TlpkAKHhhZWpfX19UVFRGRkbU1NR9jrKmpqZTbJ43VZFdZm5qampFYZh4foRgw9f8AAAACHRSTlPvvyAgAK+wr43PeosAAAVgSURBVHja7d1pV9pAGIZhuj9TMw1t02RiZawkYYlKRSm21qWb3Zf//286Y8RCgBgIlBecWwkh+sHrvIF45hyldPfOrRKWvXt37pbuLD9DpRgPsBKtwGmVVFoVCAyEWjcWsreftAdiTQp5YSU5IJaBgFgGAmIZCIhlICCWgWDSBJ9hYnEQyWbboiD8OWZayNFruSFlAzEQopCd9aSdohAh9NHFQbYeJW0UhVgMgIv+IrHMEMFCACHjCGtSS8pltS9CIBTcC0OhH+odshDJOXfBY2ZJRFHoiucJxI3kof4KXM4OWVRzpAu9E1OFxJZluXAc7ikOLx8+h8uRTEnKS4iFqx24gigkObWstuNIRBZz+yDMGQGJOWmIjACOGvBvIgIWEy5Q64O01Q7VUyuBIIpjCcc9jEPImOtj8WF8cbh/Iq7lSqKQsbkYjll0X35XH7JsF0QDoQKxbegPfX+5kxzVx5cI8uXbWkbfHkMlvPWMNr9QgNgv1zLzbAAeMvNmA3nyOunJNBDb3smGbEBJ1pHZ5mwgTx8mTQXBtRDbtpcAYg9ORH7+LFOQl2mI/8tH53zTiShB7BRkW0rX3/8kMyAbEZwd/1f9fJMYpNoPqalPue1sZ0BkiB3Hj+rn9XlAdn++nQqCFGRbQz4rTx+kqiB9P7TvY0duSHzCHCC778XHt7OYSCOOG2mIPQgRn/zzzpwgu+8BJZkKUlWQvl44a75cczImgo66/okO6nOA/IGK/yx4aqVLQ9IRm0gOSHX0dYTSc8SGgpSvn4jt/a8L4m70duqX33o2ZN1WdbaQUaez8OsIFKTCvfHVOxUNwdambuNis6Fv+rNXx6YBqTzOqFK1dehlA7Yq2VxFAZIlqVQqVQUBbPTXUxCDXEiuMMlepefoDcQecCRbOpAQl5JqtaI0A1Wr6qbSCsDGUIQmAnaVPz6WO4lFQZLs60LeFgyBrS1pD3r3SwS5tOhN8uDKhjyRggxVCEEKck0GMj4DKZSBjM9ACmUg41tRyMFZCxe1sPjyQR4jaQDSPf1+1AXw9fTkAAsvH8QPkdQHaZ220PpwgFdHFAaSE7JnRS+h6occvAOgID9eteYh4Zik/BDL2tsahOi634HTk5OjLmZesO0jf/khOn8rBXmnz6oP73BweoxZFzx7lp8yIcSKvAHI8UkLCtKCOr1QrDfldM1nzzSlPBRHYcjeOvohrZNjQCHOgKOzYoyoIVmqtobUApZONpuiGGT/ZerJfvS72+2eqfPr6/cTFKrpY6hAM95gRKxZBLIfPkYK8kp3DBx3vx6gSLyJERDFGOfm00PkFpCCzK6QYThvHANgDKlyQxRjjhDGCn8/jV8aDURlIAZiIAYyPwhjBmIgBmIgBmIgU0I4Y80mY2LpIaJ2sSzCMZwMMFFNb5EQBBoSYETbNYEJ8hpYKETUxgzEV778611h0OazgBzvJoWYuGDcQNTCj8i93sXKwCwg6yLpG6YYybiBjBGi6SNnRf4QZvKCaMxAkpFkr3dRgjCG4XgQNBpB4F273kUdknXcQAzEQAzEQAzEQAzkhkG8etIarlpOyIj/r2UgNwMytHy0rJCh5SNCEC/KXKfKXj6iBEHDG+lr5Fk+IgXhjWbWOlX28hElCFBmQ5VzLx9RgkxUECFvtCFkryOXGUhGBmIgKgPJ6CZDGkkBpo4GhGw3FSJCDsBjZVBrQojF2oBz6FiuAK0mhLTDNrhGtCVoNSGEMw7Z1jsctCph4hxyb9kxLSQCwaaAhDEASW0sU0DgSlF2V+A5Am7F7ecg1k29stPNQKhlINRamTfWLd3CSnS/dHslRqIYd2/fw7JXunX77l/CoXsjeoRJ3gAAAABJRU5ErkJggg==",
"scada": true,
"description": "Bundle with high-performance SCADA symbols",
"order": 9500,
"order": 9400,
"name": "General high-performance SCADA symbols"
},
"widgetTypeFqns": [

2
application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json

File diff suppressed because one or more lines are too long

43
application/src/main/data/json/system/widget_types/two_segment_button.json

File diff suppressed because one or more lines are too long

21
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ListenableFuture;
import jakarta.mail.MessagingException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolation;
import lombok.Getter;
@ -28,6 +29,7 @@ import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@ -182,7 +184,9 @@ import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService;
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -194,6 +198,7 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;
import static org.thingsboard.server.common.data.StringUtils.isNotEmpty;
import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD;
@ -1008,6 +1013,22 @@ public abstract class BaseController {
}
}
protected void compressResponseWithGzipIFAccepted(String acceptEncodingHeader, HttpServletResponse response, byte[] content) throws IOException {
if (StringUtils.isNotEmpty(acceptEncodingHeader) && acceptEncodingHeader.contains("gzip")) {
response.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream())) {
gzipOutputStream.write(content);
gzipOutputStream.finish();
}
} else {
try (ServletOutputStream outputStream = response.getOutputStream()) {
outputStream.write(content);
outputStream.flush();
}
}
}
protected <T> ResponseEntity<T> response(HttpStatus status) {
return ResponseEntity.status(status).build();
}

36
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -22,8 +22,10 @@ import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
@ -31,6 +33,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@ -67,6 +70,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_ID_PARAM_DESCRIPTION;
@ -149,17 +153,20 @@ public class DashboardController extends BaseController {
)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/dashboard/{dashboardId}")
public Dashboard getDashboardById(@Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
public void getDashboardById(@Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId,
@Parameter(description = INCLUDE_RESOURCES_DESCRIPTION)
@RequestParam(value = INCLUDE_RESOURCES, required = false) boolean includeResources) throws ThingsboardException {
@RequestParam(value = INCLUDE_RESOURCES, required = false) boolean includeResources,
@RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader,
HttpServletResponse response) throws Exception {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
if (includeResources) {
dashboard.setResources(tbResourceService.exportResources(dashboard, getCurrentUser()));
}
return dashboard;
response.setContentType(APPLICATION_JSON_VALUE);
compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(dashboard));
}
@ApiOperation(value = "Create Or Update Dashboard (saveDashboard)",
@ -171,11 +178,15 @@ public class DashboardController extends BaseController {
TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping(value = "/dashboard")
public Dashboard saveDashboard(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.")
@RequestBody Dashboard dashboard) throws Exception {
public void saveDashboard(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.")
@RequestBody Dashboard dashboard,
@RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader,
HttpServletResponse response) throws Exception {
dashboard.setTenantId(getTenantId());
checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
return tbDashboardService.save(dashboard, getCurrentUser());
var savedDashboard = tbDashboardService.save(dashboard, getCurrentUser());
response.setContentType(APPLICATION_JSON_VALUE);
compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(savedDashboard));
}
@ApiOperation(value = "Delete the Dashboard (deleteDashboard)",
@ -408,12 +419,13 @@ public class DashboardController extends BaseController {
"If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. "
+ DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/home", method = RequestMethod.GET)
@ResponseBody
public HomeDashboard getHomeDashboard() throws ThingsboardException {
@GetMapping(value = "/dashboard/home")
public void getHomeDashboard(@RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader,
HttpServletResponse response) throws Exception {
SecurityUser securityUser = getCurrentUser();
response.setContentType(APPLICATION_JSON_VALUE);
if (securityUser.isSystemAdmin()) {
return null;
return;
}
User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId());
JsonNode additionalInfo = user.getAdditionalInfo();
@ -431,7 +443,9 @@ public class DashboardController extends BaseController {
homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
}
}
return homeDashboard;
if (homeDashboard != null) {
compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(homeDashboard));
}
}
@ApiOperation(value = "Get Home Dashboard Info (getHomeDashboardInfo)",

39
application/src/main/java/org/thingsboard/server/controller/ImageController.java

@ -61,7 +61,9 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.io.ByteArrayOutputStream;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPOutputStream;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
@ -70,6 +72,7 @@ import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_INC
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.dao.util.ImageUtils.mediaTypeToFileExtension;
@Slf4j
@RestController
@ -179,15 +182,17 @@ public class ImageController extends BaseController {
@PathVariable String type,
@Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
return downloadIfChanged(type, key, etag, false);
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag,
@RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception {
return downloadIfChanged(type, key, etag, acceptEncodingHeader, false);
}
@GetMapping(value = "/api/images/public/{publicResourceKey}", produces = "image/*")
public ResponseEntity<ByteArrayResource> downloadPublicImage(@PathVariable String publicResourceKey,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag,
@RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception {
ImageCacheKey cacheKey = ImageCacheKey.forPublicImage(publicResourceKey);
return downloadIfChanged(cacheKey, etag, () -> imageService.getPublicImageInfoByKey(publicResourceKey));
return downloadIfChanged(cacheKey, etag, acceptEncodingHeader, () -> imageService.getPublicImageInfoByKey(publicResourceKey));
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@ -213,8 +218,9 @@ public class ImageController extends BaseController {
@PathVariable String type,
@Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
return downloadIfChanged(type, key, etag, true);
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag,
@RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception {
return downloadIfChanged(type, key, etag, acceptEncodingHeader, true);
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@ -268,12 +274,12 @@ public class ImageController extends BaseController {
return (result.isSuccess() ? ResponseEntity.ok() : ResponseEntity.badRequest()).body(result);
}
private ResponseEntity<ByteArrayResource> downloadIfChanged(String type, String key, String etag, boolean preview) throws Exception {
private ResponseEntity<ByteArrayResource> downloadIfChanged(String type, String key, String etag, String acceptEncodingHeader, boolean preview) throws Exception {
ImageCacheKey cacheKey = ImageCacheKey.forImage(getTenantId(type), key, preview);
return downloadIfChanged(cacheKey, etag, () -> checkImageInfo(type, key, Operation.READ));
return downloadIfChanged(cacheKey, etag, acceptEncodingHeader, () -> checkImageInfo(type, key, Operation.READ));
}
private ResponseEntity<ByteArrayResource> downloadIfChanged(ImageCacheKey cacheKey, String etag, ThrowingSupplier<TbResourceInfo> imageInfoSupplier) throws Exception {
private ResponseEntity<ByteArrayResource> downloadIfChanged(ImageCacheKey cacheKey, String etag, String acceptEncodingHeader, ThrowingSupplier<TbResourceInfo> imageInfoSupplier) throws Exception {
if (StringUtils.isNotEmpty(etag)) {
etag = StringUtils.remove(etag, '\"'); // etag is wrapped in double quotes due to HTTP specification
if (etag.equals(tbImageService.getETag(cacheKey))) {
@ -294,7 +300,6 @@ public class ImageController extends BaseController {
tbImageService.putETag(cacheKey, descriptor.getEtag());
var result = ResponseEntity.ok()
.header("Content-Type", descriptor.getMediaType())
.contentLength(data.length)
.eTag(descriptor.getEtag());
if (!cacheKey.isPublic()) {
result
@ -308,7 +313,19 @@ public class ImageController extends BaseController {
} else {
result.cacheControl(CacheControl.noCache());
}
return result.body(new ByteArrayResource(data));
var responseData = data;
if (mediaTypeToFileExtension(descriptor.getMediaType()).equals("svg") &&
StringUtils.isNotEmpty(acceptEncodingHeader) && acceptEncodingHeader.contains("gzip")) {
result.header(HttpHeaders.CONTENT_ENCODING, "gzip");
var outputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream)) {
gzipOutputStream.write(data);
gzipOutputStream.finish();
}
responseData = outputStream.toByteArray();
}
result.contentLength(responseData.length);
return result.body(new ByteArrayResource(responseData));
}
private TbResourceInfo checkImageInfo(String imageType, String key, Operation operation) throws ThingsboardException {

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

@ -477,7 +477,7 @@ public class NotificationController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/notification/deliveryMethods")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
public Set<NotificationDeliveryMethod> getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
public List<NotificationDeliveryMethod> getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
return notificationCenter.getAvailableDeliveryMethods(user.getTenantId());
}

9
application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java

@ -117,9 +117,10 @@ public class HousekeeperService {
throw new IllegalArgumentException("Unsupported task type " + taskType);
}
Future<Object> future = null;
try {
long startTs = System.currentTimeMillis();
Future<Object> future = taskExecutor.submit(() -> {
future = taskExecutor.submit(() -> {
taskProcessor.process((T) task);
return null;
});
@ -137,7 +138,7 @@ public class HousekeeperService {
if (e instanceof ExecutionException) {
error = e.getCause();
} else if (e instanceof TimeoutException) {
error = new TimeoutException("Timeout after " + config.getTaskProcessingTimeout() + " seconds");
error = new TimeoutException("Timeout after " + config.getTaskProcessingTimeout() + " ms");
}
if (msg.getTask().getAttempt() < config.getMaxReprocessingAttempts()) {
@ -153,6 +154,10 @@ public class HousekeeperService {
.build());
}
statsService.ifPresent(statsService -> statsService.reportFailure(taskType, msg));
} finally {
if (future != null && !future.isDone()) {
future.cancel(true);
}
}
}

11
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/HousekeeperTaskProcessor.java

@ -20,6 +20,8 @@ import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
import org.thingsboard.server.common.msg.housekeeper.HousekeeperClient;
import java.util.concurrent.Future;
public abstract class HousekeeperTaskProcessor<T extends HousekeeperTask> {
@Autowired
@ -29,4 +31,13 @@ public abstract class HousekeeperTaskProcessor<T extends HousekeeperTask> {
public abstract HousekeeperTaskType getTaskType();
public <V> V wait(Future<V> future) throws Exception {
try {
return future.get(); // will be interrupted after taskProcessingTimeout
} catch (InterruptedException e) {
future.cancel(true); // interrupting the underlying task
throw e;
}
}
}

2
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/LatestTsDeletionTaskProcessor.java

@ -33,7 +33,7 @@ public class LatestTsDeletionTaskProcessor extends HousekeeperTaskProcessor<Late
@Override
public void process(LatestTsDeletionHousekeeperTask task) throws Exception {
timeseriesService.removeLatest(task.getTenantId(), task.getEntityId(), List.of(task.getKey())).get();
wait(timeseriesService.removeLatest(task.getTenantId(), task.getEntityId(), List.of(task.getKey())));
log.debug("[{}][{}][{}] Deleted latest telemetry for key '{}'", task.getTenantId(), task.getEntityId().getEntityType(), task.getEntityId(), task.getKey());
}

2
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/TsHistoryDeletionTaskProcessor.java

@ -36,7 +36,7 @@ public class TsHistoryDeletionTaskProcessor extends HousekeeperTaskProcessor<TsH
@Override
public void process(TsHistoryDeletionHousekeeperTask task) throws Exception {
DeleteTsKvQuery deleteQuery = new BaseDeleteTsKvQuery(task.getKey(), 0, System.currentTimeMillis(), false, false);
timeseriesService.remove(task.getTenantId(), task.getEntityId(), List.of(deleteQuery)).get();
wait(timeseriesService.remove(task.getTenantId(), task.getEntityId(), List.of(deleteQuery)));
log.debug("[{}][{}][{}] Deleted timeseries history for key '{}'", task.getTenantId(), task.getEntityId().getEntityType(), task.getEntityId(), task.getKey());
}

4
application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java

@ -101,7 +101,7 @@ public class ResourcesUpdater {
for (DashboardId dashboardId : dashboards) {
executor.submit(() -> {
Dashboard dashboard = dashboardService.findDashboardById(TenantId.SYS_TENANT_ID, dashboardId);
boolean updated = resourceService.updateResourcesUsage(dashboard); // will convert resources ids to new structure
boolean updated = resourceService.updateResourcesUsage(dashboard.getTenantId(), dashboard); // will convert resources ids to new structure
if (updated) {
dashboardService.saveDashboard(dashboard);
updatedCount.incrementAndGet();
@ -130,7 +130,7 @@ public class ResourcesUpdater {
for (WidgetTypeId widgetTypeId : widgets) {
executor.submit(() -> {
WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId);
boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails);
boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails.getTenantId(), widgetTypeDetails);
if (updated) {
widgetTypeService.saveWidgetType(widgetTypeDetails);
updatedCount.incrementAndGet();

4
application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java

@ -417,7 +417,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
}
@Override
public Set<NotificationDeliveryMethod> getAvailableDeliveryMethods(TenantId tenantId) {
public List<NotificationDeliveryMethod> getAvailableDeliveryMethods(TenantId tenantId) {
return channels.values().stream()
.filter(channel -> {
try {
@ -428,7 +428,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
}
})
.map(NotificationChannel::getDeliveryMethod)
.collect(Collectors.toSet());
.sorted().toList();
}
@Override

15
application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java

@ -46,7 +46,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -132,12 +132,12 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
@Override
public List<ResourceExportData> exportResources(Dashboard dashboard, SecurityUser user) throws ThingsboardException {
return exportResources(dashboard, imageService::getUsedImages, resourceService::getUsedResources, user);
return exportResources(() -> imageService.getUsedImages(dashboard), () -> resourceService.getUsedResources(user.getTenantId(), dashboard), user);
}
@Override
public List<ResourceExportData> exportResources(WidgetTypeDetails widgetTypeDetails, SecurityUser user) throws ThingsboardException {
return exportResources(widgetTypeDetails, imageService::getUsedImages, resourceService::getUsedResources, user);
return exportResources(() -> imageService.getUsedImages(widgetTypeDetails), () -> resourceService.getUsedResources(user.getTenantId(), widgetTypeDetails), user);
}
@Override
@ -153,13 +153,12 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
}
}
private <T> List<ResourceExportData> exportResources(T entity,
Function<T, Collection<TbResourceInfo>> imagesProcessor,
Function<T, Collection<TbResourceInfo>> resourcesProcessor,
private <T> List<ResourceExportData> exportResources(Supplier<Collection<TbResourceInfo>> imagesProcessor,
Supplier<Collection<TbResourceInfo>> resourcesProcessor,
SecurityUser user) throws ThingsboardException {
List<TbResourceInfo> resources = new ArrayList<>();
resources.addAll(imagesProcessor.apply(entity));
resources.addAll(resourcesProcessor.apply(entity));
resources.addAll(imagesProcessor.get());
resources.addAll(resourcesProcessor.get());
for (TbResourceInfo resourceInfo : resources) {
accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.READ, resourceInfo.getId(), resourceInfo);
}

2
application/src/main/resources/thingsboard.yml

@ -206,7 +206,7 @@ ui:
# Help parameters
help:
# Base URL for UI help assets
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.9}"
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.0}"
# Database telemetry parameters
database:

20
application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java

@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -602,13 +603,14 @@ public class DashboardControllerTest extends AbstractControllerTest {
dashboard.setTitle("My dashboard");
dashboard.setConfiguration(JacksonUtil.newObjectNode()
.put("someImage", "tb-image;/api/images/tenant/" + imageInfo.getResourceKey())
.set("widgets", JacksonUtil.toJsonNode("""
.<ObjectNode>set("widgets", JacksonUtil.toJsonNode("""
{"xxx":
{"config":{"actions":{"elementClick":[
{"customResources":[{"url":{"entityType":"TB_RESOURCE","id":
"tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js"},"isModule":true},
{"url":"tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js","isModule":true}]}]}}}}
""")));
"""))
.put("someResource", "tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js"));
dashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
Dashboard exportedDashboard = doGet("/api/dashboard/" + dashboard.getUuidId() + "?includeResources=true", Dashboard.class);
@ -637,12 +639,18 @@ public class DashboardControllerTest extends AbstractControllerTest {
doPost("/api/resource", resource, TbResourceInfo.class);
Dashboard importedDashboard = doPost("/api/dashboard", exportedDashboard, Dashboard.class);
String newResourceKey = "gateway-management-extension_(1).js";
imageRef = importedDashboard.getConfiguration().get("someImage").asText();
assertThat(imageRef).isEqualTo("tb-image;/api/images/tenant/" + imageInfo.getResourceKey());
resourceRef = importedDashboard.getConfiguration().get("widgets").get("xxx").get("config")
.get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText();
String newResourceKey = "gateway-management-extension_(1).js";
assertThat(resourceRef).isEqualTo("tb-resource;/api/resource/js_module/tenant/" + newResourceKey);
List<String> resourcesRefs = new ArrayList<>();
resourcesRefs.add(importedDashboard.getConfiguration().get("widgets").get("xxx").get("config")
.get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText());
resourcesRefs.add(importedDashboard.getConfiguration().get("someResource").asText());
assertThat(resourcesRefs).allSatisfy(ref -> {
assertThat(ref).isEqualTo("tb-resource;/api/resource/js_module/tenant/" + newResourceKey);
});
TbResourceInfo importedImageInfo = doGet("/api/images/tenant/" + imageInfo.getResourceKey() + "/info", TbResourceInfo.class);
assertThat(importedImageInfo.getEtag()).isEqualTo(imageInfo.getEtag());

58
application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java

@ -93,8 +93,12 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -103,6 +107,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
@ -113,7 +118,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@TestPropertySource(properties = {
"queue.core.housekeeper.task-reprocessing-delay-ms=2000",
"queue.core.housekeeper.poll-interval-ms=1000",
"queue.core.housekeeper.max-reprocessing-attempts=5"
"queue.core.housekeeper.max-reprocessing-attempts=5",
"queue.core.housekeeper.task-processing-timeout-ms=5000",
})
public class HousekeeperServiceTest extends AbstractControllerTest {
@ -320,7 +326,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
@Test
public void whenTaskProcessingFails_thenReprocess() throws Exception {
TimeoutException error = new TimeoutException("Test timeout");
Exception error = new RuntimeException("Just a test");
doThrow(error).when(tsHistoryDeletionTaskProcessor).process(any());
Device device = createDevice("test", "test");
@ -344,6 +350,54 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
});
}
@Test
public void whenTaskProcessingTimedOut_thenInterruptAndReprocess() throws Exception {
ExecutorService someExecutor = Executors.newSingleThreadExecutor();
AtomicBoolean taskInterrupted = new AtomicBoolean(false);
AtomicBoolean underlyingTaskInterrupted = new AtomicBoolean(false);
doAnswer(invocationOnMock -> {
Future<?> future = someExecutor.submit(() -> {
try {
Thread.sleep(TimeUnit.HOURS.toMillis(24));
} catch (InterruptedException e) {
underlyingTaskInterrupted.set(true);
}
});
try {
future.get();
} catch (InterruptedException e) {
taskInterrupted.set(true);
future.cancel(true);
throw e;
}
return null;
}).when(tsHistoryDeletionTaskProcessor).process(any());
Device device = createDevice("test", "test");
createRelatedData(device.getId());
doDelete("/api/device/" + device.getId()).andExpect(status().isOk());
int attempts = 2;
await().atMost(30, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> {
for (int i = 0; i <= attempts; i++) {
int attempt = i;
verify(housekeeperReprocessingService).submitForReprocessing(argThat(getTaskMatcher(device.getId(), HousekeeperTaskType.DELETE_TS_HISTORY,
task -> task.getAttempt() == attempt)), argThat(error -> error instanceof TimeoutException));
}
});
assertThat(taskInterrupted).isTrue();
assertThat(underlyingTaskInterrupted).isTrue();
assertThat(getTimeseriesHistory(device.getId())).isNotEmpty();
doCallRealMethod().when(tsHistoryDeletionTaskProcessor).process(any());
someExecutor.shutdown();
await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> {
assertThat(getTimeseriesHistory(device.getId())).isEmpty();
});
}
@Test
public void whenReprocessingAttemptsExceeded_thenDropTheTask() throws Exception {
TimeoutException error = new TimeoutException("Test timeout");

2
common/actor/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/cache/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/cluster-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/coap-server/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/dao-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

8
common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java

@ -80,13 +80,13 @@ public interface ResourceService extends EntityDaoService {
TbResourceInfo findSystemOrTenantResourceByEtag(TenantId tenantId, ResourceType resourceType, String etag);
boolean updateResourcesUsage(Dashboard dashboard);
boolean updateResourcesUsage(TenantId tenantId, Dashboard dashboard);
boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails);
boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetTypeDetails);
Collection<TbResourceInfo> getUsedResources(Dashboard dashboard);
Collection<TbResourceInfo> getUsedResources(TenantId tenantId, Dashboard dashboard);
Collection<TbResourceInfo> getUsedResources(WidgetTypeDetails widgetTypeDetails);
Collection<TbResourceInfo> getUsedResources(TenantId tenantId, WidgetTypeDetails widgetTypeDetails);
TbResource createOrUpdateSystemResource(ResourceType resourceType, String resourceKey, byte[] data);

2
common/data/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

3
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java

@ -50,7 +50,8 @@ public class WidgetType extends BaseWidgetType {
@JsonIgnore
public JsonNode getDefaultConfig() {
return Optional.ofNullable(descriptor.get("defaultConfig"))
return Optional.ofNullable(descriptor)
.map(descriptor -> descriptor.get("defaultConfig"))
.filter(JsonNode::isTextual).map(JsonNode::asText)
.map(json -> {
try {

2
common/edge-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/message/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>common</artifactId>

2
common/proto/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/queue/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/script/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/script/remote-js-client/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>script</artifactId>
</parent>
<groupId>org.thingsboard.common.script</groupId>

2
common/script/script-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>script</artifactId>
</parent>
<groupId>org.thingsboard.common.script</groupId>

2
common/stats/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>

2
common/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>

2
common/transport/lwm2m/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>

2
common/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>

2
common/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/transport/snmp/pom.xml

@ -21,7 +21,7 @@
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>

2
common/transport/transport-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>

2
common/util/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/version-control/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
dao/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>dao</artifactId>

11
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -162,18 +162,19 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
dashboardValidator.validate(dashboard, DashboardInfo::getTenantId);
}
try {
TenantId tenantId = dashboard.getTenantId();
if (CollectionUtils.isNotEmpty(dashboard.getResources())) {
resourceService.importResources(dashboard.getTenantId(), dashboard.getResources());
resourceService.importResources(tenantId, dashboard.getResources());
}
imageService.updateImagesUsage(dashboard);
resourceService.updateResourcesUsage(dashboard);
resourceService.updateResourcesUsage(tenantId, dashboard);
var saved = dashboardDao.save(dashboard.getTenantId(), dashboard);
var saved = dashboardDao.save(tenantId, dashboard);
publishEvictEvent(new DashboardTitleEvictEvent(saved.getId()));
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId())
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(saved.getId()).created(dashboard.getId() == null).build());
if (dashboard.getId() == null) {
countService.publishCountEntityEvictEvent(saved.getTenantId(), EntityType.DASHBOARD);
countService.publishCountEntityEvictEvent(tenantId, EntityType.DASHBOARD);
}
return saved;
} catch (Exception e) {

2
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java

@ -64,7 +64,7 @@ public class OAuth2ClientServiceImpl extends AbstractEntityService implements OA
@Override
public List<OAuth2ClientLoginInfo> findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType(String pkgName, PlatformType platformType) {
log.trace("Executing findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType pkgName=[{}] platformType=[{}]",pkgName, platformType);
log.trace("Executing findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType pkgName=[{}] platformType=[{}]", pkgName, platformType);
return oauth2ClientDao.findEnabledByPkgNameAndPlatformType(pkgName, platformType)
.stream()
.map(OAuth2Utils::toClientLoginInfo)

126
dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java

@ -57,6 +57,7 @@ import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.service.validator.ResourceDataValidator;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
@ -401,18 +402,33 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
}
@Override
public boolean updateResourcesUsage(Dashboard dashboard) {
public boolean updateResourcesUsage(TenantId tenantId, Dashboard dashboard) {
if (dashboard.getConfiguration() == null) {
return false;
}
Map<String, String> links = getResourcesLinks(dashboard.getResources());
return updateResourcesUsage(dashboard.getTenantId(), dashboard.getConfiguration(), DASHBOARD_RESOURCES_MAPPING, links);
return updateResourcesUsage(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links);
}
@Override
public boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails) {
public boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) {
Map<String, String> links = getResourcesLinks(widgetTypeDetails.getResources());
boolean updated = updateResourcesUsage(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING, links);
List<JsonNode> jsonNodes = new ArrayList<>(2);
List<Map<String, String>> mappings = new ArrayList<>(2);
if (widgetTypeDetails.getDescriptor() != null) {
jsonNodes.add(widgetTypeDetails.getDescriptor());
mappings.add(WIDGET_RESOURCES_MAPPING);
}
JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig();
if (defaultConfig != null) {
updated |= updateResourcesUsage(widgetTypeDetails.getTenantId(), defaultConfig, WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING, links);
jsonNodes.add(defaultConfig);
mappings.add(WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING);
}
boolean updated = updateResourcesUsage(tenantId, jsonNodes, mappings, links);
if (defaultConfig != null) {
widgetTypeDetails.setDefaultConfig(defaultConfig);
}
return updated;
@ -433,8 +449,9 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
return links;
}
private boolean updateResourcesUsage(TenantId tenantId, JsonNode jsonNode, Map<String, String> mapping, Map<String, String> links) {
return processResources(jsonNode, mapping, value -> {
private boolean updateResourcesUsage(TenantId tenantId, List<JsonNode> jsonNodes, List<Map<String, String>> mappings, Map<String, String> links) {
log.trace("[{}] updateResourcesUsage (new links: {}) for {}", tenantId, links, jsonNodes);
return processResources(jsonNodes, mappings, value -> {
String link = getResourceLink(value);
if (link != null) {
String newLink = links.get(link);
@ -462,23 +479,31 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
}
@Override
public Collection<TbResourceInfo> getUsedResources(Dashboard dashboard) {
return getUsedResources(dashboard.getTenantId(), dashboard.getConfiguration(), DASHBOARD_RESOURCES_MAPPING).values();
public Collection<TbResourceInfo> getUsedResources(TenantId tenantId, Dashboard dashboard) {
return getUsedResources(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING)).values();
}
@Override
public Collection<TbResourceInfo> getUsedResources(WidgetTypeDetails widgetTypeDetails) {
Map<TbResourceId, TbResourceInfo> resources = getUsedResources(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING);
public Collection<TbResourceInfo> getUsedResources(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) {
List<JsonNode> jsonNodes = new ArrayList<>(2);
List<Map<String, String>> mappings = new ArrayList<>(2);
jsonNodes.add(widgetTypeDetails.getDescriptor());
mappings.add(WIDGET_RESOURCES_MAPPING);
JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig();
if (defaultConfig != null) {
resources.putAll(getUsedResources(widgetTypeDetails.getTenantId(), defaultConfig, WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING));
jsonNodes.add(defaultConfig);
mappings.add(WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING);
}
return resources.values();
return getUsedResources(tenantId, jsonNodes, mappings).values();
}
private Map<TbResourceId, TbResourceInfo> getUsedResources(TenantId tenantId, JsonNode jsonNode, Map<String, String> mapping) {
private Map<TbResourceId, TbResourceInfo> getUsedResources(TenantId tenantId, List<JsonNode> jsonNodes, List<Map<String, String>> mappings) {
Map<TbResourceId, TbResourceInfo> resources = new HashMap<>();
processResources(jsonNode, mapping, value -> {
log.trace("[{}] getUsedResources for {}", tenantId, jsonNodes);
processResources(jsonNodes, mappings, value -> {
String link = getResourceLink(value);
if (link == null) {
return value;
@ -517,32 +542,57 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
}
}
private boolean processResources(JsonNode jsonNode, Map<String, String> mapping, UnaryOperator<String> processor) {
private boolean processResources(List<JsonNode> jsonNodes, List<Map<String, String>> mappings, UnaryOperator<String> processor) {
AtomicBoolean updated = new AtomicBoolean(false);
JacksonUtil.replaceByMapping(jsonNode, mapping, Collections.emptyMap(), (name, urlNode) -> {
String value = null;
if (urlNode.isTextual()) { // link is in the right place
value = urlNode.asText();
} else {
JsonNode id = urlNode.get("id"); // old structure is used
if (id != null && id.isTextual()) {
value = id.asText();
}
}
if (StringUtils.isNotBlank(value)) {
value = processor.apply(value);
} else {
value = "";
}
for (int i = 0; i < jsonNodes.size(); i++) {
JsonNode jsonNode = jsonNodes.get(i);
// processing by mappings first
if (i <= mappings.size() - 1) {
JacksonUtil.replaceByMapping(jsonNode, mappings.get(i), Collections.emptyMap(), (name, urlNode) -> {
String value = null;
if (urlNode.isTextual()) { // link is in the right place
value = urlNode.asText();
} else {
JsonNode id = urlNode.get("id"); // old structure is used
if (id != null && id.isTextual()) {
value = id.asText();
}
}
JsonNode newValue = new TextNode(value);
if (!newValue.toString().equals(urlNode.toString())) {
updated.set(true);
log.trace("Replaced '{}' with '{}'", urlNode, newValue);
if (StringUtils.isNotBlank(value)) {
value = processor.apply(value);
} else {
value = "";
}
JsonNode newValue = new TextNode(value);
if (!newValue.toString().equals(urlNode.toString())) {
updated.set(true);
log.trace("Replaced by mapping '{}' ({}) with '{}'", value, name, newValue);
}
return newValue;
});
}
return newValue;
});
// processing all
JacksonUtil.replaceAll(jsonNode, "", (name, value) -> {
if (!StringUtils.startsWith(value, DataConstants.TB_RESOURCE_PREFIX + "/api/resource/")) {
return value;
}
String newValue = processor.apply(value);
if (StringUtils.equals(value, newValue)) {
return value;
} else {
updated.set(true);
log.trace("Replaced '{}' ({}) with '{}'", value, name, newValue);
return newValue;
}
});
}
return updated.get();
}
@ -555,7 +605,7 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
importResources(dashboard.getTenantId(), dashboard.getResources());
}
imageService.updateImagesUsage(dashboard);
updateResourcesUsage(dashboard);
updateResourcesUsage(dashboard.getTenantId(), dashboard);
data = JacksonUtil.writeValueAsBytes(dashboard);
}

14
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java

@ -31,6 +31,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2ClientDao;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@ -65,8 +66,17 @@ public class JpaOAuth2ClientDao extends JpaAbstractDao<OAuth2ClientEntity, OAuth
@Override
public List<OAuth2Client> findEnabledByPkgNameAndPlatformType(String pkgName, PlatformType platformType) {
return DaoUtil.convertDataList(repository.findEnabledByPkgNameAndPlatformType(pkgName,
platformType != null ? platformType.name() : null));
List<OAuth2ClientEntity> clientEntities;
if (platformType != null) {
clientEntities = switch (platformType) {
case ANDROID -> repository.findEnabledByAndroidPkgNameAndPlatformType(pkgName, platformType.name());
case IOS -> repository.findEnabledByIosPkgNameAndPlatformType(pkgName, platformType.name());
default -> Collections.emptyList();
};
} else {
clientEntities = Collections.emptyList();
}
return DaoUtil.convertDataList(clientEntities);
}
@Override

18
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java

@ -49,11 +49,21 @@ public interface OAuth2ClientRepository extends JpaRepository<OAuth2ClientEntity
"FROM OAuth2ClientEntity c " +
"LEFT JOIN MobileAppBundleOauth2ClientEntity ac ON c.id = ac.oauth2ClientId " +
"LEFT JOIN MobileAppBundleEntity b ON ac.mobileAppBundleId = b.id " +
"LEFT JOIN MobileAppEntity andApp ON b.androidAppId = andApp.id LEFT JOIN MobileAppEntity iosApp ON b.iosAppID = iosApp.id " +
"WHERE andApp.pkgName = :pkgName OR iosApp.pkgName = :pkgName AND b.oauth2Enabled = true " +
"LEFT JOIN MobileAppEntity andApp ON b.androidAppId = andApp.id " +
"WHERE andApp.pkgName = :pkgName AND b.oauth2Enabled = true " +
"AND (:platformFilter IS NULL OR c.platforms IS NULL OR c.platforms = '' OR ilike(c.platforms, CONCAT('%', :platformFilter, '%')) = true)")
List<OAuth2ClientEntity> findEnabledByPkgNameAndPlatformType(@Param("pkgName") String pkgName,
@Param("platformFilter") String platformFilter);
List<OAuth2ClientEntity> findEnabledByAndroidPkgNameAndPlatformType(@Param("pkgName") String pkgName,
@Param("platformFilter") String platformFilter);
@Query("SELECT c " +
"FROM OAuth2ClientEntity c " +
"LEFT JOIN MobileAppBundleOauth2ClientEntity ac ON c.id = ac.oauth2ClientId " +
"LEFT JOIN MobileAppBundleEntity b ON ac.mobileAppBundleId = b.id " +
"LEFT JOIN MobileAppEntity iosApp ON b.iosAppID = iosApp.id " +
"WHERE iosApp.pkgName = :pkgName AND b.oauth2Enabled = true " +
"AND (:platformFilter IS NULL OR c.platforms IS NULL OR c.platforms = '' OR ilike(c.platforms, CONCAT('%', :platformFilter, '%')) = true)")
List<OAuth2ClientEntity> findEnabledByIosPkgNameAndPlatformType(@Param("pkgName") String pkgName,
@Param("platformFilter") String platformFilter);
@Query("SELECT c " +
"FROM OAuth2ClientEntity c " +

9
dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java

@ -99,14 +99,15 @@ public class WidgetTypeServiceImpl implements WidgetTypeService {
log.trace("Executing saveWidgetType [{}]", widgetTypeDetails);
widgetTypeValidator.validate(widgetTypeDetails, WidgetType::getTenantId);
try {
TenantId tenantId = widgetTypeDetails.getTenantId();
if (CollectionUtils.isNotEmpty(widgetTypeDetails.getResources())) {
resourceService.importResources(widgetTypeDetails.getTenantId(), widgetTypeDetails.getResources());
resourceService.importResources(tenantId, widgetTypeDetails.getResources());
}
imageService.updateImagesUsage(widgetTypeDetails);
resourceService.updateResourcesUsage(widgetTypeDetails);
resourceService.updateResourcesUsage(tenantId, widgetTypeDetails);
WidgetTypeDetails result = widgetTypeDao.save(widgetTypeDetails.getTenantId(), widgetTypeDetails);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(result.getTenantId())
WidgetTypeDetails result = widgetTypeDao.save(tenantId, widgetTypeDetails);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(result.getId()).created(widgetTypeDetails.getId() == null).build());
return result;
} catch (Exception t) {

2
monitoring/pom.xml

@ -21,7 +21,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>

2
msa/black-box-tests/pom.xml

@ -21,7 +21,7 @@
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

6
msa/js-executor/api/jsInvokeMessageProcessor.ts

@ -130,7 +130,7 @@ export class JsInvokeMessageProcessor {
processCompileRequest(requestId: string, responseTopic: string, headers: any, compileRequest: JsCompileRequest) {
const scriptId = JsInvokeMessageProcessor.getScriptId(compileRequest);
this.logger.debug('[%s] Processing compile request, scriptId: [%s]', requestId, scriptId);
this.logger.debug('[%s] Processing compile request, scriptId: [%s], compileRequest [%s]', requestId, scriptId, compileRequest);
if (this.scriptMap.has(scriptId)) {
const compileResponse = JsInvokeMessageProcessor.createCompileResponse(scriptId, true);
this.logger.debug('[%s] Script was already compiled, scriptId: [%s]', requestId, scriptId);
@ -154,7 +154,7 @@ export class JsInvokeMessageProcessor {
processInvokeRequest(requestId: string, responseTopic: string, headers: any, invokeRequest: JsInvokeRequest) {
const scriptId = JsInvokeMessageProcessor.getScriptId(invokeRequest);
this.logger.debug('[%s] Processing invoke request, scriptId: [%s]', requestId, scriptId);
this.logger.debug('[%s] Processing invoke request, scriptId: [%s], invokeRequest [%s]', requestId, scriptId, invokeRequest);
this.executedScriptsCounter++;
if (this.executedScriptsCounter % statFrequency == 0) {
const nowMs = performance.now();
@ -217,7 +217,7 @@ export class JsInvokeMessageProcessor {
processReleaseRequest(requestId: string, responseTopic: string, headers: any, releaseRequest: JsReleaseRequest) {
const scriptId = JsInvokeMessageProcessor.getScriptId(releaseRequest);
this.logger.debug('[%s] Processing release request, scriptId: [%s]', requestId, scriptId);
this.logger.debug('[%s] Processing release request, scriptId: [%s], releaseRequest [%s]', requestId, scriptId, releaseRequest);
if (this.scriptMap.has(scriptId)) {
const index = this.scriptIds.indexOf(scriptId);
if (index > -1) {

2
msa/js-executor/package.json

@ -1,7 +1,7 @@
{
"name": "thingsboard-js-executor",
"private": true,
"version": "3.9.0",
"version": "4.0.0",
"description": "ThingsBoard JavaScript Executor Microservice",
"main": "server.ts",
"bin": "server.js",

2
msa/js-executor/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

2
msa/monitoring/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>

2
msa/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>msa</artifactId>

2
msa/tb-node/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

3
msa/tb/docker/install-tb.sh

@ -40,7 +40,6 @@ fi
CONF_FOLDER="${pkg.installFolder}/conf"
jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
configfile=${pkg.name}.conf
upgradeversion=${DATA_FOLDER}/.upgradeversion
source "${CONF_FOLDER}/${configfile}"
@ -54,5 +53,3 @@ java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardI
-Dinstall.upgrade=false \
-Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \
org.springframework.boot.loader.launch.PropertiesLauncher
echo "${pkg.upgradeVersion}" > ${upgradeversion}

13
msa/tb/docker/upgrade-tb.sh

@ -20,28 +20,15 @@ start-db.sh
CONF_FOLDER="${pkg.installFolder}/conf"
jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
configfile=${pkg.name}.conf
upgradeversion=${DATA_FOLDER}/.upgradeversion
source "${CONF_FOLDER}/${configfile}"
FROM_VERSION=`cat ${upgradeversion}`
echo "Starting ThingsBoard upgrade ..."
if [[ -z "${FROM_VERSION// }" ]]; then
echo "FROM_VERSION variable is invalid or unspecified!"
exit 1
else
fromVersion="${FROM_VERSION// }"
fi
java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \
-Dspring.jpa.hibernate.ddl-auto=none \
-Dinstall.upgrade=true \
-Dinstall.upgrade.from_version=${fromVersion} \
-Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \
org.springframework.boot.loader.launch.PropertiesLauncher
echo "${pkg.upgradeVersion}" > ${upgradeversion}
stop-db.sh

3
msa/tb/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>
@ -38,7 +38,6 @@
<tb-postgres.docker.name>tb-postgres</tb-postgres.docker.name>
<tb-cassandra.docker.name>tb-cassandra</tb-cassandra.docker.name>
<pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
<pkg.upgradeVersion>3.9.0</pkg.upgradeVersion>
</properties>
<dependencies>

2
msa/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.msa</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.msa</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/lwm2m/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.msa</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.msa</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

2
msa/transport/snmp/pom.xml

@ -21,7 +21,7 @@
<parent>
<groupId>org.thingsboard.msa</groupId>
<artifactId>transport</artifactId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>

2
msa/vc-executor-docker/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

2
msa/vc-executor/pom.xml

@ -21,7 +21,7 @@
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

2
msa/web-ui/package.json

@ -1,7 +1,7 @@
{
"name": "thingsboard-web-ui",
"private": true,
"version": "3.9.0",
"version": "4.0.0",
"description": "ThingsBoard Web UI Microservice",
"main": "server.ts",
"bin": "server.js",

2
msa/web-ui/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

4
netty-mqtt/pom.xml

@ -19,11 +19,11 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>netty-mqtt</artifactId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Netty MQTT Client</name>

32
pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.thingsboard</groupId>
<artifactId>thingsboard</artifactId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Thingsboard</name>
@ -42,13 +42,13 @@
<jakarta.xml.bind-api.version>4.0.2</jakarta.xml.bind-api.version>
<javax.xml.bind-api.version>2.4.0-b180830.0359</javax.xml.bind-api.version>
<jaxb-runtime.version>4.0.5</jaxb-runtime.version>
<spring-boot.version>3.2.4</spring-boot.version>
<spring-data.version>3.2.5</spring-data.version>
<spring-data-redis.version>3.2.5</spring-data-redis.version>
<spring.version>6.1.6</spring.version>
<spring-redis.version>6.2.4</spring-redis.version>
<spring-security.version>6.2.4</spring-security.version>
<jedis.version>5.1.2</jedis.version>
<spring-boot.version>3.2.12</spring-boot.version>
<spring-data.version>3.2.12</spring-data.version>
<spring-data-redis.version>3.2.12</spring-data-redis.version>
<spring.version>6.1.15</spring.version>
<spring-redis.version>6.2.11</spring-redis.version>
<spring-security.version>6.2.8</spring-security.version>
<jedis.version>5.1.5</jedis.version>
<jjwt.version>0.12.5</jjwt.version>
<slf4j.version>2.0.13</slf4j.version>
<log4j.version>2.23.1</log4j.version>
@ -81,13 +81,13 @@
<mail.version>2.0.1</mail.version>
<curator.version>5.6.0</curator.version>
<zookeeper.version>3.9.2</zookeeper.version>
<protobuf.version>3.25.3</protobuf.version> <!-- A Major v4 does not support by the pubsub yet-->
<protobuf.version>3.25.5</protobuf.version> <!-- A Major v4 does not support by the pubsub yet-->
<grpc.version>1.63.0</grpc.version>
<tbel.version>1.2.4</tbel.version>
<tbel.version>1.2.5</tbel.version>
<lombok.version>1.18.32</lombok.version>
<paho.client.version>1.2.5</paho.client.version>
<paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version>
<netty.version>4.1.109.Final</netty.version>
<netty.version>4.1.115.Final</netty.version>
<netty-tcnative.version>2.0.65.Final</netty-tcnative.version>
<reactor-netty.version>1.1.18</reactor-netty.version>
<os-maven-plugin.version>1.7.1</os-maven-plugin.version>
@ -98,7 +98,7 @@
<swagger-annotations.version>2.2.21</swagger-annotations.version>
<spatial4j.version>0.8</spatial4j.version>
<jts.version>1.19.0</jts.version>
<bouncycastle.version>1.78</bouncycastle.version>
<bouncycastle.version>1.78.1</bouncycastle.version>
<winsw.version>2.0.1</winsw.version>
<postgresql.driver.version>42.7.3</postgresql.driver.version>
<sonar.exclusions>org/thingsboard/server/gen/**/*,
@ -110,7 +110,7 @@
<!-- IMPORTANT: If you change the version of the kafka client, make sure to synchronize our overwritten implementation of the
org.apache.kafka.common.network.NetworkReceive class in the application module. It addresses the issue https://issues.apache.org/jira/browse/KAFKA-4090.
Here is the source to track https://github.com/apache/kafka/tree/trunk/clients/src/main/java/org/apache/kafka/common/network -->
<kafka.version>3.7.0</kafka.version>
<kafka.version>3.7.1</kafka.version>
<bucket4j.version>8.10.1</bucket4j.version>
<antlr.version>3.5.3</antlr.version>
<snakeyaml.version>2.2</snakeyaml.version>
@ -122,7 +122,7 @@
<ua-parser.version>1.6.1</ua-parser.version>
<commons-beanutils.version>1.9.4</commons-beanutils.version>
<commons-collections.version>4.4</commons-collections.version>
<micrometer.version>1.12.5</micrometer.version>
<micrometer.version>1.12.12</micrometer.version>
<protobuf-dynamic.version>1.0.4TB</protobuf-dynamic.version>
<wire-schema.version>3.7.1</wire-schema.version>
<twilio.version>10.1.3</twilio.version>
@ -137,7 +137,7 @@
<awaitility.version>4.2.1</awaitility.version>
<dbunit.version>2.7.3</dbunit.version>
<java-websocket.version>1.5.6</java-websocket.version>
<jupiter.version>5.10.2</jupiter.version> <!-- keep the same version as spring-boot-starter-test depend on jupiter-->
<jupiter.version>5.10.5</jupiter.version> <!-- keep the same version as spring-boot-starter-test depend on jupiter-->
<mock-server.version>5.15.0</mock-server.version>
<spring-test-dbunit.version>1.3.0</spring-test-dbunit.version> <!-- 2016 -->
<takari-cpsuite.version>1.2.7</takari-cpsuite.version> <!-- 2015 -->
@ -147,7 +147,7 @@
<assertj.version>3.25.3</assertj.version>
<rest-assured.version>5.4.0</rest-assured.version>
<hamcrest.version>2.2</hamcrest.version>
<testcontainers.version>1.19.7</testcontainers.version>
<testcontainers.version>1.20.4</testcontainers.version>
<testcontainers-junit4-mock.version>1.0.1</testcontainers-junit4-mock.version>
<zeroturnaround.version>1.12</zeroturnaround.version>
<selenium.version>4.19.1</selenium.version>

2
rest-client/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>rest-client</artifactId>

2
rule-engine/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>rule-engine</artifactId>

2
rule-engine/rule-engine-api/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>rule-engine</artifactId>
</parent>
<groupId>org.thingsboard.rule-engine</groupId>

4
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.notification.info.NotificationInfo;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
import java.util.Set;
import java.util.List;
public interface NotificationCenter {
@ -48,6 +48,6 @@ public interface NotificationCenter {
void deleteNotification(TenantId tenantId, UserId recipientId, NotificationId notificationId);
Set<NotificationDeliveryMethod> getAvailableDeliveryMethods(TenantId tenantId);
List<NotificationDeliveryMethod> getAvailableDeliveryMethods(TenantId tenantId);
}

2
rule-engine/rule-engine-components/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>rule-engine</artifactId>
</parent>
<groupId>org.thingsboard.rule-engine</groupId>

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/SemaphoreWithTbMsgQueue.java

@ -115,7 +115,7 @@ public class SemaphoreWithTbMsgQueue {
}
TbMsg msg = tbMsgTbContext.msg();
TbContext ctx = tbMsgTbContext.ctx();
log.warn("[{}] Failed to process message: {}", entityId, msg, t);
log.debug("[{}] Failed to process message: {}", entityId, msg, t);
ctx.tellFailure(msg, t); // you are not allowed to throw here, because queue will remain unprocessed
continue; // We are probably the last who process the queue. We have to continue poll until get successful callback or queue is empty
}

2
tools/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>tools</artifactId>

2
transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>

2
transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>

2
transport/lwm2m/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>

2
transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>

2
transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>transport</artifactId>

2
transport/snmp/pom.xml

@ -21,7 +21,7 @@
<parent>
<groupId>org.thingsboard</groupId>
<version>3.9.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>

2
ui-ngx/package.json

@ -1,6 +1,6 @@
{
"name": "thingsboard",
"version": "3.9.0",
"version": "4.0.0",
"scripts": {
"ng": "ng",
"start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open",

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save