Browse Source

Merge remote-tracking branch 'origin/lts-4.2' into lts-4.3

pull/15439/head
Viacheslav Klimov 7 months ago
parent
commit
f42be47267
Failed to extract signature
  1. 2
      .gitignore
  2. 9
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
  3. 7
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java
  4. 1
      dao/pom.xml
  5. 1
      netty-mqtt/pom.xml
  6. 4
      pom.xml
  7. 3
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html
  8. 6
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts
  9. 14
      ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-config.resolver.ts
  10. 4
      ui-ngx/src/app/shared/components/notification/notification.component.html
  11. 13
      ui-ngx/src/app/shared/components/notification/notification.component.ts

2
.gitignore

@ -37,4 +37,4 @@ rebuild-docker.sh
*/.run/** */.run/**
.run/** .run/**
.run .run
.claude/ .claude

9
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java

@ -171,7 +171,14 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
if (this.leshanClient != null) { if (this.leshanClient != null) {
log.info("Stop/reboot LwM2M client {}", this.leshanClient.getEndpoint(identity)); log.info("Stop/reboot LwM2M client {}", this.leshanClient.getEndpoint(identity));
this.leshanClient.stop(false); try {
this.leshanClient.stop(false);
} catch (Exception stopEx) {
// Leshan may throw NPE during CoAP observe-relation cleanup when the server
// reference is null (race condition in NotificationDataStore.toKey()).
// The client is still considered stopped at this point — proceed with restart.
log.warn("Exception during LwM2M client stop, proceeding with restart: {}", stopEx.getMessage());
}
log.info("Start after update fw LwM2M client {}", this.leshanClient.getEndpoint(identity)); log.info("Start after update fw LwM2M client {}", this.leshanClient.getEndpoint(identity));
this.leshanClient.start(); this.leshanClient.start();

7
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java

@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper;
import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
@ -37,6 +38,12 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID
@Slf4j @Slf4j
public class RpcLwm2mIntegrationReadCollectedValueTest extends AbstractRpcLwM2MIntegrationTest { public class RpcLwm2mIntegrationReadCollectedValueTest extends AbstractRpcLwM2MIntegrationTest {
@Before
public void resetCollectedValueTimestamps() {
Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0 = 0;
Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_1 = 0;
}
/** /**
* Read {"id":"/3303/12/5700"} * Read {"id":"/3303/12/5700"}
* Trigger a Send operation from the client with multiple values for the same resource as a payload * Trigger a Send operation from the client with multiple values for the same resource as a payload

1
dao/pom.xml

@ -280,7 +280,6 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>${jar-plugin.version}</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>

1
netty-mqtt/pom.xml

@ -125,7 +125,6 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
<configuration> <configuration>
<archive> <archive>
<manifest> <manifest>

4
pom.xml

@ -72,7 +72,7 @@
<paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version> <paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version>
<os-maven-plugin.version>1.7.1</os-maven-plugin.version> <os-maven-plugin.version>1.7.1</os-maven-plugin.version>
<surefire.version>3.5.4</surefire.version> <surefire.version>3.5.4</surefire.version>
<jar-plugin.version>3.4.0</jar-plugin.version> <jar-plugin.version>3.5.0</jar-plugin.version>
<springdoc-swagger.version>2.8.8TB</springdoc-swagger.version> <springdoc-swagger.version>2.8.8TB</springdoc-swagger.version>
<swagger-annotations.version>2.2.30</swagger-annotations.version> <swagger-annotations.version>2.2.30</swagger-annotations.version>
<spatial4j.version>0.8</spatial4j.version> <spatial4j.version>0.8</spatial4j.version>
@ -635,7 +635,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version> <version>${jar-plugin.version}</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

3
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html

@ -93,7 +93,8 @@
(click)="deleteTelemetry($event)"> (click)="deleteTelemetry($event)">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
</button> </button>
<button mat-raised-button color="accent" <button [class.!hidden]="isSysAdmin"
mat-raised-button color="accent"
class="button-widget-action" class="button-widget-action"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
matTooltip="{{ 'attribute.show-on-widget' | translate }}" matTooltip="{{ 'attribute.show-on-widget' | translate }}"

6
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts

@ -56,6 +56,8 @@ import {
import { AttributeDatasource } from '@home/models/datasource/attribute-datasource'; import { AttributeDatasource } from '@home/models/datasource/attribute-datasource';
import { AttributeService } from '@app/core/http/attribute.service'; import { AttributeService } from '@app/core/http/attribute.service';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { Authority } from '@shared/models/authority.enum';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { import {
AddAttributeDialogComponent, AddAttributeDialogComponent,
@ -91,7 +93,6 @@ import { FormBuilder } from '@angular/forms';
import { AggregationType, defaultTimewindow } from '@shared/models/time/time.models'; import { AggregationType, defaultTimewindow } from '@shared/models/time/time.models';
import { TimeService } from '@core/services/time.service'; import { TimeService } from '@core/services/time.service';
@Component({ @Component({
selector: 'tb-attribute-table', selector: 'tb-attribute-table',
templateUrl: './attribute-table.component.html', templateUrl: './attribute-table.component.html',
@ -186,6 +187,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
textSearch = this.fb.control('', {nonNullable: true}); textSearch = this.fb.control('', {nonNullable: true});
isSysAdmin = false;
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
@ -206,6 +209,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
private fb: FormBuilder, private fb: FormBuilder,
private timeService: TimeService) { private timeService: TimeService) {
super(store); super(store);
this.isSysAdmin = getCurrentAuthUser(this.store).authority === Authority.SYS_ADMIN;
this.dirtyValue = !this.activeValue; this.dirtyValue = !this.activeValue;
const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC }; const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC };
this.pageLink = new PageLink(10, 0, null, sortOrder); this.pageLink = new PageLink(10, 0, null, sortOrder);

14
ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-config.resolver.ts

@ -37,9 +37,10 @@ import {
InboxNotificationDialogComponent, InboxNotificationDialogComponent,
InboxNotificationDialogData InboxNotificationDialogData
} from '@home/pages/notification/inbox/inbox-notification-dialog.component'; } from '@home/pages/notification/inbox/inbox-notification-dialog.component';
import { Injectable } from '@angular/core'; import { Injectable, SecurityContext } from '@angular/core';
import { ActivatedRouteSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot } from '@angular/router';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { DomSanitizer } from '@angular/platform-browser';
@Injectable() @Injectable()
export class InboxTableConfigResolver { export class InboxTableConfigResolver {
@ -50,7 +51,8 @@ export class InboxTableConfigResolver {
private translate: TranslateService, private translate: TranslateService,
private dialog: MatDialog, private dialog: MatDialog,
private datePipe: DatePipe, private datePipe: DatePipe,
private utilsService: UtilsService) { private utilsService: UtilsService,
private sanitizer: DomSanitizer) {
this.config.entityType = EntityType.NOTIFICATION; this.config.entityType = EntityType.NOTIFICATION;
this.config.detailsPanelEnabled = false; this.config.detailsPanelEnabled = false;
@ -95,14 +97,14 @@ export class InboxTableConfigResolver {
new EntityTableColumn<Notification>('type', 'notification.type', '10%', (notification) => new EntityTableColumn<Notification>('type', 'notification.type', '10%', (notification) =>
this.translate.instant(NotificationTemplateTypeTranslateMap.get(notification.type).name)), this.translate.instant(NotificationTemplateTypeTranslateMap.get(notification.type).name)),
new EntityTableColumn<Notification>('subject', 'notification.subject', '30%', new EntityTableColumn<Notification>('subject', 'notification.subject', '30%',
(entity) => this.utilsService.customTranslation(entity.subject, entity.subject)), (entity) => this.sanitizer.sanitize(SecurityContext.HTML, this.utilsService.customTranslation(entity.subject, entity.subject))),
new EntityTableColumn<Notification>('text', 'notification.message', '60%', new EntityTableColumn<Notification>('text', 'notification.message', '60%',
(entity) => this.utilsService.customTranslation(entity.text, entity.text)) (entity) => this.sanitizer.sanitize(SecurityContext.HTML, this.utilsService.customTranslation(entity.text, entity.text)))
); );
} }
resolve(route: ActivatedRouteSnapshot): EntityTableConfig<Notification> { resolve(_route: ActivatedRouteSnapshot): EntityTableConfig<Notification> {
return this.config; return this.config;
} }
@ -128,7 +130,7 @@ export class InboxTableConfigResolver {
}); });
} }
private markAsRead($event, entity){ private markAsRead($event: Event, entity){
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }

4
ui-ngx/src/app/shared/components/notification/notification.component.html

@ -28,8 +28,8 @@
</tb-icon> </tb-icon>
</ng-template> </ng-template>
<div class="content flex-1"> <div class="content flex-1">
<div class="title" [innerHTML]="(title | safe: 'html' )"></div> <div class="title" [innerHTML]="title"></div>
<div class="message" [innerHTML]="(message | safe: 'html' )"></div> <div class="message" [innerHTML]="message"></div>
<button *ngIf="showButton" mat-stroked-button class="button" color="primary" <button *ngIf="showButton" mat-stroked-button class="button" color="primary"
(click)="navigate($event)"> (click)="navigate($event)">
{{ buttonLabel }} {{ buttonLabel }}

13
ui-ngx/src/app/shared/components/notification/notification.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, SecurityContext } from '@angular/core';
import { import {
ActionButtonLinkType, ActionButtonLinkType,
Notification, Notification,
@ -33,6 +33,7 @@ import tinycolor from 'tinycolor2';
import { StateObject } from '@core/api/widget-api.models'; import { StateObject } from '@core/api/widget-api.models';
import { objToBase64URI } from '@core/utils'; import { objToBase64URI } from '@core/utils';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean } from '@shared/decorators/coercion';
import { DomSanitizer } from '@angular/platform-browser';
@Component({ @Component({
selector: 'tb-notification', selector: 'tb-notification',
@ -71,7 +72,8 @@ export class NotificationComponent implements OnInit {
constructor( constructor(
private utils: UtilsService, private utils: UtilsService,
private router: Router private router: Router,
private sanitizer: DomSanitizer
) { ) {
} }
@ -79,11 +81,10 @@ export class NotificationComponent implements OnInit {
this.showIcon = this.notification.additionalConfig?.icon?.enabled; this.showIcon = this.notification.additionalConfig?.icon?.enabled;
this.showButton = this.notification.additionalConfig?.actionButtonConfig?.enabled; this.showButton = this.notification.additionalConfig?.actionButtonConfig?.enabled;
this.hideMarkAsReadButton = this.notification.status === NotificationStatus.READ; this.hideMarkAsReadButton = this.notification.status === NotificationStatus.READ;
this.title = this.utils.customTranslation(this.notification.subject, this.notification.subject); this.title = this.sanitizer.sanitize(SecurityContext.HTML, this.utils.customTranslation(this.notification.subject));
this.message = this.utils.customTranslation(this.notification.text, this.notification.text); this.message = this.sanitizer.sanitize(SecurityContext.HTML, this.utils.customTranslation(this.notification.text));
if (this.showButton) { if (this.showButton) {
this.buttonLabel = this.utils.customTranslation(this.notification.additionalConfig.actionButtonConfig.text, this.buttonLabel = this.utils.customTranslation(this.notification.additionalConfig.actionButtonConfig.text);
this.notification.additionalConfig.actionButtonConfig.text);
} }
} }

Loading…
Cancel
Save