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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java
0 → 100644
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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java
0 → 100644
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); | ... | ... |
... | ... | @@ -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", | ... | ... |