added restriction for number of cfs/arguments/ts rolling values
This commit is contained in:
parent
19f6f32326
commit
481753b8f0
@ -52,4 +52,7 @@ public interface ArgumentEntry {
|
|||||||
collect(Collectors.toMap(TsKvEntry::getTs, TsKvEntry::getValue, (oldValue, newValue) -> newValue, TreeMap::new)));
|
collect(Collectors.toMap(TsKvEntry::getTs, TsKvEntry::getValue, (oldValue, newValue) -> newValue, TreeMap::new)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
ArgumentEntry copy();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,25 +42,24 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState {
|
|||||||
if (existingArgumentEntry != null) {
|
if (existingArgumentEntry != null) {
|
||||||
if (existingArgumentEntry instanceof SingleValueArgumentEntry) {
|
if (existingArgumentEntry instanceof SingleValueArgumentEntry) {
|
||||||
if (existingArgumentEntry.hasUpdatedValue(argumentEntry)) {
|
if (existingArgumentEntry.hasUpdatedValue(argumentEntry)) {
|
||||||
arguments.put(key, argumentEntry);
|
arguments.put(key, argumentEntry.copy());
|
||||||
stateUpdated.set(true);
|
stateUpdated.set(true);
|
||||||
}
|
}
|
||||||
} else if (existingArgumentEntry instanceof TsRollingArgumentEntry existingTsRollingArgumentEntry) {
|
} else if (existingArgumentEntry instanceof TsRollingArgumentEntry existingTsRollingArgumentEntry) {
|
||||||
if (argumentEntry instanceof TsRollingArgumentEntry tsRollingArgumentEntry) {
|
if (argumentEntry instanceof TsRollingArgumentEntry tsRollingArgumentEntry) {
|
||||||
if (existingArgumentEntry.hasUpdatedValue(argumentEntry)) {
|
if (existingArgumentEntry.hasUpdatedValue(argumentEntry)) {
|
||||||
existingTsRollingArgumentEntry.getTsRecords().putAll(tsRollingArgumentEntry.getTsRecords());
|
existingTsRollingArgumentEntry.addAllTsRecords(tsRollingArgumentEntry.getTsRecords());
|
||||||
stateUpdated.set(true);
|
stateUpdated.set(true);
|
||||||
}
|
}
|
||||||
} else if (argumentEntry instanceof SingleValueArgumentEntry singleValueArgumentEntry) {
|
} else if (argumentEntry instanceof SingleValueArgumentEntry singleValueArgumentEntry) {
|
||||||
if (existingArgumentEntry.hasUpdatedValue(argumentEntry)) {
|
if (existingArgumentEntry.hasUpdatedValue(argumentEntry)) {
|
||||||
existingTsRollingArgumentEntry.getTsRecords().put(singleValueArgumentEntry.getTs(), singleValueArgumentEntry.getValue());
|
existingTsRollingArgumentEntry.addTsRecord(singleValueArgumentEntry.getTs(), singleValueArgumentEntry.getValue());
|
||||||
stateUpdated.set(true);
|
stateUpdated.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arguments.put(key, argumentEntry);
|
arguments.put(key, argumentEntry.copy());
|
||||||
stateUpdated.set(true);
|
stateUpdated.set(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.cf.ctx.state;
|
package org.thingsboard.server.service.cf.ctx.state;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
||||||
@ -23,6 +24,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class SingleValueArgumentEntry implements ArgumentEntry {
|
public class SingleValueArgumentEntry implements ArgumentEntry {
|
||||||
|
|
||||||
private long ts;
|
private long ts;
|
||||||
@ -51,4 +53,10 @@ public class SingleValueArgumentEntry implements ArgumentEntry {
|
|||||||
public boolean hasUpdatedValue(ArgumentEntry entry) {
|
public boolean hasUpdatedValue(ArgumentEntry entry) {
|
||||||
return this.ts != ((SingleValueArgumentEntry) entry).getTs();
|
return this.ts != ((SingleValueArgumentEntry) entry).getTs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentEntry copy() {
|
||||||
|
return new SingleValueArgumentEntry(this.ts, this.value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,18 +16,26 @@
|
|||||||
package org.thingsboard.server.service.cf.ctx.state;
|
package org.thingsboard.server.service.cf.ctx.state;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@Slf4j
|
||||||
public class TsRollingArgumentEntry implements ArgumentEntry {
|
public class TsRollingArgumentEntry implements ArgumentEntry {
|
||||||
|
|
||||||
private TreeMap<Long, Object> tsRecords;
|
private static final int MAX_ROLLING_ARGUMENT_ENTRY_SIZE = 1000;
|
||||||
|
|
||||||
|
private TreeMap<Long, Object> tsRecords = new TreeMap<>();
|
||||||
|
|
||||||
|
public TsRollingArgumentEntry(TreeMap<Long, Object> tsRecords) {
|
||||||
|
addAllTsRecords(tsRecords);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ArgumentType getType() {
|
public ArgumentType getType() {
|
||||||
@ -44,4 +52,27 @@ public class TsRollingArgumentEntry implements ArgumentEntry {
|
|||||||
public boolean hasUpdatedValue(ArgumentEntry entry) {
|
public boolean hasUpdatedValue(ArgumentEntry entry) {
|
||||||
return !tsRecords.containsKey(((SingleValueArgumentEntry) entry).getTs());
|
return !tsRecords.containsKey(((SingleValueArgumentEntry) entry).getTs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentEntry copy() {
|
||||||
|
return new TsRollingArgumentEntry(new TreeMap<>(tsRecords));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTsRecord(Long key, Object value) {
|
||||||
|
if (NumberUtils.isParsable(value.toString())) {
|
||||||
|
tsRecords.put(key, value);
|
||||||
|
if (tsRecords.size() > MAX_ROLLING_ARGUMENT_ENTRY_SIZE) {
|
||||||
|
tsRecords.pollFirstEntry();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("Argument type 'TS_ROLLING' only supports numeric values.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAllTsRecords(Map<Long, Object> newRecords) {
|
||||||
|
for (Map.Entry<Long, Object> entry : newRecords.entrySet()) {
|
||||||
|
addTsRecord(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,9 @@ import static org.thingsboard.server.dao.service.Validator.validateEntityId;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class DefaultTbCalculatedFieldService extends AbstractTbEntityService implements TbCalculatedFieldService {
|
public class DefaultTbCalculatedFieldService extends AbstractTbEntityService implements TbCalculatedFieldService {
|
||||||
|
|
||||||
|
private static final int MAX_ARGUMENT_SIZE = 10;
|
||||||
|
private static final int MAX_CALCULATED_FIELD_NUMBER = 10;
|
||||||
|
|
||||||
private final CalculatedFieldService calculatedFieldService;
|
private final CalculatedFieldService calculatedFieldService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -53,7 +56,9 @@ public class DefaultTbCalculatedFieldService extends AbstractTbEntityService imp
|
|||||||
ActionType actionType = calculatedField.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
|
ActionType actionType = calculatedField.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
|
||||||
TenantId tenantId = calculatedField.getTenantId();
|
TenantId tenantId = calculatedField.getTenantId();
|
||||||
try {
|
try {
|
||||||
|
checkCalculatedFieldNumber(tenantId, calculatedField.getEntityId());
|
||||||
checkEntityExistence(tenantId, calculatedField.getEntityId());
|
checkEntityExistence(tenantId, calculatedField.getEntityId());
|
||||||
|
checkArgumentSize(calculatedField.getConfiguration());
|
||||||
checkReferencedEntities(calculatedField.getConfiguration(), user);
|
checkReferencedEntities(calculatedField.getConfiguration(), user);
|
||||||
CalculatedField savedCalculatedField = checkNotNull(calculatedFieldService.save(calculatedField));
|
CalculatedField savedCalculatedField = checkNotNull(calculatedFieldService.save(calculatedField));
|
||||||
logEntityActionService.logEntityAction(tenantId, savedCalculatedField.getId(), savedCalculatedField, actionType, user);
|
logEntityActionService.logEntityAction(tenantId, savedCalculatedField.getId(), savedCalculatedField, actionType, user);
|
||||||
@ -105,6 +110,19 @@ public class DefaultTbCalculatedFieldService extends AbstractTbEntityService imp
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkArgumentSize(CalculatedFieldConfiguration calculatedFieldConfig) {
|
||||||
|
if (calculatedFieldConfig.getArguments().size() > MAX_ARGUMENT_SIZE) {
|
||||||
|
throw new IllegalArgumentException("Too many arguments: " + calculatedFieldConfig.getArguments().size() + ". Max number of argument is " + MAX_ARGUMENT_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCalculatedFieldNumber(TenantId tenantId, EntityId entityId) {
|
||||||
|
int numberOfCalculatedFieldsByEntityId = calculatedFieldService.findCalculatedFieldIdsByEntityId(tenantId, entityId).size();
|
||||||
|
if (numberOfCalculatedFieldsByEntityId >= MAX_CALCULATED_FIELD_NUMBER) {
|
||||||
|
throw new IllegalArgumentException("Max number of calculated fields for entity is " + MAX_CALCULATED_FIELD_NUMBER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private <E extends HasId<I> & HasTenantId, I extends EntityId> E findEntity(TenantId tenantId, EntityId entityId) {
|
private <E extends HasId<I> & HasTenantId, I extends EntityId> E findEntity(TenantId tenantId, EntityId entityId) {
|
||||||
return switch (entityId.getEntityType()) {
|
return switch (entityId.getEntityType()) {
|
||||||
case TENANT, CUSTOMER, ASSET, DEVICE -> (E) entityService.fetchEntity(tenantId, entityId).orElse(null);
|
case TENANT, CUSTOMER, ASSET, DEVICE -> (E) entityService.fetchEntity(tenantId, entityId).orElse(null);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user