Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/7542/head
Igor Kulikov 4 years ago
parent
commit
34ecc90e0d
  1. 6
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  2. 53
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  3. 24
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  4. 2
      dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java
  5. 45
      ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html
  6. 13
      ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.scss
  7. 23
      ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts

6
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java

@ -42,7 +42,11 @@ import java.util.UUID;
*/
public interface AlarmDao extends Dao<Alarm> {
ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
Alarm findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
ListenableFuture<Alarm> findLatestByOriginatorAndTypeAsync(TenantId tenantId, EntityId originator, String type);
Alarm findAlarmById(TenantId tenantId, UUID key);
ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key);

53
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -114,7 +114,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
alarm.setCustomerId(entityService.fetchEntityCustomerId(alarm.getTenantId(), alarm.getOriginator()));
if (alarm.getId() == null) {
Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get();
Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType());
if (existing == null || existing.getStatus().isCleared()) {
if (!alarmCreationEnabled) {
throw new ApiUsageLimitsExceededException("Alarms creation is disabled");
@ -124,7 +124,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
return updateAlarm(existing, alarm);
}
} else {
return updateAlarm(alarm).get();
return updateAlarm(alarm);
}
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
@ -132,7 +132,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) {
return alarmDao.findLatestByOriginatorAndType(tenantId, originator, type);
return alarmDao.findLatestByOriginatorAndTypeAsync(tenantId, originator, type);
}
@Override
@ -145,19 +145,15 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@Override
@Transactional
public AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId) {
try {
log.debug("Deleting Alarm Id: {}", alarmId);
Alarm alarm = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()).get();
if (alarm == null) {
return new AlarmOperationResult(alarm, false);
}
AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)));
deleteEntityRelations(tenantId, alarm.getId());
alarmDao.removeById(tenantId, alarm.getUuidId());
return result;
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
log.debug("Deleting Alarm Id: {}", alarmId);
Alarm alarm = alarmDao.findAlarmById(tenantId, alarmId.getId());
if (alarm == null) {
return new AlarmOperationResult(alarm, false);
}
AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)));
deleteEntityRelations(tenantId, alarm.getId());
alarmDao.removeById(tenantId, alarm.getUuidId());
return result;
}
private AlarmOperationResult createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
@ -197,19 +193,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
return relations.map(EntityRelation::getFrom).collect(Collectors.toCollection(LinkedHashSet::new));
}
private ListenableFuture<AlarmOperationResult> updateAlarm(Alarm update) {
private AlarmOperationResult updateAlarm(Alarm update) {
alarmDataValidator.validate(update, Alarm::getTenantId);
return getAndUpdate(update.getTenantId(), update.getId(), new Function<Alarm, AlarmOperationResult>() {
@Nullable
@Override
public AlarmOperationResult apply(@Nullable Alarm alarm) {
if (alarm == null) {
return null;
} else {
return updateAlarm(alarm, update);
}
}
});
return getAndUpdate(update.getTenantId(), update.getId(),
(alarm) -> alarm == null ? null : updateAlarm(alarm, update));
}
private AlarmOperationResult updateAlarm(Alarm oldAlarm, Alarm newAlarm) {
@ -233,7 +220,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@Override
public ListenableFuture<AlarmOperationResult> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTime) {
return getAndUpdate(tenantId, alarmId, new Function<Alarm, AlarmOperationResult>() {
return getAndUpdateAsync(tenantId, alarmId, new Function<Alarm, AlarmOperationResult>() {
@Nullable
@Override
public AlarmOperationResult apply(@Nullable Alarm alarm) {
@ -253,7 +240,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@Override
public ListenableFuture<AlarmOperationResult> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTime) {
return getAndUpdate(tenantId, alarmId, new Function<Alarm, AlarmOperationResult>() {
return getAndUpdateAsync(tenantId, alarmId, new Function<Alarm, AlarmOperationResult>() {
@Nullable
@Override
public AlarmOperationResult apply(@Nullable Alarm alarm) {
@ -404,9 +391,15 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
}
private <T> ListenableFuture<T> getAndUpdate(TenantId tenantId, AlarmId alarmId, Function<Alarm, T> function) {
private <T> ListenableFuture<T> getAndUpdateAsync(TenantId tenantId, AlarmId alarmId, Function<Alarm, T> function) {
validateId(alarmId, "Alarm id should be specified!");
ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId());
return Futures.transform(entity, function, readResultsProcessingExecutor);
}
private <T> T getAndUpdate(TenantId tenantId, AlarmId alarmId, Function<Alarm, T> function) {
validateId(alarmId, "Alarm id should be specified!");
Alarm entity = alarmDao.findAlarmById(tenantId, alarmId.getId());
return function.apply(entity);
}
}

24
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java

@ -79,14 +79,22 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
}
@Override
public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) {
return service.submit(() -> {
List<AlarmEntity> latest = alarmRepository.findLatestByOriginatorAndType(
originator.getId(),
type,
PageRequest.of(0, 1));
return latest.isEmpty() ? null : DaoUtil.getData(latest.get(0));
});
public Alarm findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) {
List<AlarmEntity> latest = alarmRepository.findLatestByOriginatorAndType(
originator.getId(),
type,
PageRequest.of(0, 1));
return latest.isEmpty() ? null : DaoUtil.getData(latest.get(0));
}
@Override
public ListenableFuture<Alarm> findLatestByOriginatorAndTypeAsync(TenantId tenantId, EntityId originator, String type) {
return service.submit(() -> findLatestByOriginatorAndType(tenantId, originator, type));
}
@Override
public Alarm findAlarmById(TenantId tenantId, UUID key) {
return findById(tenantId, key);
}
@Override

2
dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java

@ -63,7 +63,7 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest {
int alarmCountAfterSave = alarmDao.find(TenantId.fromUUID(tenantId)).size();
assertEquals(3, alarmCountAfterSave - alarmCountBeforeSave);
ListenableFuture<Alarm> future = alarmDao
.findLatestByOriginatorAndType(TenantId.fromUUID(tenantId), new DeviceId(originator1Id), "TEST_ALARM");
.findLatestByOriginatorAndTypeAsync(TenantId.fromUUID(tenantId), new DeviceId(originator1Id), "TEST_ALARM");
Alarm alarm = future.get(30, TimeUnit.SECONDS);
assertNotNull(alarm);
assertEquals(alarm2Id, alarm.getId().getId());

45
ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.html

@ -59,29 +59,49 @@
</div>
</mat-toolbar>
<div fxFlex class="table-container">
<table mat-table [dataSource]="dataSource"
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear>
<table mat-table
[dataSource]="dataSource"
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear
cdkDropList cdkDropListOrientation="vertical"
[cdkDropListData]="dataSource"
(cdkDropListDropped)="dropAction($event)"
[cdkDropListDisabled]="dragDisabled"
class="tb-drop-list">
<ng-container matColumnDef="actionSourceId">
<mat-header-cell *matHeaderCellDef style="width: 36px"></mat-header-cell>
<mat-cell *matCellDef="let action">
<div cdkDragHandle
class="tb-drag-handle"
style="min-width: 36px"
(mousedown)="dragDisabled = false"
(mouseup)="dragDisabled = true"
matTooltip="{{ 'action.drag' | translate }}"
matTooltipPosition="above">
<mat-icon>drag_handle</mat-icon>
</div>
</mat-cell>
</ng-container>
<ng-container matColumnDef="actionSourceName">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 20%"> {{ 'widget-config.action-source' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let action">
<mat-cell *matCellDef="let action" style="width: 20%">
{{ action.actionSourceName }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 30%"> {{ 'widget-config.action-name' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let action">
<mat-header-cell *matHeaderCellDef style="width: 30%"> {{ 'widget-config.action-name' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let action" style="width: 30%">
{{ action.name }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="icon">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 40px"> {{ 'widget-config.action-icon' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let action">
<mat-header-cell *matHeaderCellDef style="width: 40px"> {{ 'widget-config.action-icon' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let action" style="width: 40px">
<mat-icon>{{ action.icon }}</mat-icon>
</mat-cell>
</ng-container>
<ng-container matColumnDef="typeName">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 50%"> {{ 'widget-config.action-type' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let action">
<mat-header-cell *matHeaderCellDef style="width: 50%"> {{ 'widget-config.action-type' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let action" style="width: 50%">
{{ action.typeName }}
</mat-cell>
</ng-container>
@ -107,9 +127,10 @@
</div>
</mat-cell>
</ng-container>
<mat-header-row [ngClass]="{'mat-row-select': true}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [ngClass]="{'mat-row-select': true}"
*matRowDef="let action; columns: displayedColumns;"></mat-row>
<mat-header-row class="mat-row-select" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row class="mat-row-select tb-draggable"
*matRowDef="let action; columns: displayedColumns;"
cdkDrag [cdkDragData]="action"></mat-row>
</table>
<span [fxShow]="dataSource.isEmpty() | async"
fxLayoutAlign="center center"

13
ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.scss

@ -41,4 +41,17 @@
.mat-sort-header-sorted .mat-sort-header-arrow {
opacity: 1 !important;
}
.tb-draggable {
&.cdk-drag-placeholder {
opacity: 0;
}
&.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
}
}

23
ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts

@ -37,7 +37,7 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { fromEvent, merge } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
import { debounceTime, distinctUntilChanged, first, tap } from 'rxjs/operators';
import {
toWidgetActionDescriptor,
WidgetActionCallbacks,
@ -54,6 +54,7 @@ import {
import { deepClone } from '@core/utils';
import { ResizeObserver } from '@juggle/resize-observer';
import { hidePageSizePixelValue } from '@shared/models/constants';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
@Component({
selector: 'tb-manage-widget-actions',
@ -85,6 +86,7 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni
viewsInited = false;
dirtyValue = false;
dragDisabled = true;
private widgetResize$: ResizeObserver;
@ -106,7 +108,7 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni
const sortOrder: SortOrder = { property: 'actionSourceName', direction: Direction.ASC };
this.pageLink = new PageLink(10, 0, null, sortOrder);
this.dataSource = new WidgetActionsDatasource(this.translate, this.utils);
this.displayedColumns = ['actionSourceName', 'name', 'icon', 'typeName', 'actions'];
this.displayedColumns = ['actionSourceId', 'actionSourceName', 'name', 'icon', 'typeName', 'actions'];
}
ngOnInit(): void {
@ -163,6 +165,23 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni
this.dataSource.loadActions(this.pageLink, reload);
}
dropAction(event: CdkDragDrop<WidgetActionsDatasource>) {
this.dragDisabled = true;
const droppedAction: WidgetActionDescriptorInfo = event.item.data;
this.dataSource.pageData$.pipe(
first()
).subscribe((actions) => {
const action = actions.data;
let startActionSourceIndex = action.findIndex(element => element.actionSourceId === droppedAction.actionSourceId);
const targetActions = this.getOrCreateTargetActions(droppedAction.actionSourceId);
if (startActionSourceIndex === 0) {
startActionSourceIndex -= targetActions.findIndex(element => element.id === action[0].id);
}
moveItemInArray(targetActions, event.previousIndex - startActionSourceIndex, event.currentIndex - startActionSourceIndex);
this.onActionsUpdated();
});
}
addAction($event: Event) {
this.openWidgetActionDialog($event);
}

Loading…
Cancel
Save