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 44 import org.thingsboard.server.common.data.query.EntityKey;
45 45 import org.thingsboard.server.common.data.query.EntityKeyType;
46 46 import org.thingsboard.server.common.data.query.EntityListFilter;
  47 +import org.thingsboard.server.common.data.query.FilterPredicateValue;
47 48 import org.thingsboard.server.common.data.query.KeyFilter;
48 49 import org.thingsboard.server.common.data.query.NumericFilterPredicate;
49 50 import org.thingsboard.server.common.data.security.Authority;
... ... @@ -259,7 +260,7 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe
259 260 KeyFilter highTemperatureFilter = new KeyFilter();
260 261 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
261 262 NumericFilterPredicate predicate = new NumericFilterPredicate();
262   - predicate.setValue(45);
  263 + predicate.setValue(FilterPredicateValue.fromDouble(45));
263 264 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
264 265 highTemperatureFilter.setPredicate(predicate);
265 266 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
... ...
... ... @@ -26,9 +26,9 @@ import java.util.Arrays;
26 26
27 27 @RunWith(ClasspathSuite.class)
28 28 @ClasspathSuite.ClassnameFilters({
29   - "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
  29 +// "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
30 30 // "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest",
31   -// "org.thingsboard.server.controller.sql.*Test",
  31 + "org.thingsboard.server.controller.sql.*Test",
32 32 })
33 33 public class ControllerSqlTestSuite {
34 34
... ...
... ... @@ -21,7 +21,7 @@ import lombok.Data;
21 21 public class BooleanFilterPredicate implements KeyFilterPredicate {
22 22
23 23 private BooleanOperation operation;
24   - private boolean value;
  24 + private FilterPredicateValue<Boolean> value;
25 25
26 26 @Override
27 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 21 public class NumericFilterPredicate implements KeyFilterPredicate {
22 22
23 23 private NumericOperation operation;
24   - private double value;
  24 + private FilterPredicateValue<Double> value;
25 25
26 26 @Override
27 27 public FilterPredicateType getType() {
... ...
... ... @@ -21,7 +21,7 @@ import lombok.Data;
21 21 public class StringFilterPredicate implements KeyFilterPredicate {
22 22
23 23 private StringOperation operation;
24   - private String value;
  24 + private FilterPredicateValue<String> value;
25 25 private boolean ignoreCase;
26 26
27 27 @Override
... ...
... ... @@ -440,7 +440,7 @@ public class EntityKeyMapping {
440 440 private String buildStringPredicateQuery(QueryContext ctx, String field, StringFilterPredicate stringFilterPredicate) {
441 441 String operationField = field;
442 442 String paramName = getNextParameterName(field);
443   - String value = stringFilterPredicate.getValue();
  443 + String value = stringFilterPredicate.getValue().getValue();
444 444 String stringOperationQuery = "";
445 445 if (stringFilterPredicate.isIgnoreCase()) {
446 446 value = value.toLowerCase();
... ... @@ -476,7 +476,7 @@ public class EntityKeyMapping {
476 476
477 477 private String buildNumericPredicateQuery(QueryContext ctx, String field, NumericFilterPredicate numericFilterPredicate) {
478 478 String paramName = getNextParameterName(field);
479   - ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue());
  479 + ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue().getValue());
480 480 String numericOperationQuery = "";
481 481 switch (numericFilterPredicate.getOperation()) {
482 482 case EQUAL:
... ... @@ -504,7 +504,7 @@ public class EntityKeyMapping {
504 504 private String buildBooleanPredicateQuery(QueryContext ctx, String field,
505 505 BooleanFilterPredicate booleanFilterPredicate) {
506 506 String paramName = getNextParameterName(field);
507   - ctx.addBooleanParameter(paramName, booleanFilterPredicate.isValue());
  507 + ctx.addBooleanParameter(paramName, booleanFilterPredicate.getValue().getValue());
508 508 String booleanOperationQuery = "";
509 509 switch (booleanFilterPredicate.getOperation()) {
510 510 case EQUAL:
... ...
... ... @@ -49,6 +49,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder;
49 49 import org.thingsboard.server.common.data.query.EntityKey;
50 50 import org.thingsboard.server.common.data.query.EntityKeyType;
51 51 import org.thingsboard.server.common.data.query.EntityListFilter;
  52 +import org.thingsboard.server.common.data.query.FilterPredicateValue;
52 53 import org.thingsboard.server.common.data.query.KeyFilter;
53 54 import org.thingsboard.server.common.data.query.NumericFilterPredicate;
54 55 import org.thingsboard.server.common.data.query.RelationsQueryFilter;
... ... @@ -239,7 +240,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
239 240 KeyFilter highTemperatureFilter = new KeyFilter();
240 241 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
241 242 NumericFilterPredicate predicate = new NumericFilterPredicate();
242   - predicate.setValue(45);
  243 + predicate.setValue(FilterPredicateValue.fromDouble(45));
243 244 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
244 245 highTemperatureFilter.setPredicate(predicate);
245 246 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
... ... @@ -311,7 +312,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
311 312 KeyFilter highTemperatureFilter = new KeyFilter();
312 313 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
313 314 NumericFilterPredicate predicate = new NumericFilterPredicate();
314   - predicate.setValue(45);
  315 + predicate.setValue(FilterPredicateValue.fromDouble(45));
315 316 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
316 317 highTemperatureFilter.setPredicate(predicate);
317 318 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
... ... @@ -382,7 +383,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
382 383 KeyFilter highTemperatureFilter = new KeyFilter();
383 384 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "consumption"));
384 385 NumericFilterPredicate predicate = new NumericFilterPredicate();
385   - predicate.setValue(50);
  386 + predicate.setValue(FilterPredicateValue.fromDouble(50));
386 387 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
387 388 highTemperatureFilter.setPredicate(predicate);
388 389 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
... ... @@ -584,7 +585,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
584 585 KeyFilter highTemperatureFilter = new KeyFilter();
585 586 highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
586 587 NumericFilterPredicate predicate = new NumericFilterPredicate();
587   - predicate.setValue(45);
  588 + predicate.setValue(FilterPredicateValue.fromDouble(45));
588 589 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
589 590 highTemperatureFilter.setPredicate(predicate);
590 591 List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
... ...
... ... @@ -396,7 +396,9 @@ export class EntityService {
396 396 type: FilterPredicateType.STRING,
397 397 operation: StringOperation.STARTS_WITH,
398 398 ignoreCase: true,
399   - value: searchText
  399 + value: {
  400 + defaultValue: searchText
  401 + }
400 402 }
401 403 }
402 404 ] : null
... ...
... ... @@ -15,7 +15,7 @@
15 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 19 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block">
20 20 <mat-label></mat-label>
21 21 <mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
... ... @@ -24,7 +24,8 @@
24 24 </mat-option>
25 25 </mat-select>
26 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 31 </div>
... ...
... ... @@ -18,10 +18,10 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19 19 import {
20 20 BooleanFilterPredicate,
21   - BooleanOperation, booleanOperationTranslationMap,
  21 + BooleanOperation,
  22 + booleanOperationTranslationMap, EntityKeyValueType,
22 23 FilterPredicateType
23 24 } from '@shared/models/query/query.models';
24   -import { isDefined } from '@core/utils';
25 25
26 26 @Component({
27 27 selector: 'tb-boolean-filter-predicate',
... ... @@ -39,6 +39,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
39 39
40 40 @Input() disabled: boolean;
41 41
  42 + valueTypeEnum = EntityKeyValueType;
  43 +
42 44 booleanFilterPredicateFormGroup: FormGroup;
43 45
44 46 booleanOperations = Object.keys(BooleanOperation);
... ... @@ -53,7 +55,7 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
53 55 ngOnInit(): void {
54 56 this.booleanFilterPredicateFormGroup = this.fb.group({
55 57 operation: [BooleanOperation.EQUAL, [Validators.required]],
56   - value: [false]
  58 + value: [null, [Validators.required]]
57 59 });
58 60 this.booleanFilterPredicateFormGroup.valueChanges.subscribe(() => {
59 61 this.updateModel();
... ... @@ -78,16 +80,13 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
78 80
79 81 writeValue(predicate: BooleanFilterPredicate): void {
80 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 86 private updateModel() {
85 87 let predicate: BooleanFilterPredicate = null;
86 88 if (this.booleanFilterPredicateFormGroup.valid) {
87 89 predicate = this.booleanFilterPredicateFormGroup.getRawValue();
88   - if (!isDefined(predicate.value)) {
89   - predicate.value = false;
90   - }
91 90 predicate.type = FilterPredicateType.BOOLEAN;
92 91 }
93 92 this.propagateChange(predicate);
... ...
... ... @@ -39,7 +39,7 @@
39 39 </div>
40 40 <mat-divider></mat-divider>
41 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 43 formArrayName="predicates"
44 44 *ngFor="let predicateControl of predicatesFormArray().controls; let $index = index">
45 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 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
  16 +
  17 +:host {
  18 + .tb-hint {
  19 + padding-bottom: 0;
  20 + }
  21 +}
  22 +
16 23 :host ::ng-deep {
17 24 mat-form-field {
18 25 .mat-form-field-wrapper {
... ...
... ... @@ -15,7 +15,7 @@
15 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 19 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block">
20 20 <mat-label></mat-label>
21 21 <mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
... ... @@ -24,19 +24,8 @@
24 24 </mat-option>
25 25 </mat-select>
26 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 31 </div>
... ...
... ... @@ -18,9 +18,11 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19 19 import {
20 20 EntityKeyValueType,
21   - FilterPredicateType, NumericFilterPredicate, NumericOperation, numericOperationTranslationMap,
  21 + FilterPredicateType,
  22 + NumericFilterPredicate,
  23 + NumericOperation,
  24 + numericOperationTranslationMap,
22 25 } from '@shared/models/query/query.models';
23   -import { isDefined } from '@core/utils';
24 26
25 27 @Component({
26 28 selector: 'tb-numeric-filter-predicate',
... ... @@ -56,7 +58,7 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On
56 58 ngOnInit(): void {
57 59 this.numericFilterPredicateFormGroup = this.fb.group({
58 60 operation: [NumericOperation.EQUAL, [Validators.required]],
59   - value: [0, [Validators.required]]
  61 + value: [null, [Validators.required]]
60 62 });
61 63 this.numericFilterPredicateFormGroup.valueChanges.subscribe(() => {
62 64 this.updateModel();
... ... @@ -81,16 +83,13 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On
81 83
82 84 writeValue(predicate: NumericFilterPredicate): void {
83 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 89 private updateModel() {
88 90 let predicate: NumericFilterPredicate = null;
89 91 if (this.numericFilterPredicateFormGroup.valid) {
90 92 predicate = this.numericFilterPredicateFormGroup.getRawValue();
91   - if (!predicate.value) {
92   - predicate.value = 0;
93   - }
94 93 predicate.type = FilterPredicateType.NUMERIC;
95 94 }
96 95 this.propagateChange(predicate);
... ...
... ... @@ -15,7 +15,7 @@
15 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 19 <div fxFlex="40" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
20 20 <mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
21 21 <mat-label></mat-label>
... ... @@ -28,8 +28,8 @@
28 28 <mat-checkbox fxLayout="row" fxLayoutAlign="center" formControlName="ignoreCase" style="min-width: 70px;">
29 29 </mat-checkbox>
30 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 35 </div>
... ...
... ... @@ -17,6 +17,7 @@
17 17 import { Component, forwardRef, Input, OnInit } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19 19 import {
  20 + EntityKeyValueType,
20 21 FilterPredicateType,
21 22 StringFilterPredicate,
22 23 StringOperation,
... ... @@ -39,6 +40,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
39 40
40 41 @Input() disabled: boolean;
41 42
  43 + valueTypeEnum = EntityKeyValueType;
  44 +
42 45 stringFilterPredicateFormGroup: FormGroup;
43 46
44 47 stringOperations = Object.keys(StringOperation);
... ... @@ -53,7 +56,7 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
53 56 ngOnInit(): void {
54 57 this.stringFilterPredicateFormGroup = this.fb.group({
55 58 operation: [StringOperation.STARTS_WITH, [Validators.required]],
56   - value: [''],
  59 + value: [null, [Validators.required]],
57 60 ignoreCase: [false]
58 61 });
59 62 this.stringFilterPredicateFormGroup.valueChanges.subscribe(() => {
... ... @@ -79,7 +82,7 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
79 82
80 83 writeValue(predicate: StringFilterPredicate): void {
81 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 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 90 let predicate: StringFilterPredicate = null;
88 91 if (this.stringFilterPredicateFormGroup.valid) {
89 92 predicate = this.stringFilterPredicateFormGroup.getRawValue();
90   - if (!predicate.value) {
91   - predicate.value = '';
92   - }
93 93 predicate.type = FilterPredicateType.STRING;
94 94 }
95 95 this.propagateChange(predicate);
... ...
... ... @@ -84,12 +84,12 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC
84 84 const userInputControl = this.fb.group({
85 85 label: [userInput.label],
86 86 valueType: [userInput.valueType],
87   - value: [(userInput.info.keyFilterPredicate as any).value,
  87 + value: [(userInput.info.keyFilterPredicate as any).value.defaultValue,
88 88 userInput.valueType === EntityKeyValueType.NUMERIC ||
89 89 userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []]
90 90 });
91 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 94 return userInputControl;
95 95 }
... ...
... ... @@ -83,6 +83,7 @@ import { FiltersEditPanelComponent } from '@home/components/filter/filters-edit-
83 83 import { UserFilterDialogComponent } from '@home/components/filter/user-filter-dialog.component';
84 84 import { FilterUserInfoComponent } from './filter/filter-user-info.component';
85 85 import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.component';
  86 +import { FilterPredicateValueComponent } from './filter/filter-predicate-value.component';
86 87
87 88 @NgModule({
88 89 declarations:
... ... @@ -148,7 +149,8 @@ import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.
148 149 FiltersEditPanelComponent,
149 150 UserFilterDialogComponent,
150 151 FilterUserInfoComponent,
151   - FilterUserInfoDialogComponent
  152 + FilterUserInfoDialogComponent,
  153 + FilterPredicateValueComponent
152 154 ],
153 155 imports: [
154 156 CommonModule,
... ...
... ... @@ -138,16 +138,22 @@ export function createDefaultFilterPredicate(valueType: EntityKeyValueType, comp
138 138 switch (predicate.type) {
139 139 case FilterPredicateType.STRING:
140 140 predicate.operation = StringOperation.STARTS_WITH;
141   - predicate.value = '';
  141 + predicate.value = {
  142 + defaultValue: ''
  143 + };
142 144 predicate.ignoreCase = false;
143 145 break;
144 146 case FilterPredicateType.NUMERIC:
145 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 151 break;
148 152 case FilterPredicateType.BOOLEAN:
149 153 predicate.operation = BooleanOperation.EQUAL;
150   - predicate.value = false;
  154 + predicate.value = {
  155 + defaultValue: false
  156 + };
151 157 break;
152 158 case FilterPredicateType.COMPLEX:
153 159 predicate.operation = ComplexOperation.AND;
... ... @@ -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 261 export interface StringFilterPredicate {
232 262 type: FilterPredicateType.STRING,
233 263 operation: StringOperation;
234   - value: string;
  264 + value: FilterPredicateValue<string>;
235 265 ignoreCase: boolean;
236 266 }
237 267
238 268 export interface NumericFilterPredicate {
239 269 type: FilterPredicateType.NUMERIC,
240 270 operation: NumericOperation;
241   - value: number;
  271 + value: FilterPredicateValue<number>;
242 272 }
243 273
244 274 export interface BooleanFilterPredicate {
245 275 type: FilterPredicateType.BOOLEAN,
246 276 operation: BooleanOperation;
247   - value: boolean;
  277 + value: FilterPredicateValue<boolean>;
248 278 }
249 279
250 280 export interface BaseComplexFilterPredicate<T extends KeyFilterPredicate | KeyFilterPredicateInfo> {
... ...
... ... @@ -1227,7 +1227,10 @@
1227 1227 "remove-key-filter": "Remove key filter",
1228 1228 "edit-key-filter": "Edit key filter",
1229 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 1235 "fullscreen": {
1233 1236 "expand": "Expand to fullscreen",
... ...