Browse Source

Merge branch 'master' into aggregationTsDao_fix

pull/5905/head
van-vanich 5 years ago
parent
commit
dfb734d80b
  1. 2
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  2. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java
  3. 56
      application/src/main/java/org/thingsboard/server/service/install/HsqlEntityDatabaseSchemaService.java
  4. 32
      application/src/main/java/org/thingsboard/server/service/install/HsqlTsDatabaseSchemaService.java
  5. 6
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  6. 1
      application/src/main/java/org/thingsboard/server/service/install/update/PaginatedUpdater.java
  7. 3
      application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java
  8. 2
      application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java
  9. 4
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
  10. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  11. 8
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  12. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java
  13. 8
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java
  14. 2
      common/edge-api/src/main/proto/edge.proto
  15. 12
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java
  16. 3
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  17. 45
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  18. 2
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  19. 14
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java
  20. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  21. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java
  22. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmQueryRepository.java
  23. 32
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java
  24. 599
      dao/src/main/resources/sql/schema-entities-hsql.sql
  25. 4
      dao/src/main/resources/sql/schema-entities.sql
  26. 41
      dao/src/main/resources/sql/schema-ts-hsql.sql
  27. 20
      dao/src/main/resources/sql/schema-types-hsql.sql
  28. 78
      dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java
  29. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java
  30. 10
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java
  31. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeConfiguration.java
  32. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java
  33. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
  34. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java
  35. 16
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
  36. 2
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  37. 3
      ui-ngx/angular.json
  38. 1
      ui-ngx/package.json
  39. 1
      ui-ngx/src/app/core/services/menu.models.ts
  40. 5
      ui-ngx/src/app/core/services/menu.service.ts
  41. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts
  42. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-table-header.component.ts
  43. 15
      ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts
  44. 3
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.html
  45. 10
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts
  46. 61
      ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts
  47. 30
      ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts
  48. 17
      ui-ngx/src/app/modules/home/components/event/event-table-config.ts
  49. 2
      ui-ngx/src/app/modules/home/components/event/event-table-header.component.ts
  50. 13
      ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html
  51. 4
      ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts
  52. 13
      ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.html
  53. 4
      ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.ts
  54. 2
      ui-ngx/src/app/modules/home/menu/menu-link.component.html
  55. 2
      ui-ngx/src/app/modules/home/menu/menu-toggle.component.html
  56. 40
      ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
  57. 19
      ui-ngx/src/app/modules/home/models/entity/entity-details-page-component.models.ts
  58. 2
      ui-ngx/src/app/modules/home/pages/asset/asset-table-header.component.ts
  59. 25
      ui-ngx/src/app/modules/home/pages/asset/assets-table-config.resolver.ts
  60. 2
      ui-ngx/src/app/modules/home/pages/customer/customers-table-config.resolver.ts
  61. 28
      ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts
  62. 6
      ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts
  63. 2
      ui-ngx/src/app/modules/home/pages/device/device-table-header.component.ts
  64. 2
      ui-ngx/src/app/modules/home/pages/device/device.component.html
  65. 2
      ui-ngx/src/app/modules/home/pages/device/device.component.ts
  66. 24
      ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts
  67. 2
      ui-ngx/src/app/modules/home/pages/edge/edge-table-header.component.ts
  68. 16
      ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts
  69. 2
      ui-ngx/src/app/modules/home/pages/entity-view/entity-view-table-header.component.ts
  70. 18
      ui-ngx/src/app/modules/home/pages/entity-view/entity-views-table-config.resolver.ts
  71. 20
      ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts
  72. 6
      ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts
  73. 2
      ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts
  74. 2
      ui-ngx/src/app/modules/home/pages/tenant/tenants-table-config.resolver.ts
  75. 2
      ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts
  76. 4
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts
  77. 13
      ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html
  78. 9
      ui-ngx/src/app/shared/models/base-data.ts
  79. 5
      ui-ngx/yarn.lock

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

@ -229,7 +229,7 @@ public class AlarmController extends BaseController {
@ApiOperation(value = "Get Alarms (getAlarms)",
notes = "Returns a page of alarms for the selected entity. Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " +
PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public PageData<AlarmInfo> getAlarms(

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java

@ -71,7 +71,9 @@ public class AlarmMsgConstructor {
.setAckTs(alarm.getAckTs())
.setClearTs(alarm.getClearTs())
.setDetails(JacksonUtil.toString(alarm.getDetails()))
.setPropagate(alarm.isPropagate());
.setPropagate(alarm.isPropagate())
.setPropagateToOwner(alarm.isPropagateToOwner())
.setPropagateToTenant(alarm.isPropagateToTenant());
return builder.build();
}

56
application/src/main/java/org/thingsboard/server/service/install/HsqlEntityDatabaseSchemaService.java

@ -1,56 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.install;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.util.HsqlDao;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
@Service
@Slf4j
@HsqlDao
@Profile("install")
public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
implements EntityDatabaseSchemaService {
protected HsqlEntityDatabaseSchemaService() {
super("schema-entities-hsql.sql", "schema-entities-idx.sql");
}
private final String schemaTypesSql = "schema-types-hsql.sql";
@Override
public void createDatabaseSchema(boolean createIndexes) throws Exception {
log.info("Installing SQL DataBase types part: " + schemaTypesSql);
Path schemaFile = Paths.get(installScripts.getDataDir(), SQL_DIR, schemaTypesSql);
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
String sql = new String(Files.readAllBytes(schemaFile), Charset.forName("UTF-8"));
conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to load initial thingsboard database schema
}
super.createDatabaseSchema(createIndexes);
}
}

32
application/src/main/java/org/thingsboard/server/service/install/HsqlTsDatabaseSchemaService.java

@ -1,32 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.install;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.util.HsqlDao;
import org.thingsboard.server.dao.util.SqlTsDao;
@Service
@SqlTsDao
@HsqlDao
@Profile("install")
public class HsqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
implements TsDatabaseSchemaService {
public HsqlTsDatabaseSchemaService() {
super("schema-ts-hsql.sql", null);
}
}

6
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -475,6 +475,12 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", SCHEMA_UPDATE_SQL);
loadSql(schemaUpdateFile, conn);
try {
conn.createStatement().execute("ALTER TABLE alarm ADD COLUMN propagate_to_owner boolean DEFAULT false;"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
conn.createStatement().execute("ALTER TABLE alarm ADD COLUMN propagate_to_tenant boolean DEFAULT false;"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception ignored) {
}
try {
conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, alarm_type, customer_id, alarm_id)" +
" select tenant_id, originator_id, created_time, type, customer_id, id from alarm ON CONFLICT DO NOTHING;");

1
application/src/main/java/org/thingsboard/server/service/install/update/PaginatedUpdater.java

@ -28,7 +28,6 @@ public abstract class PaginatedUpdater<I, D> {
private int updated = 0;
public void updateEntities(I id) {
log.info("{}: started...", getName());
updated = 0;
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
boolean hasNext = true;

3
application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java

@ -106,8 +106,7 @@ public class DefaultEntityQueryService implements EntityQueryService {
for (EntityData entityData : entities.getData()) {
entitiesMap.put(entityData.getEntityId(), entityData);
}
PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(securityUser.getTenantId(),
securityUser.getCustomerId(), query, entitiesMap.keySet());
PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(securityUser.getTenantId(), query, entitiesMap.keySet());
for (AlarmData alarmData : alarms.getData()) {
EntityId entityId = alarmData.getEntityId();
if (entityId != null) {

2
application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java

@ -106,7 +106,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
AlarmDataUpdate update;
if (!entitiesMap.isEmpty()) {
long start = System.currentTimeMillis();
PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(getTenantId(), getCustomerId(), query, getOrderedEntityIds());
PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(getTenantId(), query, getOrderedEntityIds());
long end = System.currentTimeMillis();
stats.getAlarmQueryInvocationCnt().incrementAndGet();
stats.getAlarmQueryTimeSpent().addAndGet(end - start);

4
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java

@ -150,8 +150,8 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
}
@Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
return alarmService.findAlarmDataByQueryForEntities(tenantId, customerId, query, orderedEntityIds);
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
return alarmService.findAlarmDataByQueryForEntities(tenantId, query, orderedEntityIds);
}
@Override

2
common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -61,7 +61,7 @@ public interface AlarmService {
ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId);

8
common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java

@ -70,7 +70,11 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
private transient JsonNode details;
@ApiModelProperty(position = 15, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true")
private boolean propagate;
@ApiModelProperty(position = 16, value = "JSON array of relation types that should be used for propagation. " +
@ApiModelProperty(position = 16, value = "Propagation flag to specify if alarm should be propagated to the owner (tenant or customer) of alarm originator", example = "true")
private boolean propagateToOwner;
@ApiModelProperty(position = 17, value = "Propagation flag to specify if alarm should be propagated to the tenant entity", example = "true")
private boolean propagateToTenant;
@ApiModelProperty(position = 18, value = "JSON array of relation types that should be used for propagation. " +
"By default, 'propagateRelationTypes' array is empty which means that the alarm will be propagated based on any relation type to parent entities. " +
"This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.")
private List<String> propagateRelationTypes;
@ -98,6 +102,8 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
this.clearTs = alarm.getClearTs();
this.details = alarm.getDetails();
this.propagate = alarm.isPropagate();
this.propagateToOwner = alarm.isPropagateToOwner();
this.propagateToTenant = alarm.isPropagateToTenant();
this.propagateRelationTypes = alarm.getPropagateRelationTypes();
}

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java

@ -23,7 +23,7 @@ public class AlarmInfo extends Alarm {
private static final long serialVersionUID = 2807343093519543363L;
@ApiModelProperty(position = 17, value = "Alarm originator name", example = "Thermostat")
@ApiModelProperty(position = 19, value = "Alarm originator name", example = "Thermostat")
private String originatorName;
public AlarmInfo() {

8
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java

@ -49,8 +49,14 @@ public class DeviceProfileAlarm implements Serializable {
// Hidden in advanced settings
@ApiModelProperty(position = 5, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true")
private boolean propagate;
@ApiModelProperty(position = 6, value = "JSON array of relation types that should be used for propagation. " +
@ApiModelProperty(position = 6, value = "Propagation flag to specify if alarm should be propagated to the owner (tenant or customer) of alarm originator", example = "true")
private boolean propagateToOwner;
@ApiModelProperty(position = 7, value = "Propagation flag to specify if alarm should be propagated to the tenant entity", example = "true")
private boolean propagateToTenant;
@ApiModelProperty(position = 8, value = "JSON array of relation types that should be used for propagation. " +
"By default, 'propagateRelationTypes' array is empty which means that the alarm will be propagated based on any relation type to parent entities. " +
"This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.")
private List<String> propagateRelationTypes;
}

2
common/edge-api/src/main/proto/edge.proto

@ -269,6 +269,8 @@ message AlarmUpdateMsg {
int64 clearTs = 13;
string details = 14;
bool propagate = 15;
bool propagateToOwner = 16;
bool propagateToTenant = 17;
}
message CustomerUpdateMsg {

12
common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java

@ -22,6 +22,7 @@ import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
@ -296,4 +297,15 @@ public final class TbMsg implements Serializable {
public boolean isValid() {
return getCallback().isMsgValid();
}
public long getMetaDataTs() {
String tsStr = metaData.getValue("ts");
if (!StringUtils.isEmpty(tsStr)) {
try {
return Long.parseLong(tsStr);
} catch (NumberFormatException ignored) {
}
}
return ts;
}
}

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

@ -54,8 +54,7 @@ public interface AlarmDao extends Dao<Alarm> {
PageData<AlarmInfo> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query);
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, Set<AlarmStatus> status);

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

@ -59,6 +59,7 @@ import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@ -139,11 +140,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
@Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
return alarmDao.findAlarmDataByQueryForEntities(tenantId, customerId, query, orderedEntityIds);
return alarmDao.findAlarmDataByQueryForEntities(tenantId, query, orderedEntityIds);
}
@Override
@ -171,25 +171,25 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
private List<EntityId> createEntityAlarmRecords(Alarm alarm) throws InterruptedException, ExecutionException {
List<EntityId> propagatedEntitiesList;
Set<EntityId> propagatedEntitiesSet = new LinkedHashSet<>();
propagatedEntitiesSet.add(alarm.getOriginator());
if (alarm.isPropagate()) {
Set<EntityId> parentEntities = getParentEntities(alarm);
propagatedEntitiesList = new ArrayList<>(parentEntities.size() + 1);
for (EntityId parentId : parentEntities) {
propagatedEntitiesList.add(parentId);
createEntityAlarmRecord(alarm.getTenantId(), parentId, alarm);
}
propagatedEntitiesList.add(alarm.getOriginator());
} else {
propagatedEntitiesList = Collections.singletonList(alarm.getOriginator());
propagatedEntitiesSet.addAll(getRelatedEntities(alarm));
}
if (alarm.isPropagateToOwner()) {
propagatedEntitiesSet.add(alarm.getCustomerId() != null ? alarm.getCustomerId() : alarm.getTenantId());
}
createEntityAlarmRecord(alarm.getTenantId(), alarm.getOriginator(), alarm);
return propagatedEntitiesList;
if (alarm.isPropagateToTenant()) {
propagatedEntitiesSet.add(alarm.getTenantId());
}
for (EntityId entityId : propagatedEntitiesSet) {
createEntityAlarmRecord(alarm.getTenantId(), entityId, alarm);
}
return new ArrayList<>(propagatedEntitiesSet);
}
private Set<EntityId> getParentEntities(Alarm alarm) throws InterruptedException, ExecutionException {
private Set<EntityId> getRelatedEntities(Alarm alarm) throws InterruptedException, ExecutionException {
EntityRelationsQuery query = new EntityRelationsQuery();
//TODO 3.1: @dlandiak we need to fetch max 3 levels and then fetch more if needed and there is at least one non-duplicate.
RelationsSearchParameters parameters = new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE, false);
query.setParameters(parameters);
List<String> propagateRelationTypes = alarm.getPropagateRelationTypes();
@ -216,11 +216,12 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
private AlarmOperationResult updateAlarm(Alarm oldAlarm, Alarm newAlarm) {
boolean oldPropagate = oldAlarm.isPropagate();
boolean newPropagate = newAlarm.isPropagate();
boolean propagationEnabled = !oldAlarm.isPropagate() && newAlarm.isPropagate();
boolean propagationToOwnerEnabled = !oldAlarm.isPropagateToOwner() && newAlarm.isPropagateToOwner();
boolean propagationToTenantEnabled = !oldAlarm.isPropagateToTenant() && newAlarm.isPropagateToTenant();
Alarm result = alarmDao.save(newAlarm.getTenantId(), merge(oldAlarm, newAlarm));
List<EntityId> propagatedEntitiesList;
if (!oldPropagate && newPropagate) {
if (propagationEnabled || propagationToOwnerEnabled || propagationToTenantEnabled) {
try {
propagatedEntitiesList = createEntityAlarmRecords(result);
} catch (InterruptedException | ExecutionException e) {
@ -372,6 +373,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
existing.setDetails(alarm.getDetails());
existing.setCustomerId(alarm.getCustomerId());
existing.setPropagate(existing.isPropagate() || alarm.isPropagate());
existing.setPropagateToOwner(existing.isPropagateToOwner() || alarm.isPropagateToOwner());
existing.setPropagateToTenant(existing.isPropagateToTenant() || alarm.isPropagateToTenant());
List<String> existingPropagateRelationTypes = existing.getPropagateRelationTypes();
List<String> newRelationTypes = alarm.getPropagateRelationTypes();
if (!CollectionUtils.isEmpty(newRelationTypes)) {
@ -387,7 +390,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
private Set<EntityId> getPropagationEntityIds(Alarm alarm) {
if (alarm.isPropagate()) {
if (alarm.isPropagate() || alarm.isPropagateToOwner() || alarm.isPropagateToTenant()) {
List<EntityAlarm> entityAlarms = alarmDao.findEntityAlarmRecords(alarm.getTenantId(), alarm.getId());
return entityAlarms.stream().map(EntityAlarm::getEntityId).collect(Collectors.toSet());
} else {

2
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -273,6 +273,8 @@ public class ModelConstants {
public static final String ALARM_ACK_TS_PROPERTY = "ack_ts";
public static final String ALARM_CLEAR_TS_PROPERTY = "clear_ts";
public static final String ALARM_PROPAGATE_PROPERTY = "propagate";
public static final String ALARM_PROPAGATE_TO_OWNER_PROPERTY = "propagate_to_owner";
public static final String ALARM_PROPAGATE_TO_TENANT_PROPERTY = "propagate_to_tenant";
public static final String ALARM_PROPAGATE_RELATION_TYPES = "propagate_relation_types";
public static final String ALARM_BY_ID_VIEW_NAME = "alarm_by_id";

14
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java

@ -51,6 +51,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_I
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_RELATION_TYPES;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_TO_OWNER_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_TO_TENANT_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY;
@ -105,6 +107,12 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
@Column(name = ALARM_PROPAGATE_PROPERTY)
private Boolean propagate;
@Column(name = ALARM_PROPAGATE_TO_OWNER_PROPERTY)
private Boolean propagateToOwner;
@Column(name = ALARM_PROPAGATE_TO_TENANT_PROPERTY)
private Boolean propagateToTenant;
@Column(name = ALARM_PROPAGATE_RELATION_TYPES)
private String propagateRelationTypes;
@ -130,6 +138,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
this.severity = alarm.getSeverity();
this.status = alarm.getStatus();
this.propagate = alarm.isPropagate();
this.propagateToOwner = alarm.isPropagateToOwner();
this.propagateToTenant = alarm.isPropagateToTenant();
this.startTs = alarm.getStartTs();
this.endTs = alarm.getEndTs();
this.ackTs = alarm.getAckTs();
@ -154,6 +164,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
this.severity = alarmEntity.getSeverity();
this.status = alarmEntity.getStatus();
this.propagate = alarmEntity.getPropagate();
this.propagateToOwner = alarmEntity.getPropagateToOwner();
this.propagateToTenant = alarmEntity.getPropagateToTenant();
this.startTs = alarmEntity.getStartTs();
this.endTs = alarmEntity.getEndTs();
this.ackTs = alarmEntity.getAckTs();
@ -176,6 +188,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
alarm.setSeverity(severity);
alarm.setStatus(status);
alarm.setPropagate(propagate);
alarm.setPropagateToOwner(propagateToOwner);
alarm.setPropagateToTenant(propagateToTenant);
alarm.setStartTs(startTs);
alarm.setEndTs(endTs);
alarm.setAckTs(ackTs);

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

@ -157,8 +157,8 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
}
@Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
return alarmQueryRepository.findAlarmDataByQueryForEntities(tenantId, customerId, query, orderedEntityIds);
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
return alarmQueryRepository.findAlarmDataByQueryForEntities(tenantId, query, orderedEntityIds);
}
@Override

2
dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java

@ -83,6 +83,8 @@ public class AlarmDataAdapter {
UUID originatorId = (UUID) row.get(ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY);
alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId));
alarm.setPropagate((boolean) row.get(ModelConstants.ALARM_PROPAGATE_PROPERTY));
alarm.setPropagateToOwner((boolean) row.get(ModelConstants.ALARM_PROPAGATE_TO_OWNER_PROPERTY));
alarm.setPropagateToTenant((boolean) row.get(ModelConstants.ALARM_PROPAGATE_TO_TENANT_PROPERTY));
alarm.setType(row.get(ModelConstants.ALARM_TYPE_PROPERTY).toString());
alarm.setSeverity(AlarmSeverity.valueOf(row.get(ModelConstants.ALARM_SEVERITY_PROPERTY).toString()));
alarm.setStatus(AlarmStatus.valueOf(row.get(ModelConstants.ALARM_STATUS_PROPERTY).toString()));

2
dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmQueryRepository.java

@ -27,7 +27,7 @@ import java.util.Collection;
public interface AlarmQueryRepository {
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
}

32
dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java

@ -97,6 +97,8 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
" a.originator_id as originator_id," +
" a.originator_type as originator_type," +
" a.propagate as propagate," +
" a.propagate_to_owner as propagate_to_owner," +
" a.propagate_to_tenant as propagate_to_tenant," +
" a.severity as severity," +
" a.start_ts as start_ts," +
" a.status as status, " +
@ -119,11 +121,10 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
}
@Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
return transactionTemplate.execute(status -> {
AlarmDataPageLink pageLink = query.getPageLink();
QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, customerId, EntityType.ALARM));
QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, null, EntityType.ALARM));
ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList()));
StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION);
StringBuilder fromPart = new StringBuilder(" from alarm a ");
@ -134,7 +135,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
if (pageLink.isSearchPropagatedAlarms()) {
selectPart.append(" ea.entity_id as entity_id ");
fromPart.append(JOIN_ENTITY_ALARMS);
wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx));
wherePart.append(buildPermissionsQuery(tenantId, ctx));
addAnd = true;
} else {
selectPart.append(" a.originator_id as entity_id ");
@ -285,31 +286,10 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
}
}
private String buildPermissionsQuery(TenantId tenantId, CustomerId customerId, QueryContext ctx) {
private String buildPermissionsQuery(TenantId tenantId, QueryContext ctx) {
StringBuilder permissionsQuery = new StringBuilder();
ctx.addUuidParameter("permissions_tenant_id", tenantId.getId());
permissionsQuery.append(" a.tenant_id = :permissions_tenant_id and ea.tenant_id = :permissions_tenant_id ");
/*
No need to check the customer id, because we already use entity id list that passed security check when we were evaluating the data query.
*/
// if (customerId != null && !customerId.isNullUid()) {
// ctx.addUuidParameter("permissions_customer_id", customerId.getId());
// ctx.addUuidParameter("permissions_device_customer_id", customerId.getId());
// ctx.addUuidParameter("permissions_asset_customer_id", customerId.getId());
// ctx.addUuidParameter("permissions_user_customer_id", customerId.getId());
// ctx.addUuidParameter("permissions_entity_view_customer_id", customerId.getId());
// permissionsQuery.append(" and (");
// permissionsQuery.append("(a.originator_type = '").append(EntityType.DEVICE.ordinal()).append("' and exists (select 1 from device cd where cd.id = a.originator_id and cd.customer_id = :permissions_device_customer_id))");
// permissionsQuery.append(" or ");
// permissionsQuery.append("(a.originator_type = '").append(EntityType.ASSET.ordinal()).append("' and exists (select 1 from asset ca where ca.id = a.originator_id and ca.customer_id = :permissions_device_customer_id))");
// permissionsQuery.append(" or ");
// permissionsQuery.append("(a.originator_type = '").append(EntityType.CUSTOMER.ordinal()).append("' and exists (select 1 from customer cc where cc.id = a.originator_id and cc.id = :permissions_customer_id))");
// permissionsQuery.append(" or ");
// permissionsQuery.append("(a.originator_type = '").append(EntityType.USER.ordinal()).append("' and exists (select 1 from tb_user cu where cu.id = a.originator_id and cu.customer_id = :permissions_user_customer_id))");
// permissionsQuery.append(" or ");
// permissionsQuery.append("(a.originator_type = '").append(EntityType.ENTITY_VIEW.ordinal()).append("' and exists (select 1 from entity_view cv where cv.id = a.originator_id and cv.customer_id = :permissions_entity_view_customer_id))");
// permissionsQuery.append(")");
// }
return permissionsQuery.toString();
}

599
dao/src/main/resources/sql/schema-entities-hsql.sql

@ -1,599 +0,0 @@
--
-- Copyright © 2016-2022 The Thingsboard Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
SET DATABASE SQL SYNTAX PGS TRUE;
SET DATABASE TRANSACTION CONTROL MVCC;
CREATE TABLE IF NOT EXISTS admin_settings (
id uuid NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY,
created_time bigint NOT NULL,
json_value varchar,
key varchar(255)
);
CREATE TABLE IF NOT EXISTS alarm (
id uuid NOT NULL CONSTRAINT alarm_pkey PRIMARY KEY,
created_time bigint NOT NULL,
ack_ts bigint,
clear_ts bigint,
additional_info varchar,
end_ts bigint,
originator_id uuid,
originator_type integer,
propagate boolean,
severity varchar(255),
start_ts bigint,
status varchar(255),
tenant_id uuid,
customer_id uuid,
propagate_relation_types varchar,
type varchar(255)
);
CREATE TABLE IF NOT EXISTS entity_alarm (
tenant_id uuid NOT NULL,
entity_type varchar(32),
entity_id uuid NOT NULL,
created_time bigint NOT NULL,
alarm_type varchar(255) NOT NULL,
customer_id uuid,
alarm_id uuid,
CONSTRAINT entity_alarm_pkey PRIMARY KEY(entity_id, alarm_id),
CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS asset (
id uuid NOT NULL CONSTRAINT asset_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
customer_id uuid,
name varchar(255),
label varchar(255),
search_text varchar(255),
tenant_id uuid,
type varchar(255),
CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name)
);
CREATE TABLE IF NOT EXISTS audit_log (
id uuid NOT NULL CONSTRAINT audit_log_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid,
customer_id uuid,
entity_id uuid,
entity_type varchar(255),
entity_name varchar(255),
user_id uuid,
user_name varchar(255),
action_type varchar(255),
action_data varchar(1000000),
action_status varchar(255),
action_failure_details varchar(1000000)
);
CREATE TABLE IF NOT EXISTS attribute_kv (
entity_type varchar(255),
entity_id uuid,
attribute_type varchar(255),
attribute_key varchar(255),
bool_v boolean,
str_v varchar(10000000),
long_v bigint,
dbl_v double precision,
json_v varchar(10000000),
last_update_ts bigint,
CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key)
);
CREATE TABLE IF NOT EXISTS component_descriptor (
id uuid NOT NULL CONSTRAINT component_descriptor_pkey PRIMARY KEY,
created_time bigint NOT NULL,
actions varchar(255),
clazz varchar UNIQUE,
configuration_descriptor varchar,
name varchar(255),
scope varchar(255),
search_text varchar(255),
type varchar(255)
);
CREATE TABLE IF NOT EXISTS customer (
id uuid NOT NULL CONSTRAINT customer_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
address varchar,
address2 varchar,
city varchar(255),
country varchar(255),
email varchar(255),
phone varchar(255),
search_text varchar(255),
state varchar(255),
tenant_id uuid,
title varchar(255),
zip varchar(255)
);
CREATE TABLE IF NOT EXISTS dashboard (
id uuid NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY,
created_time bigint NOT NULL,
configuration varchar(10000000),
assigned_customers varchar(1000000),
search_text varchar(255),
tenant_id uuid,
title varchar(255),
mobile_hide boolean DEFAULT false,
mobile_order int,
image varchar(1000000)
);
CREATE TABLE IF NOT EXISTS rule_chain (
id uuid NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
configuration varchar(10000000),
name varchar(255),
type varchar(255),
first_rule_node_id uuid,
root boolean,
debug_mode boolean,
search_text varchar(255),
tenant_id uuid
);
CREATE TABLE IF NOT EXISTS rule_node (
id uuid NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY,
created_time bigint NOT NULL,
rule_chain_id uuid,
additional_info varchar,
configuration varchar(10000000),
type varchar(255),
name varchar(255),
debug_mode boolean,
search_text varchar(255)
);
CREATE TABLE IF NOT EXISTS rule_node_state (
id uuid NOT NULL CONSTRAINT rule_node_state_pkey PRIMARY KEY,
created_time bigint NOT NULL,
rule_node_id uuid NOT NULL,
entity_type varchar(32) NOT NULL,
entity_id uuid NOT NULL,
state_data varchar(16384) NOT NULL,
CONSTRAINT rule_node_state_unq_key UNIQUE (rule_node_id, entity_id),
CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS ota_package (
id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
device_profile_id uuid ,
type varchar(32) NOT NULL,
title varchar(255) NOT NULL,
version varchar(255) NOT NULL,
tag varchar(255),
url varchar(255),
file_name varchar(255),
content_type varchar(255),
checksum_algorithm varchar(32),
checksum varchar(1020),
data binary,
data_size bigint,
additional_info varchar,
search_text varchar(255),
CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
);
CREATE TABLE IF NOT EXISTS device_profile (
id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
created_time bigint NOT NULL,
name varchar(255),
type varchar(255),
image varchar(1000000),
transport_type varchar(255),
provision_type varchar(255),
profile_data jsonb,
description varchar,
search_text varchar(255),
is_default boolean,
tenant_id uuid,
firmware_id uuid,
software_id uuid,
default_rule_chain_id uuid,
default_dashboard_id uuid,
default_queue_name varchar(255),
provision_device_key varchar,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id),
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id),
CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id)
);
CREATE TABLE IF NOT EXISTS device (
id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
customer_id uuid,
device_profile_id uuid NOT NULL,
device_data jsonb,
type varchar(255),
name varchar(255),
label varchar(255),
search_text varchar(255),
tenant_id uuid,
firmware_id uuid,
software_id uuid,
CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id),
CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id),
CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id)
);
CREATE TABLE IF NOT EXISTS device_credentials (
id uuid NOT NULL CONSTRAINT device_credentials_pkey PRIMARY KEY,
created_time bigint NOT NULL,
credentials_id varchar,
credentials_type varchar(255),
credentials_value varchar,
device_id uuid,
CONSTRAINT device_credentials_id_unq_key UNIQUE (credentials_id),
CONSTRAINT device_credentials_device_id_unq_key UNIQUE (device_id)
);
CREATE TABLE IF NOT EXISTS event (
id uuid NOT NULL CONSTRAINT event_pkey PRIMARY KEY,
created_time bigint NOT NULL,
body varchar(10000000),
entity_id uuid,
entity_type varchar(255),
event_type varchar(255),
event_uid varchar(255),
tenant_id uuid,
ts bigint NOT NULL,
CONSTRAINT event_unq_key UNIQUE (tenant_id, entity_type, entity_id, event_type, event_uid)
);
CREATE TABLE IF NOT EXISTS relation (
from_id uuid,
from_type varchar(255),
to_id uuid,
to_type varchar(255),
relation_type_group varchar(255),
relation_type varchar(255),
additional_info varchar,
CONSTRAINT relation_pkey PRIMARY KEY (from_id, from_type, relation_type_group, relation_type, to_id, to_type)
);
CREATE TABLE IF NOT EXISTS tb_user (
id uuid NOT NULL CONSTRAINT tb_user_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
authority varchar(255),
customer_id uuid,
email varchar(255) UNIQUE,
first_name varchar(255),
last_name varchar(255),
search_text varchar(255),
tenant_id uuid
);
CREATE TABLE IF NOT EXISTS tenant_profile (
id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY,
created_time bigint NOT NULL,
name varchar(255),
profile_data jsonb,
description varchar,
search_text varchar(255),
is_default boolean,
isolated_tb_core boolean,
isolated_tb_rule_engine boolean,
CONSTRAINT tenant_profile_name_unq_key UNIQUE (name)
);
CREATE TABLE IF NOT EXISTS tenant (
id uuid NOT NULL CONSTRAINT tenant_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
tenant_profile_id uuid NOT NULL,
address varchar,
address2 varchar,
city varchar(255),
country varchar(255),
email varchar(255),
phone varchar(255),
region varchar(255),
search_text varchar(255),
state varchar(255),
title varchar(255),
zip varchar(255),
CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id)
);
CREATE TABLE IF NOT EXISTS user_credentials (
id uuid NOT NULL CONSTRAINT user_credentials_pkey PRIMARY KEY,
created_time bigint NOT NULL,
activate_token varchar(255) UNIQUE,
enabled boolean,
password varchar(255),
reset_token varchar(255) UNIQUE,
user_id uuid UNIQUE
);
CREATE TABLE IF NOT EXISTS widget_type (
id uuid NOT NULL CONSTRAINT widget_type_pkey PRIMARY KEY,
created_time bigint NOT NULL,
alias varchar(255),
bundle_alias varchar(255),
descriptor varchar(1000000),
name varchar(255),
tenant_id uuid,
image varchar(1000000),
description varchar(255)
);
CREATE TABLE IF NOT EXISTS widgets_bundle (
id uuid NOT NULL CONSTRAINT widgets_bundle_pkey PRIMARY KEY,
created_time bigint NOT NULL,
alias varchar(255),
search_text varchar(255),
tenant_id uuid,
title varchar(255),
image varchar(1000000),
description varchar(255)
);
CREATE TABLE IF NOT EXISTS entity_view (
id uuid NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY,
created_time bigint NOT NULL,
entity_id uuid,
entity_type varchar(255),
tenant_id uuid,
customer_id uuid,
type varchar(255),
name varchar(255),
keys varchar(10000000),
start_ts bigint,
end_ts bigint,
search_text varchar(255),
additional_info varchar
);
CREATE TABLE IF NOT EXISTS ts_kv_latest (
entity_id uuid NOT NULL,
key int NOT NULL,
ts bigint NOT NULL,
bool_v boolean,
str_v varchar(10000000),
long_v bigint,
dbl_v double precision,
json_v varchar(10000000),
CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
);
CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
key varchar(255) NOT NULL,
key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE,
CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
);
CREATE TABLE IF NOT EXISTS oauth2_params (
id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY,
enabled boolean,
tenant_id uuid,
created_time bigint NOT NULL
);
CREATE TABLE IF NOT EXISTS oauth2_registration (
id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL,
created_time bigint NOT NULL,
additional_info varchar,
client_id varchar(255),
client_secret varchar(2048),
authorization_uri varchar(255),
token_uri varchar(255),
scope varchar(255),
platforms varchar(255),
user_info_uri varchar(255),
user_name_attribute_name varchar(255),
jwk_set_uri varchar(255),
client_authentication_method varchar(255),
login_button_label varchar(255),
login_button_icon varchar(255),
allow_user_creation boolean,
activate_user boolean,
type varchar(31),
basic_email_attribute_key varchar(31),
basic_first_name_attribute_key varchar(31),
basic_last_name_attribute_key varchar(31),
basic_tenant_name_strategy varchar(31),
basic_tenant_name_pattern varchar(255),
basic_customer_name_pattern varchar(255),
basic_default_dashboard_name varchar(255),
basic_always_full_screen boolean,
custom_url varchar(255),
custom_username varchar(255),
custom_password varchar(255),
custom_send_token boolean,
CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS oauth2_domain (
id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL,
created_time bigint NOT NULL,
domain_name varchar(255),
domain_scheme varchar(31),
CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE,
CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme)
);
CREATE TABLE IF NOT EXISTS oauth2_mobile (
id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL,
created_time bigint NOT NULL,
pkg_name varchar(255),
app_secret varchar(2048),
CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE,
CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name)
);
CREATE TABLE IF NOT EXISTS oauth2_client_registration_template (
id uuid NOT NULL CONSTRAINT oauth2_client_registration_template_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
provider_id varchar(255),
authorization_uri varchar(255),
token_uri varchar(255),
scope varchar(255),
user_info_uri varchar(255),
user_name_attribute_name varchar(255),
jwk_set_uri varchar(255),
client_authentication_method varchar(255),
type varchar(31),
basic_email_attribute_key varchar(31),
basic_first_name_attribute_key varchar(31),
basic_last_name_attribute_key varchar(31),
basic_tenant_name_strategy varchar(31),
basic_tenant_name_pattern varchar(255),
basic_customer_name_pattern varchar(255),
basic_default_dashboard_name varchar(255),
basic_always_full_screen boolean,
comment varchar,
login_button_icon varchar(255),
login_button_label varchar(255),
help_link varchar(255),
CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id)
);
-- Deprecated
CREATE TABLE IF NOT EXISTS oauth2_client_registration_info (
id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY,
enabled boolean,
created_time bigint NOT NULL,
additional_info varchar,
client_id varchar(255),
client_secret varchar(255),
authorization_uri varchar(255),
token_uri varchar(255),
scope varchar(255),
user_info_uri varchar(255),
user_name_attribute_name varchar(255),
jwk_set_uri varchar(255),
client_authentication_method varchar(255),
login_button_label varchar(255),
login_button_icon varchar(255),
allow_user_creation boolean,
activate_user boolean,
type varchar(31),
basic_email_attribute_key varchar(31),
basic_first_name_attribute_key varchar(31),
basic_last_name_attribute_key varchar(31),
basic_tenant_name_strategy varchar(31),
basic_tenant_name_pattern varchar(255),
basic_customer_name_pattern varchar(255),
basic_default_dashboard_name varchar(255),
basic_always_full_screen boolean,
custom_url varchar(255),
custom_username varchar(255),
custom_password varchar(255),
custom_send_token boolean
);
-- Deprecated
CREATE TABLE IF NOT EXISTS oauth2_client_registration (
id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY,
created_time bigint NOT NULL,
domain_name varchar(255),
domain_scheme varchar(31),
client_registration_info_id uuid
);
CREATE TABLE IF NOT EXISTS api_usage_state (
id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid,
entity_type varchar(32),
entity_id uuid,
transport varchar(32),
db_storage varchar(32),
re_exec varchar(32),
js_exec varchar(32),
email_exec varchar(32),
sms_exec varchar(32),
alarm_exec varchar(32),
CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
);
CREATE TABLE IF NOT EXISTS resource (
id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
title varchar(255) NOT NULL,
resource_type varchar(32) NOT NULL,
resource_key varchar(255) NOT NULL,
search_text varchar(255),
file_name varchar(255) NOT NULL,
data varchar,
CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)
);
CREATE TABLE IF NOT EXISTS edge (
id uuid NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
customer_id uuid,
root_rule_chain_id uuid,
type varchar(255),
name varchar(255),
label varchar(255),
routing_key varchar(255),
secret varchar(255),
edge_license_key varchar(30),
cloud_endpoint varchar(255),
search_text varchar(255),
tenant_id uuid,
CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key)
);
CREATE TABLE IF NOT EXISTS edge_event (
id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY,
created_time bigint NOT NULL,
edge_id uuid,
edge_event_type varchar(255),
edge_event_uid varchar(255),
entity_id uuid,
edge_event_action varchar(255),
body varchar(10000000),
tenant_id uuid,
ts bigint NOT NULL
);
CREATE TABLE IF NOT EXISTS rpc (
id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
device_id uuid NOT NULL,
expiration_time bigint NOT NULL,
request varchar(10000000) NOT NULL,
response varchar(10000000),
additional_info varchar(10000000),
status varchar(255) NOT NULL
);

4
dao/src/main/resources/sql/schema-entities.sql

@ -55,7 +55,9 @@ CREATE TABLE IF NOT EXISTS alarm (
tenant_id uuid,
customer_id uuid,
propagate_relation_types varchar,
type varchar(255)
type varchar(255),
propagate_to_owner boolean,
propagate_to_tenant boolean
);
CREATE TABLE IF NOT EXISTS entity_alarm (

41
dao/src/main/resources/sql/schema-ts-hsql.sql

@ -1,41 +0,0 @@
--
-- Copyright © 2016-2022 The Thingsboard Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
SET DATABASE SQL SYNTAX PGS TRUE;
SET DATABASE TRANSACTION CONTROL MVCC;
CREATE TABLE IF NOT EXISTS ts_kv (
entity_id uuid NOT NULL,
key int NOT NULL,
ts bigint NOT NULL,
bool_v boolean,
str_v varchar(10000000),
long_v bigint,
dbl_v double precision,
json_v varchar(10000000),
CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts)
);
CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
key varchar(255) NOT NULL,
key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE,
CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
);
CREATE FUNCTION to_uuid(IN entity_id varchar)
RETURNS UUID
RETURN UUID(substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) ||
'-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12));

20
dao/src/main/resources/sql/schema-types-hsql.sql

@ -1,20 +0,0 @@
--
-- Copyright © 2016-2022 The Thingsboard Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
DROP TYPE json IF EXISTS;
CREATE TYPE json AS varchar;
DROP TYPE jsonb IF EXISTS;
CREATE TYPE jsonb AS other;

78
dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java

@ -264,10 +264,10 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
PageData<AlarmData> tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), toQuery(pageLink), Arrays.asList(tenantDevice.getId(), customerDevice.getId()));
PageData<AlarmData> tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Arrays.asList(tenantDevice.getId(), customerDevice.getId()));
Assert.assertEquals(2, tenantAlarms.getData().size());
PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, customer.getId(), toQuery(pageLink), Collections.singletonList(customerDevice.getId()));
PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customerDevice.getId()));
Assert.assertEquals(1, customerAlarms.getData().size());
Assert.assertEquals(deviceAlarm, customerAlarms.getData().get(0));
@ -283,7 +283,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
}
@Test
public void testFindPopagatedCustomerAlarm() throws ExecutionException, InterruptedException {
public void testFindPropagatedCustomerAssetAlarm() throws ExecutionException, InterruptedException {
Customer customer = new Customer();
customer.setTitle("TestCustomer");
customer.setTenantId(tenantId);
@ -341,7 +341,63 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
//TEST that propagated alarms are visible on the asset level.
PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, customer.getId(), toQuery(pageLink), Collections.singletonList(customerAsset.getId()));
PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customerAsset.getId()));
Assert.assertEquals(1, customerAlarms.getData().size());
Assert.assertEquals(customerAlarm, customerAlarms.getData().get(0));
}
@Test
public void testFindPropagatedToOwnerAndTenantAlarm() {
Customer customer = new Customer();
customer.setTitle("TestCustomer");
customer.setTenantId(tenantId);
customer = customerService.saveCustomer(customer);
Device device = new Device();
device.setName("TestTenantDevice");
device.setType("default");
device.setTenantId(tenantId);
device.setCustomerId(customer.getId());
device = deviceService.saveDevice(device);
long ts = System.currentTimeMillis();
Alarm tenantAlarm = Alarm.builder().tenantId(tenantId)
.originator(device.getId())
.type("Propagated To Tenant")
.propagateToTenant(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm);
tenantAlarm = result.getAlarm();
Alarm customerAlarm = Alarm.builder().tenantId(tenantId)
.originator(device.getId())
.type("Propagated to Customer")
.propagate(false)
.propagateToOwner(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.startTs(ts).build();
result = alarmService.createOrUpdateAlarm(customerAlarm);
customerAlarm = result.getAlarm();
AlarmDataPageLink pageLink = new AlarmDataPageLink();
pageLink.setPage(0);
pageLink.setPageSize(10);
pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime")));
pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(true);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
//TEST that propagated alarms are visible on the asset level.
PageData<AlarmData> tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(tenantId));
Assert.assertEquals(1, tenantAlarms.getData().size());
Assert.assertEquals(tenantAlarm, tenantAlarms.getData().get(0));
//TEST that propagated alarms are visible on the asset level.
PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customer.getId()));
Assert.assertEquals(1, customerAlarms.getData().size());
Assert.assertEquals(customerAlarm, customerAlarms.getData().get(0));
}
@ -444,7 +500,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), toQuery(pageLink), Collections.singletonList(childId));
PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
@ -460,13 +516,13 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), toQuery(pageLink), Collections.singletonList(childId));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, new Alarm(alarms.getData().get(0)));
pageLink.setSearchPropagatedAlarms(true);
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), toQuery(pageLink), Collections.singletonList(childId));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, new Alarm(alarms.getData().get(0)));
@ -487,7 +543,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), toQuery(pageLink), Collections.singletonList(childId));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));
@ -503,7 +559,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), toQuery(pageLink), Collections.singletonList(parentId));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(parentId));
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));
@ -548,7 +604,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), toQuery(pageLink), Collections.singletonList(parentId));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(parentId));
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));
@ -566,7 +622,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), toQuery(pageLink), Collections.singletonList(childId));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));

2
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java

@ -61,5 +61,5 @@ public interface RuleEngineAlarmService {
AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus);
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
}

10
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java

@ -145,10 +145,14 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
if (msgAlarm != null) {
existingAlarm.setSeverity(msgAlarm.getSeverity());
existingAlarm.setPropagate(msgAlarm.isPropagate());
existingAlarm.setPropagateToOwner(msgAlarm.isPropagateToOwner());
existingAlarm.setPropagateToTenant(msgAlarm.isPropagateToTenant());
existingAlarm.setPropagateRelationTypes(msgAlarm.getPropagateRelationTypes());
} else {
existingAlarm.setSeverity(processAlarmSeverity(msg));
existingAlarm.setPropagate(config.isPropagate());
existingAlarm.setPropagateToOwner(config.isPropagateToOwner());
existingAlarm.setPropagateToTenant(config.isPropagateToTenant());
existingAlarm.setPropagateRelationTypes(relationTypes);
}
existingAlarm.setDetails(details);
@ -160,6 +164,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
}
private Alarm buildAlarm(TbMsg msg, JsonNode details, TenantId tenantId) {
long ts = msg.getMetaDataTs();
return Alarm.builder()
.tenantId(tenantId)
.originator(msg.getOriginator())
@ -168,9 +173,8 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
.propagate(config.isPropagate())
.type(TbNodeUtils.processPattern(this.config.getAlarmType(), msg))
.propagateRelationTypes(relationTypes)
//todo-vp: alarm date should be taken from Message or current Time should be used?
// .startTs(System.currentTimeMillis())
// .endTs(System.currentTimeMillis())
.startTs(ts)
.endTs(ts)
.details(details)
.build();
}

4
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeConfiguration.java

@ -27,6 +27,8 @@ public class TbCreateAlarmNodeConfiguration extends TbAbstractAlarmNodeConfigura
private String severity;
private boolean propagate;
private boolean propagateToOwner;
private boolean propagateToTenant;
private boolean useMessageAlarmData;
private boolean dynamicSeverity;
@ -39,6 +41,8 @@ public class TbCreateAlarmNodeConfiguration extends TbAbstractAlarmNodeConfigura
configuration.setAlarmType("General Alarm");
configuration.setSeverity(AlarmSeverity.CRITICAL.name());
configuration.setPropagate(false);
configuration.setPropagateToOwner(false);
configuration.setPropagateToTenant(false);
configuration.setUseMessageAlarmData(false);
configuration.setRelationTypes(Collections.emptyList());
configuration.setDynamicSeverity(false);

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java

@ -28,7 +28,6 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.TbRelationTypes;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.plugin.ComponentType;
@ -82,7 +81,7 @@ public class CalculateDeltaNode implements TbNode {
DonAsynchron.withCallback(getLastValue(msg.getOriginator()),
previousData -> {
double currentValue = json.get(inputKey).asDouble();
long currentTs = TbMsgTimeseriesNode.getTs(msg);
long currentTs = msg.getMetaDataTs();
if (useCache) {
cache.put(msg.getOriginator(), new ValueWithTs(currentTs, currentValue));

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java

@ -258,6 +258,8 @@ class AlarmState {
currentAlarm.setOriginator(originator);
currentAlarm.setTenantId(ctx.getTenantId());
currentAlarm.setPropagate(alarmDefinition.isPropagate());
currentAlarm.setPropagateToOwner(alarmDefinition.isPropagateToOwner());
currentAlarm.setPropagateToTenant(alarmDefinition.isPropagateToTenant());
if (alarmDefinition.getPropagateRelationTypes() != null) {
currentAlarm.setPropagateRelationTypes(alarmDefinition.getPropagateRelationTypes());
}

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java

@ -21,7 +21,6 @@ import org.springframework.util.StringUtils;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.profile.state.PersistedAlarmState;
import org.thingsboard.rule.engine.profile.state.PersistedDeviceState;
import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
@ -252,7 +251,7 @@ class DeviceState {
protected boolean processTelemetry(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {
boolean stateChanged = false;
Map<Long, List<KvEntry>> tsKvMap = JsonConverter.convertToSortedTelemetry(new JsonParser().parse(msg.getData()), TbMsgTimeseriesNode.getTs(msg));
Map<Long, List<KvEntry>> tsKvMap = JsonConverter.convertToSortedTelemetry(new JsonParser().parse(msg.getData()), msg.getMetaDataTs());
// iterate over data by ts (ASC order).
for (Map.Entry<Long, List<KvEntry>> entry : tsKvMap.entrySet()) {
Long ts = entry.getKey();

16
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java

@ -111,21 +111,7 @@ public class TbMsgTimeseriesNode implements TbNode {
}
public static long computeTs(TbMsg msg, boolean ignoreMetadataTs) {
return ignoreMetadataTs ? System.currentTimeMillis() : getTs(msg);
}
public static long getTs(TbMsg msg) {
long ts = -1;
String tsStr = msg.getMetaData().getValue("ts");
if (!StringUtils.isEmpty(tsStr)) {
try {
ts = Long.parseLong(tsStr);
} catch (NumberFormatException ignored) {
}
} else {
ts = msg.getTs();
}
return ts;
return ignoreMetadataTs ? System.currentTimeMillis() : msg.getMetaDataTs();
}
@Override

2
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

3
ui-ngx/angular.json

@ -89,8 +89,7 @@
"scripts": [
"node_modules/tinycolor2/dist/tinycolor-min.js",
"node_modules/split.js/dist/split.min.js",
"node_modules/systemjs/dist/system.js",
"node_modules/systemjs-babel/dist/systemjs-babel.js",
"node_modules/systemjs/dist/system.min.js",
"node_modules/marked/lib/marked.js",
"node_modules/prismjs/prism.js",
"node_modules/prismjs/components/prism-css.min.js",

1
ui-ngx/package.json

@ -89,7 +89,6 @@
"screenfull": "^6.0.0",
"split.js": "^1.6.4",
"systemjs": "6.11.0",
"systemjs-babel": "^0.3.1",
"tinycolor2": "^1.4.2",
"tooltipster": "^4.2.8",
"tslib": "^2.3.1",

1
ui-ngx/src/app/core/services/menu.models.ts

@ -23,7 +23,6 @@ export interface MenuSection extends HasUUID{
type: MenuSectionType;
path: string;
icon: string;
notExact?: boolean;
isMdiIcon?: boolean;
height?: string;
pages?: Array<MenuSection>;

5
ui-ngx/src/app/core/services/menu.service.ts

@ -235,7 +235,6 @@ export class MenuService {
name: 'home.home',
type: 'link',
path: '/home',
notExact: true,
icon: 'home'
},
{
@ -344,8 +343,7 @@ export class MenuService {
name: 'api-usage.api-usage',
type: 'link',
path: '/usage',
icon: 'insert_chart',
notExact: true
icon: 'insert_chart'
},
{
id: guid(),
@ -517,7 +515,6 @@ export class MenuService {
name: 'home.home',
type: 'link',
path: '/home',
notExact: true,
icon: 'home'
},
{

2
ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts

@ -128,7 +128,7 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
}).afterClosed().subscribe(
(res) => {
if (res) {
this.table.updateData();
this.updateData();
}
}
);

2
ui-ngx/src/app/modules/home/components/alarm/alarm-table-header.component.ts

@ -43,6 +43,6 @@ export class AlarmTableHeaderComponent extends EntityTableHeaderComponent<AlarmI
searchStatusChanged(searchStatus: AlarmSearchStatus) {
this.alarmTableConfig.searchStatus = searchStatus;
this.alarmTableConfig.table.resetSortAndFilter(true, true);
this.alarmTableConfig.getTable().resetSortAndFilter(true, true);
}
}

15
ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts

@ -48,7 +48,7 @@ import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component';
import { EdgeService } from '@core/http/edge.service';
import { concatMap, map } from 'rxjs/operators';
import { EntityService } from "@core/http/entity.service";
import { EntityService } from '@core/http/entity.service';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { ActionNotificationShow } from '@core/notification/notification.actions';
@ -94,9 +94,9 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
private onUpdate(attributes: any): void {
this.queueStartTs = 0;
let edge = attributes.reduce(function (map, attribute) {
map[attribute.key] = attribute;
return map;
const edge = attributes.reduce((attrMap, attribute) => {
attrMap[attribute.key] = attribute;
return attrMap;
}, {});
this.queueStartTs = edge.queueStartTs && edge.queueStartTs.value ? edge.queueStartTs.value : 0;
}
@ -114,7 +114,8 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
new EntityTableColumn<EdgeEvent>('status', 'event.status', '10%',
(entity) => this.updateEdgeEventStatus(entity.createdTime),
entity => ({
color: this.isPending(entity.createdTime) ? edgeEventStatusColor.get(EdgeEventStatus.PENDING) : edgeEventStatusColor.get(EdgeEventStatus.DEPLOYED)
color: this.isPending(entity.createdTime) ? edgeEventStatusColor.get(EdgeEventStatus.PENDING) :
edgeEventStatusColor.get(EdgeEventStatus.DEPLOYED)
}), false),
new EntityActionTableColumn<EdgeEvent>('data', 'event.data',
{
@ -124,7 +125,7 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
onAction: ($event, entity) =>
{
this.prepareEdgeEventContent(entity).subscribe(
(content) => this.showEdgeEventContent($event, content,'event.data'),
(content) => this.showEdgeEventContent($event, content, 'event.data'),
() => this.showEntityNotFoundError()
);
}
@ -132,7 +133,7 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig<EdgeEvent, TimePa
'40px'),
);
if (updateTableColumns) {
this.table.columnsUpdated(true);
this.getTable().columnsUpdated(true);
}
}

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

@ -23,8 +23,9 @@
position="end"
[opened]="isDetailsOpen">
<tb-entity-details-panel
#entityDetailsPanel
[entitiesTableConfig]="entitiesTableConfig"
[entityId]="dataSource.currentEntity?.id"
[entityId]="isDetailsOpen ? dataSource.currentEntity?.id : null"
(closeEntityDetails)="isDetailsOpen = false; detailsPanelOpened.emit(isDetailsOpen);"
(entityUpdated)="onEntityUpdated($event)"
(entityAction)="onEntityAction($event)"

10
ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts

@ -64,6 +64,7 @@ import { HasUUID } from '@shared/models/id/has-uuid';
import { ResizeObserver } from '@juggle/resize-observer';
import { hidePageSizePixelValue } from '@shared/models/constants';
import { IEntitiesTableComponent } from '@home/models/entity/entity-table-component.models';
import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component';
@Component({
selector: 'tb-entities-table',
@ -118,6 +119,8 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
@ViewChild('entityDetailsPanel') entityDetailsPanel: EntityDetailsPanelComponent;
private updateDataSubscription: Subscription;
private viewInited = false;
@ -172,6 +175,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
private init(entitiesTableConfig: EntityTableConfig<BaseData<HasId>>) {
this.isDetailsOpen = false;
this.entitiesTableConfig = entitiesTableConfig;
this.pageMode = this.entitiesTableConfig.pageMode;
if (this.entitiesTableConfig.headerComponent) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.entitiesTableConfig.headerComponent);
const viewContainerRef = this.entityTableHeaderAnchor.viewContainerRef;
@ -181,7 +185,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
headerComponent.entitiesTableConfig = this.entitiesTableConfig;
}
this.entitiesTableConfig.table = this;
this.entitiesTableConfig.setTable(this);
this.translations = this.entitiesTableConfig.entityTranslations;
this.headerActionDescriptors = [...this.entitiesTableConfig.headerActionDescriptors];
@ -233,7 +237,6 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
this.displayPagination = this.entitiesTableConfig.displayPagination;
this.defaultPageSize = this.entitiesTableConfig.defaultPageSize;
this.pageMode = this.entitiesTableConfig.pageMode;
this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3];
if (this.entitiesTableConfig.useTimePageLink) {
@ -392,6 +395,9 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
}
}
this.dataSource.loadEntities(this.pageLink);
if (this.isDetailsOpen && this.entityDetailsPanel) {
this.entityDetailsPanel.reloadEntity();
}
}
private dataLoaded(col?: number, row?: number) {

61
ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts

@ -32,10 +32,11 @@ import { ActivatedRoute, Router } from '@angular/router';
import { FormGroup } from '@angular/forms';
import { AssetId } from '@shared/models/id/asset-id';
import { TranslateService } from '@ngx-translate/core';
import { deepClone, mergeDeep } from '@core/utils';
import { deepClone } from '@core/utils';
import { BroadcastService } from '@core/services/broadcast.service';
import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component';
import { DialogService } from '@core/services/dialog.service';
import { IEntityDetailsPageComponent } from '@home/models/entity/entity-details-page-component.models';
@Component({
selector: 'tb-entity-details-page',
@ -43,7 +44,7 @@ import { DialogService } from '@core/services/dialog.service';
styleUrls: ['./entity-details-page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EntityDetailsPageComponent extends EntityDetailsPanelComponent implements OnInit, OnDestroy {
export class EntityDetailsPageComponent extends EntityDetailsPanelComponent implements IEntityDetailsPageComponent, OnInit, OnDestroy {
headerTitle: string;
headerSubtitle: string;
@ -54,6 +55,7 @@ export class EntityDetailsPageComponent extends EntityDetailsPanelComponent impl
if (this.entitiesTableConfigValue !== entitiesTableConfig) {
this.entitiesTableConfigValue = entitiesTableConfig;
if (this.entitiesTableConfigValue) {
this.entitiesTableConfigValue.setEntityDetailsPage(this);
this.isEdit = false;
this.entity = null;
}
@ -81,9 +83,6 @@ export class EntityDetailsPageComponent extends EntityDetailsPanelComponent impl
ngOnInit() {
this.headerSubtitle = '';
this.route.paramMap.subscribe( paramMap => {
this.entityId = new AssetId(paramMap.get('entityId'));
});
this.headerSubtitle = this.translate.instant(this.entitiesTableConfig.entityTranslations.details);
super.init();
this.entityComponent.isDetailsPage = true;
@ -92,6 +91,15 @@ export class EntityDetailsPageComponent extends EntityDetailsPanelComponent impl
this.deleteEntity(action.event, action.entity);
}
}));
this.subscriptions.push(this.route.paramMap.subscribe( paramMap => {
if (this.entitiesTableConfig) {
const entityType = this.entitiesTableConfig.entityType;
const id = paramMap.get('entityId');
this.currentEntityId = { id, entityType };
this.reload();
this.selectedTab = 0;
}
}));
}
ngOnDestroy() {
@ -99,20 +107,9 @@ export class EntityDetailsPageComponent extends EntityDetailsPanelComponent impl
}
reload(): void {
this.isEdit = false;
this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe(
(entity) => {
this.entity = entity;
this.broadcast.broadcast('updateBreadcrumb');
this.isReadOnly = this.entitiesTableConfig.detailsReadonly(entity);
this.headerTitle = this.entitiesTableConfig.entityTitle(entity);
this.entityComponent.entity = entity;
this.entityComponent.isEdit = false;
if (this.entityTabsComponent) {
this.entityTabsComponent.entity = entity;
}
}
);
this.reloadEntity().subscribe(() => {
this.onUpdateEntity();
});
}
onToggleDetailsEditMode() {
@ -136,29 +133,23 @@ export class EntityDetailsPageComponent extends EntityDetailsPanelComponent impl
}
onApplyDetails() {
if (this.detailsForm && this.detailsForm.valid) {
const editingEntity = {...this.editingEntity, ...this.entityComponent.entityFormValue()};
if (this.editingEntity.hasOwnProperty('additionalInfo')) {
editingEntity.additionalInfo =
mergeDeep((this.editingEntity as any).additionalInfo, this.entityComponent.entityFormValue()?.additionalInfo);
this.saveEntity(false).subscribe((entity) => {
if (entity) {
this.onUpdateEntity();
}
this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity).subscribe(
(entity) => {
this.entity = entity;
this.entityComponent.entity = entity;
if (this.entityTabsComponent) {
this.entityTabsComponent.entity = entity;
}
this.isEdit = false;
}
);
}
});
}
confirmForm(): FormGroup {
return this.detailsForm;
}
private onUpdateEntity() {
this.broadcast.broadcast('updateBreadcrumb');
this.isReadOnly = this.entitiesTableConfig.detailsReadonly(this.entity);
this.headerTitle = this.entitiesTableConfig.entityTitle(this.entity);
}
private deleteEntity($event: Event, entity: BaseData<HasId>) {
if ($event) {
$event.stopPropagation();

30
ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts

@ -34,16 +34,17 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { BaseData, HasId } from '@shared/models/base-data';
import { BaseData, HasId, hasIdEquals } from '@shared/models/base-data';
import { EntityType, EntityTypeResource, EntityTypeTranslation } from '@shared/models/entity-type.models';
import { FormGroup } from '@angular/forms';
import { EntityComponent } from './entity.component';
import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { Subscription } from 'rxjs';
import { Observable, ReplaySubject, Subscription } from 'rxjs';
import { MatTab, MatTabGroup } from '@angular/material/tabs';
import { EntityTabsComponent } from '@home/components/entity/entity-tabs.component';
import { deepClone, mergeDeep } from '@core/utils';
import { entityIdEquals } from '@shared/models/id/entity-id';
@Component({
selector: 'tb-entity-details-panel',
@ -102,9 +103,11 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
@Input()
set entityId(entityId: HasId) {
if (entityId && entityId !== this.currentEntityId) {
if (!hasIdEquals(entityId, this.currentEntityId)) {
this.currentEntityId = entityId;
this.reload();
if (this.currentEntityId) {
this.reloadEntity();
}
}
}
@ -228,7 +231,8 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
return this.isEditValue && this.entitiesTableConfig.hideDetailsTabsOnEdit;
}
reload(): void {
reloadEntity(): Observable<BaseData<HasId>> {
const loadEntitySubject = new ReplaySubject<BaseData<HasId>>();
this.isEdit = false;
this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe(
(entity) => {
@ -237,8 +241,11 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
if (this.entityTabsComponent) {
this.entityTabsComponent.entity = entity;
}
loadEntitySubject.next(entity);
loadEntitySubject.complete();
}
);
return loadEntitySubject;
}
onCloseEntityDetails() {
@ -273,7 +280,8 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
}
}
saveEntity() {
saveEntity(emitEntityUpdated = true): Observable<BaseData<HasId>> {
const saveEntitySubject = new ReplaySubject<BaseData<HasId>>();
if (this.detailsForm.valid) {
const editingEntity = {...this.editingEntity, ...this.entityComponent.entityFormValue()};
if (this.editingEntity.hasOwnProperty('additionalInfo')) {
@ -288,10 +296,18 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
this.entityTabsComponent.entity = entity;
}
this.isEdit = false;
this.entityUpdated.emit(this.entity);
if (emitEntityUpdated) {
this.entityUpdated.emit(this.entity);
}
saveEntitySubject.next(entity);
saveEntitySubject.complete();
}
);
} else {
saveEntitySubject.next(null);
saveEntitySubject.complete();
}
return saveEntitySubject;
}
ngAfterViewInit(): void {

17
ui-ngx/src/app/modules/home/components/event/event-table-config.ts

@ -142,10 +142,11 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
icon: 'delete',
isEnabled: () => true,
onAction: ($event) => {
this.eventService.clearEvents(this.entityId, this.eventType, this.filterParams, this.tenantId, this.table.pageLink as TimePageLink).subscribe(
this.eventService.clearEvents(this.entityId, this.eventType, this.filterParams, this.tenantId,
this.getTable().pageLink as TimePageLink).subscribe(
() => {
this.table.paginator.pageIndex = 0;
this.table.updateData();
this.getTable().paginator.pageIndex = 0;
this.updateData();
}
);
}
@ -275,7 +276,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
break;
}
if (updateTableColumns) {
this.table.columnsUpdated(true);
this.getTable().columnsUpdated(true);
}
}
@ -345,8 +346,8 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
}
this.filterParams = {};
this.table.paginator.pageIndex = 0;
this.table.updateData();
this.getTable().paginator.pageIndex = 0;
this.updateData();
}
private editEventFilter($event: MouseEvent) {
@ -389,8 +390,8 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
componentRef.onDestroy(() => {
if (componentRef.instance.result && !isEqual(this.filterParams, componentRef.instance.result.filterParams)) {
this.filterParams = componentRef.instance.result.filterParams;
this.table.paginator.pageIndex = 0;
this.table.updateData();
this.getTable().paginator.pageIndex = 0;
this.updateData();
}
});
this.cd.detectChanges();

2
ui-ngx/src/app/modules/home/components/event/event-table-header.component.ts

@ -40,6 +40,6 @@ export class EventTableHeaderComponent extends EntityTableHeaderComponent<Event>
eventTypeChanged(eventType: EventType | DebugEventType) {
this.eventTableConfig.eventType = eventType;
this.eventTableConfig.table.resetSortAndFilter(true, true);
this.eventTableConfig.getTable().resetSortAndFilter(true, true);
}
}

13
ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html

@ -23,7 +23,11 @@
[required]="required"
(keydown)="deviceProfileEnter($event)"
(keypress)="deviceProfileEnter($event)"
[matAutocomplete]="deviceProfileAutocomplete">
[matAutocomplete]="deviceProfileAutocomplete"
[fxShow]="!showDetailsPageLink || !disabled || !selectDeviceProfileFormGroup.get('deviceProfile').value">
<a *ngIf="showDetailsPageLink && selectDeviceProfileFormGroup.get('deviceProfile').value && disabled" aria-label="Open tenant profile" [routerLink]=deviceProfileURL>
{{ displayDeviceProfileFn(selectDeviceProfileFormGroup.get('deviceProfile').value) }}
</a>
<button *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').value && !disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Clear"
@ -38,13 +42,6 @@
(click)="editDeviceProfile($event)">
<mat-icon class="material-icons">edit</mat-icon>
</button>
<button *ngIf="showDetailsPageLink && selectDeviceProfileFormGroup.get('deviceProfile').value && disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Open device profile">
<a [routerLink]=deviceProfileURL>
<mat-icon class="material-icons">open_in_new</mat-icon>
</a>
</button>
<mat-autocomplete
class="tb-autocomplete"
(closed)="onPanelClosed()"

4
ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts

@ -233,9 +233,9 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.selectDeviceProfileFormGroup.disable();
this.selectDeviceProfileFormGroup.disable({emitEvent: false});
} else {
this.selectDeviceProfileFormGroup.enable();
this.selectDeviceProfileFormGroup.enable({emitEvent: false});
}
}

13
ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.html

@ -23,7 +23,11 @@
[required]="required"
(keydown)="tenantProfileEnter($event)"
(keypress)="tenantProfileEnter($event)"
[matAutocomplete]="tenantProfileAutocomplete">
[matAutocomplete]="tenantProfileAutocomplete"
[fxShow]="!showDetailsPageLink || !disabled || !selectTenantProfileFormGroup.get('tenantProfile').value">
<a *ngIf="showDetailsPageLink && selectTenantProfileFormGroup.get('tenantProfile').value && disabled" aria-label="Open tenant profile" [routerLink]=tenantProfileURL>
{{ displayTenantProfileFn(selectTenantProfileFormGroup.get('tenantProfile').value) }}
</a>
<button *ngIf="selectTenantProfileFormGroup.get('tenantProfile').value && !disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Clear"
@ -38,13 +42,6 @@
(click)="editTenantProfile($event)">
<mat-icon class="material-icons">edit</mat-icon>
</button>
<button *ngIf="showDetailsPageLink && selectTenantProfileFormGroup.get('tenantProfile').value && disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Open device profile">
<a [routerLink]=tenantProfileURL>
<mat-icon class="material-icons">open_in_new</mat-icon>
</a>
</button>
<mat-autocomplete
class="tb-autocomplete"
#tenantProfileAutocomplete="matAutocomplete"

4
ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.ts

@ -139,9 +139,9 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor,
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.selectTenantProfileFormGroup.disable();
this.selectTenantProfileFormGroup.disable({emitEvent: false});
} else {
this.selectTenantProfileFormGroup.enable();
this.selectTenantProfileFormGroup.enable({emitEvent: false});
}
}

2
ui-ngx/src/app/modules/home/menu/menu-link.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<a mat-button routerLinkActive="tb-active" [routerLinkActiveOptions]="{exact: !section.notExact}" routerLink="{{section.path}}">
<a mat-button routerLinkActive="tb-active" [routerLinkActiveOptions]="{paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored'}" routerLink="{{section.path}}">
<mat-icon *ngIf="!section.isMdiIcon && section.icon != null" class="material-icons">{{section.icon}}</mat-icon>
<mat-icon *ngIf="section.isMdiIcon && section.icon != null" [svgIcon]="section.icon"></mat-icon>
<span>{{section.name | translate}}</span>

2
ui-ngx/src/app/modules/home/menu/menu-toggle.component.html

@ -16,7 +16,7 @@
-->
<a mat-button class="tb-button-toggle"
routerLinkActive="tb-active" [routerLinkActiveOptions]="{exact: true}" routerLink="{{section.path}}">
routerLinkActive="tb-active" [routerLinkActiveOptions]="{paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored'}" routerLink="{{section.path}}">
<mat-icon *ngIf="!section.isMdiIcon && section.icon != null" class="material-icons">{{section.icon}}</mat-icon>
<mat-icon *ngIf="section.isMdiIcon && section.icon != null" [svgIcon]="section.icon"></mat-icon>
<span>{{section.name | translate}}</span>

40
ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts

@ -31,6 +31,8 @@ import { ActivatedRoute } from '@angular/router';
import { EntityTabsComponent } from '../../components/entity/entity-tabs.component';
import { DAY, historyInterval } from '@shared/models/time/time.models';
import { IEntitiesTableComponent } from '@home/models/entity/entity-table-component.models';
import { IEntityDetailsPageComponent } from '@home/models/entity/entity-details-page-component.models';
import { templateJitUrl } from '@angular/compiler';
export type EntityBooleanFunction<T extends BaseData<HasId>> = (entity: T) => boolean;
export type EntityStringFunction<T extends BaseData<HasId>> = (entity: T) => string;
@ -136,11 +138,13 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
constructor() {}
private table: IEntitiesTableComponent = null;
private entityDetailsPage: IEntityDetailsPageComponent = null;
componentsData: any = null;
loadDataOnInit = true;
onLoadAction: (route: ActivatedRoute) => void = null;
table: IEntitiesTableComponent = null;
useTimePageLink = false;
defaultTimewindowInterval = historyInterval(DAY);
entityType: EntityType = null;
@ -189,6 +193,40 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
entityAdded: EntityVoidFunction<T> = () => {};
entityUpdated: EntityVoidFunction<T> = () => {};
entitiesDeleted: EntityIdsVoidFunction<T> = () => {};
getTable(): IEntitiesTableComponent {
return this.table;
}
setTable(table: IEntitiesTableComponent) {
this.table = table;
this.entityDetailsPage = null;
}
getEntityDetailsPage(): IEntityDetailsPageComponent {
return this.entityDetailsPage;
}
setEntityDetailsPage(entityDetailsPage: IEntityDetailsPageComponent) {
this.entityDetailsPage = entityDetailsPage;
this.table = null;
}
updateData(closeDetails = false) {
if (this.table) {
this.table.updateData(closeDetails);
} else if (this.entityDetailsPage) {
this.entityDetailsPage.reload();
}
}
getActivatedRoute(): ActivatedRoute {
if (this.table) {
return this.table.route;
} else {
return null;
}
}
}
export function checkBoxCell(value: boolean): string {

19
ui-ngx/src/app/modules/home/models/entity/entity-details-page-component.models.ts

@ -0,0 +1,19 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
export interface IEntityDetailsPageComponent {
reload(): void;
}

2
ui-ngx/src/app/modules/home/pages/asset/asset-table-header.component.ts

@ -36,7 +36,7 @@ export class AssetTableHeaderComponent extends EntityTableHeaderComponent<AssetI
assetTypeChanged(assetType: string) {
this.entitiesTableConfig.componentsData.assetType = assetType;
this.entitiesTableConfig.table.resetSortAndFilter(true);
this.entitiesTableConfig.getTable().resetSortAndFilter(true);
}
}

25
ui-ngx/src/app/modules/home/pages/asset/assets-table-config.resolver.ts

@ -104,7 +104,8 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
));
};
this.config.onEntityAction = action => this.onAssetAction(action, this.config);
this.config.detailsReadonly = () => (this.config.componentsData.assetScope === 'customer_user' || this.config.componentsData.assetScope === 'edge_customer_user');
this.config.detailsReadonly = () => (this.config.componentsData.assetScope === 'customer_user' ||
this.config.componentsData.assetScope === 'edge_customer_user');
this.config.headerComponent = AssetTableHeaderComponent;
@ -295,7 +296,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
name: this.translate.instant('asset.add-asset-text'),
icon: 'insert_drive_file',
isEnabled: () => true,
onAction: ($event) => this.config.table.addEntity($event)
onAction: ($event) => this.config.getTable().addEntity($event)
},
{
name: this.translate.instant('asset.import'),
@ -332,7 +333,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
this.homeDialogs.importEntities(EntityType.ASSET).subscribe((res) => {
if (res) {
this.broadcast.broadcast('assetSaved');
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -341,7 +342,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
if ($event) {
$event.stopPropagation();
}
const url = this.router.createUrlTree([asset.id.id], {relativeTo: config.table.route});
const url = this.router.createUrlTree([asset.id.id], {relativeTo: config.getActivatedRoute()});
this.router.navigateByUrl(url);
}
@ -360,7 +361,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -379,7 +380,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
if (res) {
this.assetService.makeAssetPublic(asset.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -402,7 +403,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -431,7 +432,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
if (res) {
this.assetService.unassignAssetFromCustomer(asset.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.assetScope !== 'tenant');
}
);
}
@ -459,7 +460,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -503,7 +504,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -522,7 +523,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
if (res) {
this.assetService.unassignAssetFromEdge(this.config.componentsData.edgeId, asset.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.assetScope !== 'tenant');
}
);
}
@ -550,7 +551,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}

2
ui-ngx/src/app/modules/home/pages/customer/customers-table-config.resolver.ts

@ -141,7 +141,7 @@ export class CustomersTableConfigResolver implements Resolve<EntityTableConfig<C
if ($event) {
$event.stopPropagation();
}
const url = this.router.createUrlTree([customer.id.id], {relativeTo: config.table.route});
const url = this.router.createUrlTree([customer.id.id], {relativeTo: config.getActivatedRoute()});
this.router.navigateByUrl(url);
}

28
ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts

@ -103,7 +103,8 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
return this.dashboardService.saveDashboard(dashboard as Dashboard);
};
this.config.onEntityAction = action => this.onDashboardAction(action);
this.config.detailsReadonly = () => (this.config.componentsData.dashboardScope === 'customer_user' || this.config.componentsData.dashboardScope === 'edge_customer_user');
this.config.detailsReadonly = () => (this.config.componentsData.dashboardScope === 'customer_user' ||
this.config.componentsData.dashboardScope === 'edge_customer_user');
}
resolve(route: ActivatedRouteSnapshot): Observable<EntityTableConfig<DashboardInfo | Dashboard>> {
@ -147,7 +148,8 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
this.config.cellActionDescriptors = this.configureCellActions(this.config.componentsData.dashboardScope);
this.config.groupActionDescriptors = this.configureGroupActions(this.config.componentsData.dashboardScope);
this.config.addActionDescriptors = this.configureAddActions(this.config.componentsData.dashboardScope);
this.config.addEnabled = !(this.config.componentsData.dashboardScope === 'customer_user' || this.config.componentsData.dashboardScope === 'edge_customer_user');
this.config.addEnabled = !(this.config.componentsData.dashboardScope === 'customer_user' ||
this.config.componentsData.dashboardScope === 'edge_customer_user');
this.config.entitiesDeleteEnabled = this.config.componentsData.dashboardScope === 'tenant';
this.config.deleteEnabled = () => this.config.componentsData.dashboardScope === 'tenant';
return this.config;
@ -322,7 +324,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
name: this.translate.instant('dashboard.create-new-dashboard'),
icon: 'insert_drive_file',
isEnabled: () => true,
onAction: ($event) => this.config.table.addEntity($event)
onAction: ($event) => this.config.getTable().addEntity($event)
},
{
name: this.translate.instant('dashboard.import'),
@ -372,7 +374,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
this.importExport.importDashboard().subscribe(
(dashboard) => {
if (dashboard) {
this.config.table.updateData();
this.config.updateData();
}
}
);
@ -400,7 +402,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -420,7 +422,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
}
}).afterClosed()
.subscribe(() => {
this.config.table.updateData();
this.config.updateData();
});
}
);
@ -440,7 +442,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
if (res) {
this.dashboardService.makeDashboardPrivate(dashboard.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -480,7 +482,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -499,7 +501,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
if (res) {
this.dashboardService.unassignDashboardFromCustomer(customerId, dashboard.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.dashboardScope !== 'tenant');
}
);
}
@ -527,7 +529,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -577,7 +579,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -596,7 +598,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
if (res) {
this.dashboardService.unassignDashboardFromEdge(this.config.componentsData.edgeId, dashboard.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.dashboardScope !== 'tenant');
}
);
}
@ -624,7 +626,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}

6
ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts

@ -151,7 +151,7 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon
}).afterClosed().subscribe(
(res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
}
);
@ -171,7 +171,7 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon
if (res) {
this.deviceProfileService.setDefaultDeviceProfile(deviceProfile.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -191,7 +191,7 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon
this.importExport.importDeviceProfile().subscribe(
(deviceProfile) => {
if (deviceProfile) {
this.config.table.updateData();
this.config.updateData();
}
}
);

2
ui-ngx/src/app/modules/home/pages/device/device-table-header.component.ts

@ -37,7 +37,7 @@ export class DeviceTableHeaderComponent extends EntityTableHeaderComponent<Devic
deviceProfileChanged(deviceProfileId: DeviceProfileId) {
this.entitiesTableConfig.componentsData.deviceProfileId = deviceProfileId;
this.entitiesTableConfig.table.resetSortAndFilter(true);
this.entitiesTableConfig.getTable().resetSortAndFilter(true);
}
}

2
ui-ngx/src/app/modules/home/pages/device/device.component.html

@ -116,12 +116,14 @@
</mat-form-field>
<tb-ota-package-autocomplete
[useFullEntityId]="true"
[showDetailsPageLink]="true"
[type]="otaUpdateType.FIRMWARE"
[deviceProfileId]="entityForm.get('deviceProfileId').value?.id"
formControlName="firmwareId">
</tb-ota-package-autocomplete>
<tb-ota-package-autocomplete
[useFullEntityId]="true"
[showDetailsPageLink]="true"
[type]="otaUpdateType.SOFTWARE"
[deviceProfileId]="entityForm.get('deviceProfileId').value?.id"
formControlName="softwareId">

2
ui-ngx/src/app/modules/home/pages/device/device.component.ts

@ -140,7 +140,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
}
onDeviceProfileUpdated() {
this.entitiesTableConfig.table.updateData(false);
this.entitiesTableConfig.updateData(false);
}
onDeviceProfileChanged(deviceProfile: DeviceProfileInfo) {

24
ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts

@ -375,7 +375,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
if ($event) {
$event.stopPropagation();
}
const url = this.router.createUrlTree([device.id.id], {relativeTo: config.table.route});
const url = this.router.createUrlTree([device.id.id], {relativeTo: config.getActivatedRoute()});
this.router.navigateByUrl(url);
}
@ -383,7 +383,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
this.homeDialogs.importEntities(EntityType.DEVICE).subscribe((res) => {
if (res) {
this.broadcast.broadcast('deviceSaved');
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -394,12 +394,12 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
entitiesTableConfig: this.config.table.entitiesTableConfig
entitiesTableConfig: this.config
}
}).afterClosed().subscribe(
(res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
}
);
@ -420,7 +420,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -439,7 +439,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
if (res) {
this.deviceService.makeDevicePublic(device.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -462,7 +462,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -491,7 +491,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
if (res) {
this.deviceService.unassignDeviceFromCustomer(device.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.deviceScope !== 'tenant');
}
);
}
@ -519,7 +519,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -586,7 +586,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -605,7 +605,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
if (res) {
this.deviceService.unassignDeviceFromEdge(this.config.componentsData.edgeId, device.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.deviceScope !== 'tenant');
}
);
}
@ -633,7 +633,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}

2
ui-ngx/src/app/modules/home/pages/edge/edge-table-header.component.ts

@ -36,7 +36,7 @@ export class EdgeTableHeaderComponent extends EntityTableHeaderComponent<EdgeInf
edgeTypeChanged(edgeType: string) {
this.entitiesTableConfig.componentsData.edgeType = edgeType;
this.entitiesTableConfig.table.resetSortAndFilter(true);
this.entitiesTableConfig.getTable().resetSortAndFilter(true);
}
}

16
ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts

@ -318,7 +318,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
name: this.translate.instant('edge.add-edge-text'),
icon: 'insert_drive_file',
isEnabled: () => true,
onAction: ($event) => this.config.table.addEntity($event)
onAction: ($event) => this.config.getTable().addEntity($event)
},
{
name: this.translate.instant('edge.import'),
@ -345,7 +345,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
this.homeDialogs.importEntities(EntityType.EDGE).subscribe((res) => {
if (res) {
this.broadcast.broadcast('edgeSaved');
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -365,7 +365,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -374,7 +374,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
if ($event) {
$event.stopPropagation();
}
const url = this.router.createUrlTree([edge.id.id], {relativeTo: config.table.route});
const url = this.router.createUrlTree([edge.id.id], {relativeTo: config.getActivatedRoute()});
this.router.navigateByUrl(url);
}
@ -392,7 +392,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
if (res) {
this.edgeService.makeEdgePublic(edge.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -443,7 +443,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -472,7 +472,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
if (res) {
this.edgeService.unassignEdgeFromCustomer(edge.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.edgeScope !== 'tenant');
}
);
}
@ -500,7 +500,7 @@ export class EdgesTableConfigResolver implements Resolve<EntityTableConfig<EdgeI
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}

2
ui-ngx/src/app/modules/home/pages/entity-view/entity-view-table-header.component.ts

@ -36,7 +36,7 @@ export class EntityViewTableHeaderComponent extends EntityTableHeaderComponent<E
entityViewTypeChanged(entityViewType: string) {
this.entitiesTableConfig.componentsData.entityViewType = entityViewType;
this.entitiesTableConfig.table.resetSortAndFilter(true);
this.entitiesTableConfig.getTable().resetSortAndFilter(true);
}
}

18
ui-ngx/src/app/modules/home/pages/entity-view/entity-views-table-config.resolver.ts

@ -328,7 +328,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -337,7 +337,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
if ($event) {
$event.stopPropagation();
}
const url = this.router.createUrlTree([entityView.id.id], {relativeTo: config.table.route});
const url = this.router.createUrlTree([entityView.id.id], {relativeTo: config.getActivatedRoute()});
this.router.navigateByUrl(url);
}
@ -355,7 +355,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
if (res) {
this.entityViewService.makeEntityViewPublic(entityView.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -378,7 +378,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -407,7 +407,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
if (res) {
this.entityViewService.unassignEntityViewFromCustomer(entityView.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.entityViewScope !== 'tenant');
}
);
}
@ -435,7 +435,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -479,7 +479,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
}).afterClosed()
.subscribe((res) => {
if (res) {
this.config.table.updateData();
this.config.updateData();
}
});
}
@ -498,7 +498,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
if (res) {
this.entityViewService.unassignEntityViewFromEdge(this.config.componentsData.edgeId, entityView.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.entityViewScope !== 'tenant');
}
);
}
@ -526,7 +526,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}

20
ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts

@ -151,7 +151,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
name: this.translate.instant('rulechain.create-new-rulechain'),
icon: 'insert_drive_file',
isEnabled: () => true,
onAction: ($event) => this.config.table.addEntity($event)
onAction: ($event) => this.config.getTable().addEntity($event)
},
{
name: this.translate.instant('rulechain.import'),
@ -343,13 +343,13 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
this.ruleChainService.setEdgeRootRuleChain(this.config.componentsData.edgeId, ruleChain.id.id).subscribe(
(edge) => {
this.config.componentsData.edge = edge;
this.config.table.updateData();
this.config.updateData();
}
);
} else {
this.ruleChainService.setRootRuleChain(ruleChain.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -399,7 +399,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
if (res) {
this.ruleChainService.setEdgeTemplateRootRuleChain(ruleChain.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -436,11 +436,11 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
this.dialogService.alert(this.translate.instant('edge.missing-related-rule-chains-title'),
message, this.translate.instant('action.close'), true).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
} else {
this.config.table.updateData();
this.config.updateData();
}
}
);
@ -463,7 +463,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
if (res) {
this.ruleChainService.unassignRuleChainFromEdge(this.config.componentsData.edgeId, ruleChain.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData(this.config.componentsData.ruleChainScope !== 'tenant');
}
);
}
@ -491,7 +491,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
);
forkJoin(tasks).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -513,7 +513,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
if (res) {
this.ruleChainService.setAutoAssignToEdgeRuleChain(ruleChain.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -535,7 +535,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
if (res) {
this.ruleChainService.unsetAutoAssignToEdgeRuleChain(ruleChain.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}

6
ui-ngx/src/app/modules/home/pages/tenant-profile/tenant-profiles-table-config.resolver.ts

@ -106,7 +106,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
name: this.translate.instant('tenant-profile.create-tenant-profile'),
icon: 'insert_drive_file',
isEnabled: () => true,
onAction: ($event) => this.config.table.addEntity($event)
onAction: ($event) => this.config.getTable().addEntity($event)
},
{
name: this.translate.instant('tenant-profile.import'),
@ -140,7 +140,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
if (res) {
this.tenantProfileService.setDefaultTenantProfile(tenantProfile.id.id).subscribe(
() => {
this.config.table.updateData();
this.config.updateData();
}
);
}
@ -152,7 +152,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
this.importExport.importTenantProfile().subscribe(
(deviceProfile) => {
if (deviceProfile) {
this.config.table.updateData();
this.config.updateData();
}
}
);

2
ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts

@ -99,6 +99,6 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> {
}
onTenantProfileUpdated() {
this.entitiesTableConfig.table.updateData(false);
this.entitiesTableConfig.updateData(false);
}
}

2
ui-ngx/src/app/modules/home/pages/tenant/tenants-table-config.resolver.ts

@ -91,7 +91,7 @@ export class TenantsTableConfigResolver implements Resolve<EntityTableConfig<Ten
if ($event) {
$event.stopPropagation();
}
const url = this.router.createUrlTree([tenant.id.id], {relativeTo: config.table.route});
const url = this.router.createUrlTree([tenant.id.id], {relativeTo: config.getActivatedRoute()});
this.router.navigateByUrl(url);
}

2
ui-ngx/src/app/modules/home/pages/user/users-table-config.resolver.ts

@ -178,7 +178,7 @@ export class UsersTableConfigResolver implements Resolve<EntityTableConfig<User>
if ($event) {
$event.stopPropagation();
}
const url = this.router.createUrlTree([user.id.id], {relativeTo: config.table.route});
const url = this.router.createUrlTree([user.id.id], {relativeTo: config.getActivatedRoute()});
this.router.navigateByUrl(url);
}

4
ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts

@ -76,7 +76,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
name: this.translate.instant('widgets-bundle.create-new-widgets-bundle'),
icon: 'insert_drive_file',
isEnabled: () => true,
onAction: ($event) => this.config.table.addEntity($event)
onAction: ($event) => this.config.getTable().addEntity($event)
},
{
name: this.translate.instant('widgets-bundle.import'),
@ -137,7 +137,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve<EntityTableCon
this.importExport.importWidgetsBundle().subscribe(
(widgetsBundle) => {
if (widgetsBundle) {
this.config.table.updateData();
this.config.updateData();
}
}
);

13
ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html

@ -22,20 +22,17 @@
(focusin)="onFocus()"
[required]="required"
[matAutocomplete]="packageAutocomplete"
[matAutocompleteDisabled]="disabled">
[matAutocompleteDisabled]="disabled"
[fxShow]="!showDetailsPageLink || !disabled || !otaPackageFormGroup.get('packageId').value">
<a *ngIf="showDetailsPageLink && otaPackageFormGroup.get('packageId').value && disabled" aria-label="Open ota package" [routerLink]=packageURL>
{{ displayPackageFn(otaPackageFormGroup.get('packageId').value) }}
</a>
<button *ngIf="otaPackageFormGroup.get('packageId').value && !disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Clear"
(click)="clear()">
<mat-icon class="material-icons">close</mat-icon>
</button>
<button *ngIf="showDetailsPageLink && otaPackageFormGroup.get('packageId').value && disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Open ota package">
<a [routerLink]=packageURL>
<mat-icon class="material-icons">open_in_new</mat-icon>
</a>
</button>
<mat-autocomplete class="tb-autocomplete"
#packageAutocomplete="matAutocomplete"
[displayWith]="displayPackageFn">

9
ui-ngx/src/app/shared/models/base-data.ts

@ -16,6 +16,7 @@
import { EntityId } from '@shared/models/id/entity-id';
import { HasUUID } from '@shared/models/id/has-uuid';
import { isDefinedAndNotNull } from '@core/utils';
export declare type HasId = EntityId | HasUUID;
@ -25,3 +26,11 @@ export interface BaseData<T extends HasId> {
name?: string;
label?: string;
}
export function hasIdEquals(id1: HasId, id2: HasId): boolean {
if (isDefinedAndNotNull(id1) && isDefinedAndNotNull(id2)) {
return id1.id === id2.id;
} else {
return id1 === id2;
}
}

5
ui-ngx/yarn.lock

@ -9280,11 +9280,6 @@ symbol-observable@4.0.0:
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==
systemjs-babel@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/systemjs-babel/-/systemjs-babel-0.3.1.tgz#4b864be909b542fb7a84303f24d0dbdbc35da2e1"
integrity sha512-ggZ+v7RSKmN1OlAd46REpfZJmBTnrBUH1zgHnSalIB0KPepfPXEm1p2auE6v7wCmJl231EyApRxnw6X3SgpQtA==
systemjs@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-6.11.0.tgz#8df8e74fc05822e6c40170aa409b9ca64833315f"

Loading…
Cancel
Save