Commit cf6824cf22b60a0e8d6464820a7dc71066414109

Authored by Igor Kulikov
1 parent faee8e6b

Filter predicate dynamic value

Showing 25 changed files with 395 additions and 69 deletions
@@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder; @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder;
44 import org.thingsboard.server.common.data.query.EntityKey; 44 import org.thingsboard.server.common.data.query.EntityKey;
45 import org.thingsboard.server.common.data.query.EntityKeyType; 45 import org.thingsboard.server.common.data.query.EntityKeyType;
46 import org.thingsboard.server.common.data.query.EntityListFilter; 46 import org.thingsboard.server.common.data.query.EntityListFilter;
  47 +import org.thingsboard.server.common.data.query.FilterPredicateValue;
47 import org.thingsboard.server.common.data.query.KeyFilter; 48 import org.thingsboard.server.common.data.query.KeyFilter;
48 import org.thingsboard.server.common.data.query.NumericFilterPredicate; 49 import org.thingsboard.server.common.data.query.NumericFilterPredicate;
49 import org.thingsboard.server.common.data.security.Authority; 50 import org.thingsboard.server.common.data.security.Authority;
@@ -259,7 +260,7 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe @@ -259,7 +260,7 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe
259 KeyFilter highTemperatureFilter = new KeyFilter(); 260 KeyFilter highTemperatureFilter = new KeyFilter();
260 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature")); 261 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
261 NumericFilterPredicate predicate = new NumericFilterPredicate(); 262 NumericFilterPredicate predicate = new NumericFilterPredicate();
262 - predicate.setValue(45); 263 + predicate.setValue(FilterPredicateValue.fromDouble(45));
263 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 264 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
264 highTemperatureFilter.setPredicate(predicate); 265 highTemperatureFilter.setPredicate(predicate);
265 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter); 266 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
@@ -26,9 +26,9 @@ import java.util.Arrays; @@ -26,9 +26,9 @@ import java.util.Arrays;
26 26
27 @RunWith(ClasspathSuite.class) 27 @RunWith(ClasspathSuite.class)
28 @ClasspathSuite.ClassnameFilters({ 28 @ClasspathSuite.ClassnameFilters({
29 - "org.thingsboard.server.controller.sql.WebsocketApiSqlTest", 29 +// "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
30 // "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest", 30 // "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest",
31 -// "org.thingsboard.server.controller.sql.*Test", 31 + "org.thingsboard.server.controller.sql.*Test",
32 }) 32 })
33 public class ControllerSqlTestSuite { 33 public class ControllerSqlTestSuite {
34 34
@@ -21,7 +21,7 @@ import lombok.Data; @@ -21,7 +21,7 @@ import lombok.Data;
21 public class BooleanFilterPredicate implements KeyFilterPredicate { 21 public class BooleanFilterPredicate implements KeyFilterPredicate {
22 22
23 private BooleanOperation operation; 23 private BooleanOperation operation;
24 - private boolean value; 24 + private FilterPredicateValue<Boolean> value;
25 25
26 @Override 26 @Override
27 public FilterPredicateType getType() { 27 public FilterPredicateType getType() {
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.query;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.Data;
  20 +import lombok.Getter;
  21 +
  22 +@Data
  23 +public class DynamicValue<T> {
  24 +
  25 + @JsonIgnore
  26 + private T resolvedValue;
  27 +
  28 + @Getter
  29 + private final DynamicValueSourceType sourceType;
  30 + @Getter
  31 + private final String sourceAttribute;
  32 +
  33 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.query;
  17 +
  18 +public enum DynamicValueSourceType {
  19 + CURRENT_TENANT,
  20 + CURRENT_CUSTOMER,
  21 + CURRENT_USER
  22 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.query;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonIgnore;
  20 +import com.fasterxml.jackson.annotation.JsonProperty;
  21 +import lombok.Data;
  22 +import lombok.Getter;
  23 +
  24 +@Data
  25 +public class FilterPredicateValue<T> {
  26 +
  27 + @Getter
  28 + private final T defaultValue;
  29 + @Getter
  30 + private final DynamicValue<T> dynamicValue;
  31 +
  32 + public FilterPredicateValue(T defaultValue) {
  33 + this(defaultValue, null);
  34 + }
  35 +
  36 + @JsonCreator
  37 + public FilterPredicateValue(@JsonProperty("defaultValue") T defaultValue,
  38 + @JsonProperty("dynamicValue") DynamicValue<T> dynamicValue) {
  39 + this.defaultValue = defaultValue;
  40 + this.dynamicValue = dynamicValue;
  41 + }
  42 +
  43 + @JsonIgnore
  44 + public T getValue() {
  45 + if (this.dynamicValue != null && this.dynamicValue.getResolvedValue() != null) {
  46 + return this.dynamicValue.getResolvedValue();
  47 + } else {
  48 + return defaultValue;
  49 + }
  50 + }
  51 +
  52 + public static FilterPredicateValue<Double> fromDouble(double value) {
  53 + return new FilterPredicateValue<>(value);
  54 + }
  55 +
  56 + public static FilterPredicateValue<String> fromString(String value) {
  57 + return new FilterPredicateValue<>(value);
  58 + }
  59 +
  60 + public static FilterPredicateValue<Boolean> fromBoolean(boolean value) {
  61 + return new FilterPredicateValue<>(value);
  62 + }
  63 +}
@@ -21,7 +21,7 @@ import lombok.Data; @@ -21,7 +21,7 @@ import lombok.Data;
21 public class NumericFilterPredicate implements KeyFilterPredicate { 21 public class NumericFilterPredicate implements KeyFilterPredicate {
22 22
23 private NumericOperation operation; 23 private NumericOperation operation;
24 - private double value; 24 + private FilterPredicateValue<Double> value;
25 25
26 @Override 26 @Override
27 public FilterPredicateType getType() { 27 public FilterPredicateType getType() {
@@ -21,7 +21,7 @@ import lombok.Data; @@ -21,7 +21,7 @@ import lombok.Data;
21 public class StringFilterPredicate implements KeyFilterPredicate { 21 public class StringFilterPredicate implements KeyFilterPredicate {
22 22
23 private StringOperation operation; 23 private StringOperation operation;
24 - private String value; 24 + private FilterPredicateValue<String> value;
25 private boolean ignoreCase; 25 private boolean ignoreCase;
26 26
27 @Override 27 @Override
@@ -440,7 +440,7 @@ public class EntityKeyMapping { @@ -440,7 +440,7 @@ public class EntityKeyMapping {
440 private String buildStringPredicateQuery(QueryContext ctx, String field, StringFilterPredicate stringFilterPredicate) { 440 private String buildStringPredicateQuery(QueryContext ctx, String field, StringFilterPredicate stringFilterPredicate) {
441 String operationField = field; 441 String operationField = field;
442 String paramName = getNextParameterName(field); 442 String paramName = getNextParameterName(field);
443 - String value = stringFilterPredicate.getValue(); 443 + String value = stringFilterPredicate.getValue().getValue();
444 String stringOperationQuery = ""; 444 String stringOperationQuery = "";
445 if (stringFilterPredicate.isIgnoreCase()) { 445 if (stringFilterPredicate.isIgnoreCase()) {
446 value = value.toLowerCase(); 446 value = value.toLowerCase();
@@ -476,7 +476,7 @@ public class EntityKeyMapping { @@ -476,7 +476,7 @@ public class EntityKeyMapping {
476 476
477 private String buildNumericPredicateQuery(QueryContext ctx, String field, NumericFilterPredicate numericFilterPredicate) { 477 private String buildNumericPredicateQuery(QueryContext ctx, String field, NumericFilterPredicate numericFilterPredicate) {
478 String paramName = getNextParameterName(field); 478 String paramName = getNextParameterName(field);
479 - ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue()); 479 + ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue().getValue());
480 String numericOperationQuery = ""; 480 String numericOperationQuery = "";
481 switch (numericFilterPredicate.getOperation()) { 481 switch (numericFilterPredicate.getOperation()) {
482 case EQUAL: 482 case EQUAL:
@@ -504,7 +504,7 @@ public class EntityKeyMapping { @@ -504,7 +504,7 @@ public class EntityKeyMapping {
504 private String buildBooleanPredicateQuery(QueryContext ctx, String field, 504 private String buildBooleanPredicateQuery(QueryContext ctx, String field,
505 BooleanFilterPredicate booleanFilterPredicate) { 505 BooleanFilterPredicate booleanFilterPredicate) {
506 String paramName = getNextParameterName(field); 506 String paramName = getNextParameterName(field);
507 - ctx.addBooleanParameter(paramName, booleanFilterPredicate.isValue()); 507 + ctx.addBooleanParameter(paramName, booleanFilterPredicate.getValue().getValue());
508 String booleanOperationQuery = ""; 508 String booleanOperationQuery = "";
509 switch (booleanFilterPredicate.getOperation()) { 509 switch (booleanFilterPredicate.getOperation()) {
510 case EQUAL: 510 case EQUAL:
@@ -49,6 +49,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder; @@ -49,6 +49,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder;
49 import org.thingsboard.server.common.data.query.EntityKey; 49 import org.thingsboard.server.common.data.query.EntityKey;
50 import org.thingsboard.server.common.data.query.EntityKeyType; 50 import org.thingsboard.server.common.data.query.EntityKeyType;
51 import org.thingsboard.server.common.data.query.EntityListFilter; 51 import org.thingsboard.server.common.data.query.EntityListFilter;
  52 +import org.thingsboard.server.common.data.query.FilterPredicateValue;
52 import org.thingsboard.server.common.data.query.KeyFilter; 53 import org.thingsboard.server.common.data.query.KeyFilter;
53 import org.thingsboard.server.common.data.query.NumericFilterPredicate; 54 import org.thingsboard.server.common.data.query.NumericFilterPredicate;
54 import org.thingsboard.server.common.data.query.RelationsQueryFilter; 55 import org.thingsboard.server.common.data.query.RelationsQueryFilter;
@@ -239,7 +240,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { @@ -239,7 +240,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
239 KeyFilter highTemperatureFilter = new KeyFilter(); 240 KeyFilter highTemperatureFilter = new KeyFilter();
240 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature")); 241 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
241 NumericFilterPredicate predicate = new NumericFilterPredicate(); 242 NumericFilterPredicate predicate = new NumericFilterPredicate();
242 - predicate.setValue(45); 243 + predicate.setValue(FilterPredicateValue.fromDouble(45));
243 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 244 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
244 highTemperatureFilter.setPredicate(predicate); 245 highTemperatureFilter.setPredicate(predicate);
245 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter); 246 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
@@ -311,7 +312,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { @@ -311,7 +312,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
311 KeyFilter highTemperatureFilter = new KeyFilter(); 312 KeyFilter highTemperatureFilter = new KeyFilter();
312 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature")); 313 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
313 NumericFilterPredicate predicate = new NumericFilterPredicate(); 314 NumericFilterPredicate predicate = new NumericFilterPredicate();
314 - predicate.setValue(45); 315 + predicate.setValue(FilterPredicateValue.fromDouble(45));
315 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 316 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
316 highTemperatureFilter.setPredicate(predicate); 317 highTemperatureFilter.setPredicate(predicate);
317 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter); 318 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
@@ -382,7 +383,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { @@ -382,7 +383,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
382 KeyFilter highTemperatureFilter = new KeyFilter(); 383 KeyFilter highTemperatureFilter = new KeyFilter();
383 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "consumption")); 384 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "consumption"));
384 NumericFilterPredicate predicate = new NumericFilterPredicate(); 385 NumericFilterPredicate predicate = new NumericFilterPredicate();
385 - predicate.setValue(50); 386 + predicate.setValue(FilterPredicateValue.fromDouble(50));
386 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 387 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
387 highTemperatureFilter.setPredicate(predicate); 388 highTemperatureFilter.setPredicate(predicate);
388 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter); 389 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
@@ -584,7 +585,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { @@ -584,7 +585,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
584 KeyFilter highTemperatureFilter = new KeyFilter(); 585 KeyFilter highTemperatureFilter = new KeyFilter();
585 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature")); 586 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
586 NumericFilterPredicate predicate = new NumericFilterPredicate(); 587 NumericFilterPredicate predicate = new NumericFilterPredicate();
587 - predicate.setValue(45); 588 + predicate.setValue(FilterPredicateValue.fromDouble(45));
588 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 589 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
589 highTemperatureFilter.setPredicate(predicate); 590 highTemperatureFilter.setPredicate(predicate);
590 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter); 591 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
@@ -396,7 +396,9 @@ export class EntityService { @@ -396,7 +396,9 @@ export class EntityService {
396 type: FilterPredicateType.STRING, 396 type: FilterPredicateType.STRING,
397 operation: StringOperation.STARTS_WITH, 397 operation: StringOperation.STARTS_WITH,
398 ignoreCase: true, 398 ignoreCase: true,
399 - value: searchText 399 + value: {
  400 + defaultValue: searchText
  401 + }
400 } 402 }
401 } 403 }
402 ] : null 404 ] : null
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="booleanFilterPredicateFormGroup"> 18 +<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="booleanFilterPredicateFormGroup">
19 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block"> 19 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block">
20 <mat-label></mat-label> 20 <mat-label></mat-label>
21 <mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}"> 21 <mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
@@ -24,7 +24,8 @@ @@ -24,7 +24,8 @@
24 </mat-option> 24 </mat-option>
25 </mat-select> 25 </mat-select>
26 </mat-form-field> 26 </mat-form-field>
27 - <mat-checkbox fxFlex="60" formControlName="value">  
28 - {{ (booleanFilterPredicateFormGroup.get('value').value ? 'value.true' : 'value.false') | translate }}  
29 - </mat-checkbox> 27 + <tb-filter-predicate-value fxFlex="60"
  28 + [valueType]="valueTypeEnum.BOOLEAN"
  29 + formControlName="value">
  30 + </tb-filter-predicate-value>
30 </div> 31 </div>
@@ -18,10 +18,10 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'; @@ -18,10 +18,10 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core';
18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19 import { 19 import {
20 BooleanFilterPredicate, 20 BooleanFilterPredicate,
21 - BooleanOperation, booleanOperationTranslationMap, 21 + BooleanOperation,
  22 + booleanOperationTranslationMap, EntityKeyValueType,
22 FilterPredicateType 23 FilterPredicateType
23 } from '@shared/models/query/query.models'; 24 } from '@shared/models/query/query.models';
24 -import { isDefined } from '@core/utils';  
25 25
26 @Component({ 26 @Component({
27 selector: 'tb-boolean-filter-predicate', 27 selector: 'tb-boolean-filter-predicate',
@@ -39,6 +39,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On @@ -39,6 +39,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
39 39
40 @Input() disabled: boolean; 40 @Input() disabled: boolean;
41 41
  42 + valueTypeEnum = EntityKeyValueType;
  43 +
42 booleanFilterPredicateFormGroup: FormGroup; 44 booleanFilterPredicateFormGroup: FormGroup;
43 45
44 booleanOperations = Object.keys(BooleanOperation); 46 booleanOperations = Object.keys(BooleanOperation);
@@ -53,7 +55,7 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On @@ -53,7 +55,7 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
53 ngOnInit(): void { 55 ngOnInit(): void {
54 this.booleanFilterPredicateFormGroup = this.fb.group({ 56 this.booleanFilterPredicateFormGroup = this.fb.group({
55 operation: [BooleanOperation.EQUAL, [Validators.required]], 57 operation: [BooleanOperation.EQUAL, [Validators.required]],
56 - value: [false] 58 + value: [null, [Validators.required]]
57 }); 59 });
58 this.booleanFilterPredicateFormGroup.valueChanges.subscribe(() => { 60 this.booleanFilterPredicateFormGroup.valueChanges.subscribe(() => {
59 this.updateModel(); 61 this.updateModel();
@@ -78,16 +80,13 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On @@ -78,16 +80,13 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
78 80
79 writeValue(predicate: BooleanFilterPredicate): void { 81 writeValue(predicate: BooleanFilterPredicate): void {
80 this.booleanFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false}); 82 this.booleanFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false});
81 - this.booleanFilterPredicateFormGroup.get('value').patchValue(isDefined(predicate.value) ? predicate.value : false, {emitEvent: false}); 83 + this.booleanFilterPredicateFormGroup.get('value').patchValue(predicate.value, {emitEvent: false});
82 } 84 }
83 85
84 private updateModel() { 86 private updateModel() {
85 let predicate: BooleanFilterPredicate = null; 87 let predicate: BooleanFilterPredicate = null;
86 if (this.booleanFilterPredicateFormGroup.valid) { 88 if (this.booleanFilterPredicateFormGroup.valid) {
87 predicate = this.booleanFilterPredicateFormGroup.getRawValue(); 89 predicate = this.booleanFilterPredicateFormGroup.getRawValue();
88 - if (!isDefined(predicate.value)) {  
89 - predicate.value = false;  
90 - }  
91 predicate.type = FilterPredicateType.BOOLEAN; 90 predicate.type = FilterPredicateType.BOOLEAN;
92 } 91 }
93 this.propagateChange(predicate); 92 this.propagateChange(predicate);
@@ -39,7 +39,7 @@ @@ -39,7 +39,7 @@
39 </div> 39 </div>
40 <mat-divider></mat-divider> 40 <mat-divider></mat-divider>
41 <div class="predicate-list"> 41 <div class="predicate-list">
42 - <div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;" 42 + <div fxLayout="row" fxLayoutAlign="start center" style="height: 45px;"
43 formArrayName="predicates" 43 formArrayName="predicates"
44 *ngFor="let predicateControl of predicatesFormArray().controls; let $index = index"> 44 *ngFor="let predicateControl of predicatesFormArray().controls; let $index = index">
45 <div fxFlex="8" fxLayout="row" fxLayoutAlign="center" class="filters-operation"> 45 <div fxFlex="8" fxLayout="row" fxLayoutAlign="center" class="filters-operation">
  1 +<!--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateValueFormGroup">
  19 + <div fxFlex fxLayout="column">
  20 + <div fxFlex fxLayout="column" [ngSwitch]="valueType">
  21 + <ng-template [ngSwitchCase]="valueTypeEnum.STRING">
  22 + <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
  23 + <mat-label></mat-label>
  24 + <input matInput formControlName="defaultValue" placeholder="{{'filter.value' | translate}}">
  25 + </mat-form-field>
  26 + </ng-template>
  27 + <ng-template [ngSwitchCase]="valueTypeEnum.NUMERIC">
  28 + <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
  29 + <mat-label></mat-label>
  30 + <input required type="number" matInput formControlName="defaultValue"
  31 + placeholder="{{'filter.value' | translate}}">
  32 + </mat-form-field>
  33 + </ng-template>
  34 + <ng-template [ngSwitchCase]="valueTypeEnum.DATE_TIME">
  35 + <tb-datetime fxFlex formControlName="defaultValue"
  36 + dateText="filter.date"
  37 + timeText="filter.time"
  38 + required [showLabel]="false"></tb-datetime>
  39 + </ng-template>
  40 + <ng-template [ngSwitchCase]="valueTypeEnum.BOOLEAN">
  41 + <mat-checkbox fxFlex formControlName="defaultValue">
  42 + {{ (filterPredicateValueFormGroup.get('defaultValue').value ? 'value.true' : 'value.false') | translate }}
  43 + </mat-checkbox>
  44 + </ng-template>
  45 + </div>
  46 + <div class="tb-hint">Default value</div>
  47 + </div>
  48 +</div>
  1 +///
  2 +/// Copyright © 2016-2020 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Component, forwardRef, Input, OnInit } from '@angular/core';
  18 +import {
  19 + ControlValueAccessor,
  20 + FormBuilder,
  21 + FormGroup,
  22 + NG_VALUE_ACCESSOR,
  23 + ValidatorFn,
  24 + Validators
  25 +} from '@angular/forms';
  26 +import { EntityKeyValueType, FilterPredicateValue } from '@shared/models/query/query.models';
  27 +
  28 +@Component({
  29 + selector: 'tb-filter-predicate-value',
  30 + templateUrl: './filter-predicate-value.component.html',
  31 + styleUrls: ['./filter-predicate.scss'],
  32 + providers: [
  33 + {
  34 + provide: NG_VALUE_ACCESSOR,
  35 + useExisting: forwardRef(() => FilterPredicateValueComponent),
  36 + multi: true
  37 + }
  38 + ]
  39 +})
  40 +export class FilterPredicateValueComponent implements ControlValueAccessor, OnInit {
  41 +
  42 + @Input() disabled: boolean;
  43 +
  44 + @Input()
  45 + valueType: EntityKeyValueType;
  46 +
  47 + valueTypeEnum = EntityKeyValueType;
  48 +
  49 + filterPredicateValueFormGroup: FormGroup;
  50 +
  51 + private propagateChange = null;
  52 +
  53 + constructor(private fb: FormBuilder) {
  54 + }
  55 +
  56 + ngOnInit(): void {
  57 + let defaultValue: string | number | boolean;
  58 + let defaultValueValidators: ValidatorFn[];
  59 + switch (this.valueType) {
  60 + case EntityKeyValueType.STRING:
  61 + defaultValue = '';
  62 + defaultValueValidators = [];
  63 + break;
  64 + case EntityKeyValueType.NUMERIC:
  65 + defaultValue = 0;
  66 + defaultValueValidators = [Validators.required];
  67 + break;
  68 + case EntityKeyValueType.BOOLEAN:
  69 + defaultValue = false;
  70 + defaultValueValidators = [];
  71 + break;
  72 + case EntityKeyValueType.DATE_TIME:
  73 + defaultValue = Date.now();
  74 + defaultValueValidators = [Validators.required];
  75 + break;
  76 + }
  77 + this.filterPredicateValueFormGroup = this.fb.group({
  78 + defaultValue: [defaultValue, defaultValueValidators],
  79 + dynamicValue: this.fb.group(
  80 + {
  81 + sourceType: [null],
  82 + sourceAttribute: [null]
  83 + }
  84 + )
  85 + });
  86 + this.filterPredicateValueFormGroup.valueChanges.subscribe(() => {
  87 + this.updateModel();
  88 + });
  89 + }
  90 +
  91 + registerOnChange(fn: any): void {
  92 + this.propagateChange = fn;
  93 + }
  94 +
  95 + registerOnTouched(fn: any): void {
  96 + }
  97 +
  98 + setDisabledState?(isDisabled: boolean): void {
  99 + this.disabled = isDisabled;
  100 + if (this.disabled) {
  101 + this.filterPredicateValueFormGroup.disable({emitEvent: false});
  102 + } else {
  103 + this.filterPredicateValueFormGroup.enable({emitEvent: false});
  104 + }
  105 + }
  106 +
  107 + writeValue(predicateValue: FilterPredicateValue<string | number | boolean>): void {
  108 + this.filterPredicateValueFormGroup.get('defaultValue').patchValue(predicateValue.defaultValue, {emitEvent: false});
  109 + this.filterPredicateValueFormGroup.get('dynamicValue').patchValue(predicateValue.dynamicValue ?
  110 + predicateValue.dynamicValue : { sourceType: null, sourceAttribute: null }, {emitEvent: false});
  111 + }
  112 +
  113 + private updateModel() {
  114 + let predicateValue: FilterPredicateValue<string | number | boolean> = null;
  115 + if (this.filterPredicateValueFormGroup.valid) {
  116 + predicateValue = this.filterPredicateValueFormGroup.getRawValue();
  117 + if (predicateValue.dynamicValue) {
  118 + if (!predicateValue.dynamicValue.sourceType || !predicateValue.dynamicValue.sourceAttribute) {
  119 + predicateValue.dynamicValue = null;
  120 + }
  121 + }
  122 + }
  123 + this.propagateChange(predicateValue);
  124 + }
  125 +
  126 +}
@@ -13,6 +13,13 @@ @@ -13,6 +13,13 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
  16 +
  17 +:host {
  18 + .tb-hint {
  19 + padding-bottom: 0;
  20 + }
  21 +}
  22 +
16 :host ::ng-deep { 23 :host ::ng-deep {
17 mat-form-field { 24 mat-form-field {
18 .mat-form-field-wrapper { 25 .mat-form-field-wrapper {
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="numericFilterPredicateFormGroup"> 18 +<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="numericFilterPredicateFormGroup">
19 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block"> 19 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block">
20 <mat-label></mat-label> 20 <mat-label></mat-label>
21 <mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}"> 21 <mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
@@ -24,19 +24,8 @@ @@ -24,19 +24,8 @@
24 </mat-option> 24 </mat-option>
25 </mat-select> 25 </mat-select>
26 </mat-form-field> 26 </mat-form-field>
27 - <div fxFlex="60" fxLayout="column" [ngSwitch]="valueType">  
28 - <ng-template [ngSwitchCase]="valueTypeEnum.NUMERIC">  
29 - <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">  
30 - <mat-label></mat-label>  
31 - <input required type="number" matInput formControlName="value"  
32 - placeholder="{{'filter.value' | translate}}">  
33 - </mat-form-field>  
34 - </ng-template>  
35 - <ng-template [ngSwitchCase]="valueTypeEnum.DATE_TIME">  
36 - <tb-datetime fxFlex formControlName="value"  
37 - dateText="filter.date"  
38 - timeText="filter.time"  
39 - required [showLabel]="false"></tb-datetime>  
40 - </ng-template>  
41 - </div> 27 + <tb-filter-predicate-value fxFlex="60"
  28 + [valueType]="valueType"
  29 + formControlName="value">
  30 + </tb-filter-predicate-value>
42 </div> 31 </div>
@@ -18,9 +18,11 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'; @@ -18,9 +18,11 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core';
18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19 import { 19 import {
20 EntityKeyValueType, 20 EntityKeyValueType,
21 - FilterPredicateType, NumericFilterPredicate, NumericOperation, numericOperationTranslationMap, 21 + FilterPredicateType,
  22 + NumericFilterPredicate,
  23 + NumericOperation,
  24 + numericOperationTranslationMap,
22 } from '@shared/models/query/query.models'; 25 } from '@shared/models/query/query.models';
23 -import { isDefined } from '@core/utils';  
24 26
25 @Component({ 27 @Component({
26 selector: 'tb-numeric-filter-predicate', 28 selector: 'tb-numeric-filter-predicate',
@@ -56,7 +58,7 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On @@ -56,7 +58,7 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On
56 ngOnInit(): void { 58 ngOnInit(): void {
57 this.numericFilterPredicateFormGroup = this.fb.group({ 59 this.numericFilterPredicateFormGroup = this.fb.group({
58 operation: [NumericOperation.EQUAL, [Validators.required]], 60 operation: [NumericOperation.EQUAL, [Validators.required]],
59 - value: [0, [Validators.required]] 61 + value: [null, [Validators.required]]
60 }); 62 });
61 this.numericFilterPredicateFormGroup.valueChanges.subscribe(() => { 63 this.numericFilterPredicateFormGroup.valueChanges.subscribe(() => {
62 this.updateModel(); 64 this.updateModel();
@@ -81,16 +83,13 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On @@ -81,16 +83,13 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On
81 83
82 writeValue(predicate: NumericFilterPredicate): void { 84 writeValue(predicate: NumericFilterPredicate): void {
83 this.numericFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false}); 85 this.numericFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false});
84 - this.numericFilterPredicateFormGroup.get('value').patchValue(isDefined(predicate.value) ? predicate.value : 0, {emitEvent: false}); 86 + this.numericFilterPredicateFormGroup.get('value').patchValue(predicate.value, {emitEvent: false});
85 } 87 }
86 88
87 private updateModel() { 89 private updateModel() {
88 let predicate: NumericFilterPredicate = null; 90 let predicate: NumericFilterPredicate = null;
89 if (this.numericFilterPredicateFormGroup.valid) { 91 if (this.numericFilterPredicateFormGroup.valid) {
90 predicate = this.numericFilterPredicateFormGroup.getRawValue(); 92 predicate = this.numericFilterPredicateFormGroup.getRawValue();
91 - if (!predicate.value) {  
92 - predicate.value = 0;  
93 - }  
94 predicate.type = FilterPredicateType.NUMERIC; 93 predicate.type = FilterPredicateType.NUMERIC;
95 } 94 }
96 this.propagateChange(predicate); 95 this.propagateChange(predicate);
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="stringFilterPredicateFormGroup"> 18 +<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="stringFilterPredicateFormGroup">
19 <div fxFlex="40" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> 19 <div fxFlex="40" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
20 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block"> 20 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
21 <mat-label></mat-label> 21 <mat-label></mat-label>
@@ -28,8 +28,8 @@ @@ -28,8 +28,8 @@
28 <mat-checkbox fxLayout="row" fxLayoutAlign="center" formControlName="ignoreCase" style="min-width: 70px;"> 28 <mat-checkbox fxLayout="row" fxLayoutAlign="center" formControlName="ignoreCase" style="min-width: 70px;">
29 </mat-checkbox> 29 </mat-checkbox>
30 </div> 30 </div>
31 - <mat-form-field floatLabel="always" hideRequiredMarker fxFlex="60" class="mat-block">  
32 - <mat-label></mat-label>  
33 - <input matInput formControlName="value" placeholder="{{'filter.value' | translate}}">  
34 - </mat-form-field> 31 + <tb-filter-predicate-value fxFlex="60"
  32 + [valueType]="valueTypeEnum.STRING"
  33 + formControlName="value">
  34 + </tb-filter-predicate-value>
35 </div> 35 </div>
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 import { Component, forwardRef, Input, OnInit } from '@angular/core'; 17 import { Component, forwardRef, Input, OnInit } from '@angular/core';
18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19 import { 19 import {
  20 + EntityKeyValueType,
20 FilterPredicateType, 21 FilterPredicateType,
21 StringFilterPredicate, 22 StringFilterPredicate,
22 StringOperation, 23 StringOperation,
@@ -39,6 +40,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI @@ -39,6 +40,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
39 40
40 @Input() disabled: boolean; 41 @Input() disabled: boolean;
41 42
  43 + valueTypeEnum = EntityKeyValueType;
  44 +
42 stringFilterPredicateFormGroup: FormGroup; 45 stringFilterPredicateFormGroup: FormGroup;
43 46
44 stringOperations = Object.keys(StringOperation); 47 stringOperations = Object.keys(StringOperation);
@@ -53,7 +56,7 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI @@ -53,7 +56,7 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
53 ngOnInit(): void { 56 ngOnInit(): void {
54 this.stringFilterPredicateFormGroup = this.fb.group({ 57 this.stringFilterPredicateFormGroup = this.fb.group({
55 operation: [StringOperation.STARTS_WITH, [Validators.required]], 58 operation: [StringOperation.STARTS_WITH, [Validators.required]],
56 - value: [''], 59 + value: [null, [Validators.required]],
57 ignoreCase: [false] 60 ignoreCase: [false]
58 }); 61 });
59 this.stringFilterPredicateFormGroup.valueChanges.subscribe(() => { 62 this.stringFilterPredicateFormGroup.valueChanges.subscribe(() => {
@@ -79,7 +82,7 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI @@ -79,7 +82,7 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
79 82
80 writeValue(predicate: StringFilterPredicate): void { 83 writeValue(predicate: StringFilterPredicate): void {
81 this.stringFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false}); 84 this.stringFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false});
82 - this.stringFilterPredicateFormGroup.get('value').patchValue(predicate.value ? predicate.value : '', {emitEvent: false}); 85 + this.stringFilterPredicateFormGroup.get('value').patchValue(predicate.value, {emitEvent: false});
83 this.stringFilterPredicateFormGroup.get('ignoreCase').patchValue(predicate.ignoreCase, {emitEvent: false}); 86 this.stringFilterPredicateFormGroup.get('ignoreCase').patchValue(predicate.ignoreCase, {emitEvent: false});
84 } 87 }
85 88
@@ -87,9 +90,6 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI @@ -87,9 +90,6 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
87 let predicate: StringFilterPredicate = null; 90 let predicate: StringFilterPredicate = null;
88 if (this.stringFilterPredicateFormGroup.valid) { 91 if (this.stringFilterPredicateFormGroup.valid) {
89 predicate = this.stringFilterPredicateFormGroup.getRawValue(); 92 predicate = this.stringFilterPredicateFormGroup.getRawValue();
90 - if (!predicate.value) {  
91 - predicate.value = '';  
92 - }  
93 predicate.type = FilterPredicateType.STRING; 93 predicate.type = FilterPredicateType.STRING;
94 } 94 }
95 this.propagateChange(predicate); 95 this.propagateChange(predicate);
@@ -84,12 +84,12 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC @@ -84,12 +84,12 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC
84 const userInputControl = this.fb.group({ 84 const userInputControl = this.fb.group({
85 label: [userInput.label], 85 label: [userInput.label],
86 valueType: [userInput.valueType], 86 valueType: [userInput.valueType],
87 - value: [(userInput.info.keyFilterPredicate as any).value, 87 + value: [(userInput.info.keyFilterPredicate as any).value.defaultValue,
88 userInput.valueType === EntityKeyValueType.NUMERIC || 88 userInput.valueType === EntityKeyValueType.NUMERIC ||
89 userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []] 89 userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []]
90 }); 90 });
91 userInputControl.get('value').valueChanges.subscribe(value => { 91 userInputControl.get('value').valueChanges.subscribe(value => {
92 - (userInput.info.keyFilterPredicate as any).value = value; 92 + (userInput.info.keyFilterPredicate as any).value.defaultValue = value;
93 }); 93 });
94 return userInputControl; 94 return userInputControl;
95 } 95 }
@@ -83,6 +83,7 @@ import { FiltersEditPanelComponent } from '@home/components/filter/filters-edit- @@ -83,6 +83,7 @@ import { FiltersEditPanelComponent } from '@home/components/filter/filters-edit-
83 import { UserFilterDialogComponent } from '@home/components/filter/user-filter-dialog.component'; 83 import { UserFilterDialogComponent } from '@home/components/filter/user-filter-dialog.component';
84 import { FilterUserInfoComponent } from './filter/filter-user-info.component'; 84 import { FilterUserInfoComponent } from './filter/filter-user-info.component';
85 import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.component'; 85 import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.component';
  86 +import { FilterPredicateValueComponent } from './filter/filter-predicate-value.component';
86 87
87 @NgModule({ 88 @NgModule({
88 declarations: 89 declarations:
@@ -148,7 +149,8 @@ import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog. @@ -148,7 +149,8 @@ import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.
148 FiltersEditPanelComponent, 149 FiltersEditPanelComponent,
149 UserFilterDialogComponent, 150 UserFilterDialogComponent,
150 FilterUserInfoComponent, 151 FilterUserInfoComponent,
151 - FilterUserInfoDialogComponent 152 + FilterUserInfoDialogComponent,
  153 + FilterPredicateValueComponent
152 ], 154 ],
153 imports: [ 155 imports: [
154 CommonModule, 156 CommonModule,
@@ -138,16 +138,22 @@ export function createDefaultFilterPredicate(valueType: EntityKeyValueType, comp @@ -138,16 +138,22 @@ export function createDefaultFilterPredicate(valueType: EntityKeyValueType, comp
138 switch (predicate.type) { 138 switch (predicate.type) {
139 case FilterPredicateType.STRING: 139 case FilterPredicateType.STRING:
140 predicate.operation = StringOperation.STARTS_WITH; 140 predicate.operation = StringOperation.STARTS_WITH;
141 - predicate.value = ''; 141 + predicate.value = {
  142 + defaultValue: ''
  143 + };
142 predicate.ignoreCase = false; 144 predicate.ignoreCase = false;
143 break; 145 break;
144 case FilterPredicateType.NUMERIC: 146 case FilterPredicateType.NUMERIC:
145 predicate.operation = NumericOperation.EQUAL; 147 predicate.operation = NumericOperation.EQUAL;
146 - predicate.value = valueType === EntityKeyValueType.DATE_TIME ? Date.now() : 0; 148 + predicate.value = {
  149 + defaultValue: valueType === EntityKeyValueType.DATE_TIME ? Date.now() : 0
  150 + };
147 break; 151 break;
148 case FilterPredicateType.BOOLEAN: 152 case FilterPredicateType.BOOLEAN:
149 predicate.operation = BooleanOperation.EQUAL; 153 predicate.operation = BooleanOperation.EQUAL;
150 - predicate.value = false; 154 + predicate.value = {
  155 + defaultValue: false
  156 + };
151 break; 157 break;
152 case FilterPredicateType.COMPLEX: 158 case FilterPredicateType.COMPLEX:
153 predicate.operation = ComplexOperation.AND; 159 predicate.operation = ComplexOperation.AND;
@@ -228,23 +234,47 @@ export const complexOperationTranslationMap = new Map<ComplexOperation, string>( @@ -228,23 +234,47 @@ export const complexOperationTranslationMap = new Map<ComplexOperation, string>(
228 ] 234 ]
229 ); 235 );
230 236
  237 +export enum DynamicValueSourceType {
  238 + CURRENT_TENANT = 'CURRENT_TENANT',
  239 + CURRENT_CUSTOMER = 'CURRENT_CUSTOMER',
  240 + CURRENT_USER = 'CURRENT_USER'
  241 +}
  242 +
  243 +export const dynamicValueSourceTypeTranslationMap = new Map<DynamicValueSourceType, string>(
  244 + [
  245 + [DynamicValueSourceType.CURRENT_TENANT, 'filter.current-tenant'],
  246 + [DynamicValueSourceType.CURRENT_CUSTOMER, 'filter.current-customer'],
  247 + [DynamicValueSourceType.CURRENT_USER, 'filter.current-user']
  248 + ]
  249 +);
  250 +
  251 +export interface DynamicValue<T> {
  252 + sourceType: DynamicValueSourceType;
  253 + sourceAttribute: string;
  254 +}
  255 +
  256 +export interface FilterPredicateValue<T> {
  257 + defaultValue: T;
  258 + dynamicValue?: DynamicValue<T>;
  259 +}
  260 +
231 export interface StringFilterPredicate { 261 export interface StringFilterPredicate {
232 type: FilterPredicateType.STRING, 262 type: FilterPredicateType.STRING,
233 operation: StringOperation; 263 operation: StringOperation;
234 - value: string; 264 + value: FilterPredicateValue<string>;
235 ignoreCase: boolean; 265 ignoreCase: boolean;
236 } 266 }
237 267
238 export interface NumericFilterPredicate { 268 export interface NumericFilterPredicate {
239 type: FilterPredicateType.NUMERIC, 269 type: FilterPredicateType.NUMERIC,
240 operation: NumericOperation; 270 operation: NumericOperation;
241 - value: number; 271 + value: FilterPredicateValue<number>;
242 } 272 }
243 273
244 export interface BooleanFilterPredicate { 274 export interface BooleanFilterPredicate {
245 type: FilterPredicateType.BOOLEAN, 275 type: FilterPredicateType.BOOLEAN,
246 operation: BooleanOperation; 276 operation: BooleanOperation;
247 - value: boolean; 277 + value: FilterPredicateValue<boolean>;
248 } 278 }
249 279
250 export interface BaseComplexFilterPredicate<T extends KeyFilterPredicate | KeyFilterPredicateInfo> { 280 export interface BaseComplexFilterPredicate<T extends KeyFilterPredicate | KeyFilterPredicateInfo> {
@@ -1227,7 +1227,10 @@ @@ -1227,7 +1227,10 @@
1227 "remove-key-filter": "Remove key filter", 1227 "remove-key-filter": "Remove key filter",
1228 "edit-key-filter": "Edit key filter", 1228 "edit-key-filter": "Edit key filter",
1229 "date": "Date", 1229 "date": "Date",
1230 - "time": "Time" 1230 + "time": "Time",
  1231 + "current-tenant": "Current tenant",
  1232 + "current-customer": "Current customer",
  1233 + "current-user": "Current user"
1231 }, 1234 },
1232 "fullscreen": { 1235 "fullscreen": {
1233 "expand": "Expand to fullscreen", 1236 "expand": "Expand to fullscreen",