Browse Source

Merge branch 'feature/entity-data-query' of github.com:thingsboard/thingsboard into feature/entity-data-query

pull/3053/head
Andrii Shvaika 6 years ago
parent
commit
39287c8273
  1. 3
      application/src/test/java/org/thingsboard/server/controller/BaseEntityQueryControllerTest.java
  2. 4
      application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java
  3. 2
      common/data/src/main/java/org/thingsboard/server/common/data/query/BooleanFilterPredicate.java
  4. 33
      common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java
  5. 22
      common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java
  6. 71
      common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java
  7. 2
      common/data/src/main/java/org/thingsboard/server/common/data/query/NumericFilterPredicate.java
  8. 2
      common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java
  9. 8
      dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java
  10. 9
      dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java
  11. 4
      ui-ngx/package-lock.json
  12. 4
      ui-ngx/src/app/core/http/entity.service.ts
  13. 9
      ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html
  14. 13
      ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts
  15. 2
      ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html
  16. 2
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html
  17. 82
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html
  18. 146
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts
  19. 7
      ui-ngx/src/app/modules/home/components/filter/filter-predicate.scss
  20. 4
      ui-ngx/src/app/modules/home/components/filter/filters-edit-panel.component.html
  21. 2
      ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.html
  22. 21
      ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.html
  23. 13
      ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts
  24. 10
      ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.html
  25. 10
      ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts
  26. 11
      ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.ts
  27. 4
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  28. 43
      ui-ngx/src/app/shared/models/query/query.models.ts
  29. 11
      ui-ngx/src/assets/locale/locale.constant-en_US.json

3
application/src/test/java/org/thingsboard/server/controller/BaseEntityQueryControllerTest.java

@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.EntityListFilter;
import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.query.KeyFilter;
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
import org.thingsboard.server.common.data.security.Authority;
@ -259,7 +260,7 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe
KeyFilter highTemperatureFilter = new KeyFilter();
highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
NumericFilterPredicate predicate = new NumericFilterPredicate();
predicate.setValue(45);
predicate.setValue(FilterPredicateValue.fromDouble(45));
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
highTemperatureFilter.setPredicate(predicate);
List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);

4
application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java

@ -26,9 +26,9 @@ import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
// "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
// "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest",
// "org.thingsboard.server.controller.sql.*Test",
"org.thingsboard.server.controller.sql.*Test",
})
public class ControllerSqlTestSuite {

2
common/data/src/main/java/org/thingsboard/server/common/data/query/BooleanFilterPredicate.java

@ -21,7 +21,7 @@ import lombok.Data;
public class BooleanFilterPredicate implements KeyFilterPredicate {
private BooleanOperation operation;
private boolean value;
private FilterPredicateValue<Boolean> value;
@Override
public FilterPredicateType getType() {

33
common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValue.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.query;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.Getter;
@Data
public class DynamicValue<T> {
@JsonIgnore
private T resolvedValue;
@Getter
private final DynamicValueSourceType sourceType;
@Getter
private final String sourceAttribute;
}

22
common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java

@ -0,0 +1,22 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.query;
public enum DynamicValueSourceType {
CURRENT_TENANT,
CURRENT_CUSTOMER,
CURRENT_USER
}

71
common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java

@ -0,0 +1,71 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.query;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.Getter;
@Data
public class FilterPredicateValue<T> {
@Getter
private final T defaultValue;
@Getter
private final T userValue;
@Getter
private final DynamicValue<T> dynamicValue;
public FilterPredicateValue(T defaultValue) {
this(defaultValue, null, null);
}
@JsonCreator
public FilterPredicateValue(@JsonProperty("defaultValue") T defaultValue,
@JsonProperty("userValue") T userValue,
@JsonProperty("dynamicValue") DynamicValue<T> dynamicValue) {
this.defaultValue = defaultValue;
this.userValue = userValue;
this.dynamicValue = dynamicValue;
}
@JsonIgnore
public T getValue() {
if (this.userValue != null) {
return this.userValue;
} else {
if (this.dynamicValue != null && this.dynamicValue.getResolvedValue() != null) {
return this.dynamicValue.getResolvedValue();
} else {
return defaultValue;
}
}
}
public static FilterPredicateValue<Double> fromDouble(double value) {
return new FilterPredicateValue<>(value);
}
public static FilterPredicateValue<String> fromString(String value) {
return new FilterPredicateValue<>(value);
}
public static FilterPredicateValue<Boolean> fromBoolean(boolean value) {
return new FilterPredicateValue<>(value);
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/query/NumericFilterPredicate.java

@ -21,7 +21,7 @@ import lombok.Data;
public class NumericFilterPredicate implements KeyFilterPredicate {
private NumericOperation operation;
private double value;
private FilterPredicateValue<Double> value;
@Override
public FilterPredicateType getType() {

2
common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java

@ -21,7 +21,7 @@ import lombok.Data;
public class StringFilterPredicate implements KeyFilterPredicate {
private StringOperation operation;
private String value;
private FilterPredicateValue<String> value;
private boolean ignoreCase;
@Override

8
dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java

@ -406,7 +406,7 @@ public class EntityKeyMapping {
String entityFieldAlias = getEntityFieldAlias(filterType, entityType);
String column = null;
if (existingEntityFields.contains(entityFieldAlias)) {
column = entityFieldColumnMap.get(key.getKey());
column = entityFieldColumnMap.get(entityFieldAlias);
}
if (column != null) {
String field = alias + "." + column;
@ -440,7 +440,7 @@ public class EntityKeyMapping {
private String buildStringPredicateQuery(QueryContext ctx, String field, StringFilterPredicate stringFilterPredicate) {
String operationField = field;
String paramName = getNextParameterName(field);
String value = stringFilterPredicate.getValue();
String value = stringFilterPredicate.getValue().getValue();
String stringOperationQuery = "";
if (stringFilterPredicate.isIgnoreCase()) {
value = value.toLowerCase();
@ -476,7 +476,7 @@ public class EntityKeyMapping {
private String buildNumericPredicateQuery(QueryContext ctx, String field, NumericFilterPredicate numericFilterPredicate) {
String paramName = getNextParameterName(field);
ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue());
ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue().getValue());
String numericOperationQuery = "";
switch (numericFilterPredicate.getOperation()) {
case EQUAL:
@ -504,7 +504,7 @@ public class EntityKeyMapping {
private String buildBooleanPredicateQuery(QueryContext ctx, String field,
BooleanFilterPredicate booleanFilterPredicate) {
String paramName = getNextParameterName(field);
ctx.addBooleanParameter(paramName, booleanFilterPredicate.isValue());
ctx.addBooleanParameter(paramName, booleanFilterPredicate.getValue().getValue());
String booleanOperationQuery = "";
switch (booleanFilterPredicate.getOperation()) {
case EQUAL:

9
dao/src/test/java/org/thingsboard/server/dao/service/BaseEntityServiceTest.java

@ -49,6 +49,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.EntityListFilter;
import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.query.KeyFilter;
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
import org.thingsboard.server.common.data.query.RelationsQueryFilter;
@ -239,7 +240,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
KeyFilter highTemperatureFilter = new KeyFilter();
highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
NumericFilterPredicate predicate = new NumericFilterPredicate();
predicate.setValue(45);
predicate.setValue(FilterPredicateValue.fromDouble(45));
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
highTemperatureFilter.setPredicate(predicate);
List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
@ -311,7 +312,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
KeyFilter highTemperatureFilter = new KeyFilter();
highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
NumericFilterPredicate predicate = new NumericFilterPredicate();
predicate.setValue(45);
predicate.setValue(FilterPredicateValue.fromDouble(45));
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
highTemperatureFilter.setPredicate(predicate);
List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
@ -382,7 +383,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
KeyFilter highTemperatureFilter = new KeyFilter();
highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "consumption"));
NumericFilterPredicate predicate = new NumericFilterPredicate();
predicate.setValue(50);
predicate.setValue(FilterPredicateValue.fromDouble(50));
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
highTemperatureFilter.setPredicate(predicate);
List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
@ -584,7 +585,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
KeyFilter highTemperatureFilter = new KeyFilter();
highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
NumericFilterPredicate predicate = new NumericFilterPredicate();
predicate.setValue(45);
predicate.setValue(FilterPredicateValue.fromDouble(45));
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
highTemperatureFilter.setPredicate(predicate);
List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);

4
ui-ngx/package-lock.json

@ -8997,10 +8997,10 @@
"integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw=="
},
"ngx-flowchart": {
"version": "git://github.com/thingsboard/ngx-flowchart.git#a4157b0eef2eb3646ef920447c7b06b39d54f87f",
"version": "git://github.com/thingsboard/ngx-flowchart.git#7a02f4748b5e7821a883c903107af5f20415d026",
"from": "git://github.com/thingsboard/ngx-flowchart.git#master",
"requires": {
"tslib": "^1.10.0"
"tslib": "^1.13.0"
},
"dependencies": {
"tslib": {

4
ui-ngx/src/app/core/http/entity.service.ts

@ -396,7 +396,9 @@ export class EntityService {
type: FilterPredicateType.STRING,
operation: StringOperation.STARTS_WITH,
ignoreCase: true,
value: searchText
value: {
defaultValue: searchText
}
}
}
] : null

9
ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="booleanFilterPredicateFormGroup">
<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="booleanFilterPredicateFormGroup">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block">
<mat-label></mat-label>
<mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
@ -24,7 +24,8 @@
</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox fxFlex="60" formControlName="value">
{{ (booleanFilterPredicateFormGroup.get('value').value ? 'value.true' : 'value.false') | translate }}
</mat-checkbox>
<tb-filter-predicate-value fxFlex="60"
[valueType]="valueTypeEnum.BOOLEAN"
formControlName="value">
</tb-filter-predicate-value>
</div>

13
ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts

@ -18,10 +18,10 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {
BooleanFilterPredicate,
BooleanOperation, booleanOperationTranslationMap,
BooleanOperation,
booleanOperationTranslationMap, EntityKeyValueType,
FilterPredicateType
} from '@shared/models/query/query.models';
import { isDefined } from '@core/utils';
@Component({
selector: 'tb-boolean-filter-predicate',
@ -39,6 +39,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
@Input() disabled: boolean;
valueTypeEnum = EntityKeyValueType;
booleanFilterPredicateFormGroup: FormGroup;
booleanOperations = Object.keys(BooleanOperation);
@ -53,7 +55,7 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
ngOnInit(): void {
this.booleanFilterPredicateFormGroup = this.fb.group({
operation: [BooleanOperation.EQUAL, [Validators.required]],
value: [false]
value: [null, [Validators.required]]
});
this.booleanFilterPredicateFormGroup.valueChanges.subscribe(() => {
this.updateModel();
@ -78,16 +80,13 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
writeValue(predicate: BooleanFilterPredicate): void {
this.booleanFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false});
this.booleanFilterPredicateFormGroup.get('value').patchValue(isDefined(predicate.value) ? predicate.value : false, {emitEvent: false});
this.booleanFilterPredicateFormGroup.get('value').patchValue(predicate.value, {emitEvent: false});
}
private updateModel() {
let predicate: BooleanFilterPredicate = null;
if (this.booleanFilterPredicateFormGroup.valid) {
predicate = this.booleanFilterPredicateFormGroup.getRawValue();
if (!isDefined(predicate.value)) {
predicate.value = false;
}
predicate.type = FilterPredicateType.BOOLEAN;
}
this.propagateChange(predicate);

2
ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<form [formGroup]="complexFilterFormGroup" (ngSubmit)="save()" style="width: 700px;">
<form [formGroup]="complexFilterFormGroup" (ngSubmit)="save()" style="width: 900px;">
<mat-toolbar color="primary">
<h2 translate>filter.complex-filter</h2>
<span fxFlex></span>

2
ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html

@ -39,7 +39,7 @@
</div>
<mat-divider></mat-divider>
<div class="predicate-list">
<div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;"
<div fxLayout="row" fxLayoutAlign="start center" style="height: 45px;"
formArrayName="predicates"
*ngFor="let predicateControl of predicatesFormArray().controls; let $index = index">
<div fxFlex="8" fxLayout="row" fxLayoutAlign="center" class="filters-operation">

82
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html

@ -0,0 +1,82 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateValueFormGroup">
<div fxFlex fxLayout="column" [fxShow]="!dynamicMode">
<div fxFlex fxLayout="column" [ngSwitch]="valueType">
<ng-template [ngSwitchCase]="valueTypeEnum.STRING">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
<mat-label></mat-label>
<input matInput formControlName="defaultValue" placeholder="{{'filter.value' | translate}}">
</mat-form-field>
</ng-template>
<ng-template [ngSwitchCase]="valueTypeEnum.NUMERIC">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
<mat-label></mat-label>
<input required type="number" matInput formControlName="defaultValue"
placeholder="{{'filter.value' | translate}}">
</mat-form-field>
</ng-template>
<ng-template [ngSwitchCase]="valueTypeEnum.DATE_TIME">
<tb-datetime fxFlex formControlName="defaultValue"
dateText="filter.date"
timeText="filter.time"
required [showLabel]="false"></tb-datetime>
</ng-template>
<ng-template [ngSwitchCase]="valueTypeEnum.BOOLEAN">
<mat-checkbox fxFlex formControlName="defaultValue">
{{ (filterPredicateValueFormGroup.get('defaultValue').value ? 'value.true' : 'value.false') | translate }}
</mat-checkbox>
</ng-template>
</div>
<div class="tb-hint" translate>filter.default-value</div>
</div>
<div fxFlex fxLayout="column" [fxShow]="dynamicMode">
<div fxFlex formGroupName="dynamicValue" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<div fxFlex fxLayout="column">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
<mat-label></mat-label>
<mat-select formControlName="sourceType" placeholder="{{'filter.dynamic-source-type' | translate}}">
<mat-option [value]="null">
{{'filter.no-dynamic-value' | translate}}
</mat-option>
<mat-option *ngFor="let sourceType of dynamicValueSourceTypes" [value]="sourceType">
{{dynamicValueSourceTypeTranslations.get(dynamicValueSourceTypeEnum[sourceType]) | translate}}
</mat-option>
</mat-select>
</mat-form-field>
<div class="tb-hint" translate>filter.dynamic-source-type</div>
</div>
<div fxFlex fxLayout="column">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
<mat-label></mat-label>
<input matInput formControlName="sourceAttribute" placeholder="{{'filter.source-attribute' | translate}}">
</mat-form-field>
<div class="tb-hint" translate>filter.source-attribute</div>
</div>
</div>
</div>
<button mat-icon-button
class="mat-elevation-z1 tb-mat-32"
color="primary"
type="button"
matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}"
matTooltipPosition="above"
(click)="dynamicMode = !dynamicMode">
<mat-icon class="tb-mat-20" [svgIcon]="dynamicMode ? 'mdi:numeric' : 'mdi:variable'"></mat-icon>
</button>
</div>

146
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts

@ -0,0 +1,146 @@
///
/// Copyright © 2016-2020 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALUE_ACCESSOR,
ValidatorFn,
Validators
} from '@angular/forms';
import {
DynamicValueSourceType,
dynamicValueSourceTypeTranslationMap,
EntityKeyValueType,
FilterPredicateValue
} from '@shared/models/query/query.models';
@Component({
selector: 'tb-filter-predicate-value',
templateUrl: './filter-predicate-value.component.html',
styleUrls: ['./filter-predicate.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FilterPredicateValueComponent),
multi: true
}
]
})
export class FilterPredicateValueComponent implements ControlValueAccessor, OnInit {
@Input() disabled: boolean;
@Input()
valueType: EntityKeyValueType;
valueTypeEnum = EntityKeyValueType;
dynamicValueSourceTypes = Object.keys(DynamicValueSourceType);
dynamicValueSourceTypeEnum = DynamicValueSourceType;
dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap;
filterPredicateValueFormGroup: FormGroup;
dynamicMode = false;
private propagateChange = null;
constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
let defaultValue: string | number | boolean;
let defaultValueValidators: ValidatorFn[];
switch (this.valueType) {
case EntityKeyValueType.STRING:
defaultValue = '';
defaultValueValidators = [];
break;
case EntityKeyValueType.NUMERIC:
defaultValue = 0;
defaultValueValidators = [Validators.required];
break;
case EntityKeyValueType.BOOLEAN:
defaultValue = false;
defaultValueValidators = [];
break;
case EntityKeyValueType.DATE_TIME:
defaultValue = Date.now();
defaultValueValidators = [Validators.required];
break;
}
this.filterPredicateValueFormGroup = this.fb.group({
defaultValue: [defaultValue, defaultValueValidators],
dynamicValue: this.fb.group(
{
sourceType: [null],
sourceAttribute: [null]
}
)
});
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').valueChanges.subscribe(
(sourceType) => {
if (!sourceType) {
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(null, {emitEvent: false});
}
}
);
this.filterPredicateValueFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.filterPredicateValueFormGroup.disable({emitEvent: false});
} else {
this.filterPredicateValueFormGroup.enable({emitEvent: false});
}
}
writeValue(predicateValue: FilterPredicateValue<string | number | boolean>): void {
this.filterPredicateValueFormGroup.get('defaultValue').patchValue(predicateValue.defaultValue, {emitEvent: false});
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceType').patchValue(predicateValue.dynamicValue ?
predicateValue.dynamicValue.sourceType : null, {emitEvent: false});
this.filterPredicateValueFormGroup.get('dynamicValue').get('sourceAttribute').patchValue(predicateValue.dynamicValue ?
predicateValue.dynamicValue.sourceAttribute : null, {emitEvent: false});
}
private updateModel() {
let predicateValue: FilterPredicateValue<string | number | boolean> = null;
if (this.filterPredicateValueFormGroup.valid) {
predicateValue = this.filterPredicateValueFormGroup.getRawValue();
if (predicateValue.dynamicValue) {
if (!predicateValue.dynamicValue.sourceType || !predicateValue.dynamicValue.sourceAttribute) {
predicateValue.dynamicValue = null;
}
}
}
this.propagateChange(predicateValue);
}
}

7
ui-ngx/src/app/modules/home/components/filter/filter-predicate.scss

@ -13,6 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
.tb-hint {
padding-bottom: 0;
}
}
:host ::ng-deep {
mat-form-field {
.mat-form-field-wrapper {

4
ui-ngx/src/app/modules/home/components/filter/filters-edit-panel.component.html

@ -16,7 +16,7 @@
-->
<div fxLayout="column" class="mat-content mat-padding">
<div fxLayout="column" *ngFor="let filter of filtersInfo | keyvalue">
<div fxLayout="column" *ngFor="let filter of filtersInfo | keyvalue; let last = last">
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<mat-label fxFlex>{{filter.value.filter}}</mat-label>
<button mat-icon-button color="primary"
@ -28,6 +28,6 @@
<mat-icon>edit</mat-icon>
</button>
</div>
<mat-divider></mat-divider>
<mat-divider *ngIf="!last"></mat-divider>
</div>
</div>

2
ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<form [formGroup]="keyFilterFormGroup" (ngSubmit)="save()" style="width: 700px;">
<form [formGroup]="keyFilterFormGroup" (ngSubmit)="save()" style="width: 900px;">
<mat-toolbar color="primary">
<h2>{{(data.isAdd ? 'filter.add-key-filter' : 'filter.edit-key-filter') | translate}}</h2>
<span fxFlex></span>

21
ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="numericFilterPredicateFormGroup">
<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="numericFilterPredicateFormGroup">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="40" class="mat-block">
<mat-label></mat-label>
<mat-select required formControlName="operation" placeholder="{{'filter.operation.operation' | translate}}">
@ -24,19 +24,8 @@
</mat-option>
</mat-select>
</mat-form-field>
<div fxFlex="60" fxLayout="column" [ngSwitch]="valueType">
<ng-template [ngSwitchCase]="valueTypeEnum.NUMERIC">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
<mat-label></mat-label>
<input required type="number" matInput formControlName="value"
placeholder="{{'filter.value' | translate}}">
</mat-form-field>
</ng-template>
<ng-template [ngSwitchCase]="valueTypeEnum.DATE_TIME">
<tb-datetime fxFlex formControlName="value"
dateText="filter.date"
timeText="filter.time"
required [showLabel]="false"></tb-datetime>
</ng-template>
</div>
<tb-filter-predicate-value fxFlex="60"
[valueType]="valueType"
formControlName="value">
</tb-filter-predicate-value>
</div>

13
ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts

@ -18,9 +18,11 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {
EntityKeyValueType,
FilterPredicateType, NumericFilterPredicate, NumericOperation, numericOperationTranslationMap,
FilterPredicateType,
NumericFilterPredicate,
NumericOperation,
numericOperationTranslationMap,
} from '@shared/models/query/query.models';
import { isDefined } from '@core/utils';
@Component({
selector: 'tb-numeric-filter-predicate',
@ -56,7 +58,7 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On
ngOnInit(): void {
this.numericFilterPredicateFormGroup = this.fb.group({
operation: [NumericOperation.EQUAL, [Validators.required]],
value: [0, [Validators.required]]
value: [null, [Validators.required]]
});
this.numericFilterPredicateFormGroup.valueChanges.subscribe(() => {
this.updateModel();
@ -81,16 +83,13 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On
writeValue(predicate: NumericFilterPredicate): void {
this.numericFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false});
this.numericFilterPredicateFormGroup.get('value').patchValue(isDefined(predicate.value) ? predicate.value : 0, {emitEvent: false});
this.numericFilterPredicateFormGroup.get('value').patchValue(predicate.value, {emitEvent: false});
}
private updateModel() {
let predicate: NumericFilterPredicate = null;
if (this.numericFilterPredicateFormGroup.valid) {
predicate = this.numericFilterPredicateFormGroup.getRawValue();
if (!predicate.value) {
predicate.value = 0;
}
predicate.type = FilterPredicateType.NUMERIC;
}
this.propagateChange(predicate);

10
ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="stringFilterPredicateFormGroup">
<div fxFlex fxLayout="row" fxLayoutAlign="start start" fxLayoutGap="8px" [formGroup]="stringFilterPredicateFormGroup">
<div fxFlex="40" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex class="mat-block">
<mat-label></mat-label>
@ -28,8 +28,8 @@
<mat-checkbox fxLayout="row" fxLayoutAlign="center" formControlName="ignoreCase" style="min-width: 70px;">
</mat-checkbox>
</div>
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="60" class="mat-block">
<mat-label></mat-label>
<input matInput formControlName="value" placeholder="{{'filter.value' | translate}}">
</mat-form-field>
<tb-filter-predicate-value fxFlex="60"
[valueType]="valueTypeEnum.STRING"
formControlName="value">
</tb-filter-predicate-value>
</div>

10
ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts

@ -17,6 +17,7 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {
EntityKeyValueType,
FilterPredicateType,
StringFilterPredicate,
StringOperation,
@ -39,6 +40,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
@Input() disabled: boolean;
valueTypeEnum = EntityKeyValueType;
stringFilterPredicateFormGroup: FormGroup;
stringOperations = Object.keys(StringOperation);
@ -53,7 +56,7 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
ngOnInit(): void {
this.stringFilterPredicateFormGroup = this.fb.group({
operation: [StringOperation.STARTS_WITH, [Validators.required]],
value: [''],
value: [null, [Validators.required]],
ignoreCase: [false]
});
this.stringFilterPredicateFormGroup.valueChanges.subscribe(() => {
@ -79,7 +82,7 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
writeValue(predicate: StringFilterPredicate): void {
this.stringFilterPredicateFormGroup.get('operation').patchValue(predicate.operation, {emitEvent: false});
this.stringFilterPredicateFormGroup.get('value').patchValue(predicate.value ? predicate.value : '', {emitEvent: false});
this.stringFilterPredicateFormGroup.get('value').patchValue(predicate.value, {emitEvent: false});
this.stringFilterPredicateFormGroup.get('ignoreCase').patchValue(predicate.ignoreCase, {emitEvent: false});
}
@ -87,9 +90,6 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
let predicate: StringFilterPredicate = null;
if (this.stringFilterPredicateFormGroup.valid) {
predicate = this.stringFilterPredicateFormGroup.getRawValue();
if (!predicate.value) {
predicate.value = '';
}
predicate.type = FilterPredicateType.STRING;
}
this.propagateChange(predicate);

11
ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.ts

@ -33,10 +33,11 @@ import { DialogComponent } from '@app/shared/components/dialog.component';
import { TranslateService } from '@ngx-translate/core';
import {
EntityKeyValueType,
Filter,
Filter, FilterPredicateValue,
filterToUserFilterInfoList,
UserFilterInputInfo
} from '@shared/models/query/query.models';
import { isDefinedAndNotNull } from '@core/utils';
export interface UserFilterDialogData {
filter: Filter;
@ -81,15 +82,17 @@ export class UserFilterDialogComponent extends DialogComponent<UserFilterDialogC
}
private createUserInputFormControl(userInput: UserFilterInputInfo): AbstractControl {
const predicateValue: FilterPredicateValue<string | number | boolean> = (userInput.info.keyFilterPredicate as any).value;
const value = isDefinedAndNotNull(predicateValue.userValue) ? predicateValue.userValue : predicateValue.defaultValue;
const userInputControl = this.fb.group({
label: [userInput.label],
valueType: [userInput.valueType],
value: [(userInput.info.keyFilterPredicate as any).value,
value: [value,
userInput.valueType === EntityKeyValueType.NUMERIC ||
userInput.valueType === EntityKeyValueType.DATE_TIME ? [Validators.required] : []]
});
userInputControl.get('value').valueChanges.subscribe(value => {
(userInput.info.keyFilterPredicate as any).value = value;
userInputControl.get('value').valueChanges.subscribe(userValue => {
(userInput.info.keyFilterPredicate as any).value.userValue = userValue;
});
return userInputControl;
}

4
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -83,6 +83,7 @@ import { FiltersEditPanelComponent } from '@home/components/filter/filters-edit-
import { UserFilterDialogComponent } from '@home/components/filter/user-filter-dialog.component';
import { FilterUserInfoComponent } from './filter/filter-user-info.component';
import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.component';
import { FilterPredicateValueComponent } from './filter/filter-predicate-value.component';
@NgModule({
declarations:
@ -148,7 +149,8 @@ import { FilterUserInfoDialogComponent } from './filter/filter-user-info-dialog.
FiltersEditPanelComponent,
UserFilterDialogComponent,
FilterUserInfoComponent,
FilterUserInfoDialogComponent
FilterUserInfoDialogComponent,
FilterPredicateValueComponent
],
imports: [
CommonModule,

43
ui-ngx/src/app/shared/models/query/query.models.ts

@ -138,16 +138,22 @@ export function createDefaultFilterPredicate(valueType: EntityKeyValueType, comp
switch (predicate.type) {
case FilterPredicateType.STRING:
predicate.operation = StringOperation.STARTS_WITH;
predicate.value = '';
predicate.value = {
defaultValue: ''
};
predicate.ignoreCase = false;
break;
case FilterPredicateType.NUMERIC:
predicate.operation = NumericOperation.EQUAL;
predicate.value = valueType === EntityKeyValueType.DATE_TIME ? Date.now() : 0;
predicate.value = {
defaultValue: valueType === EntityKeyValueType.DATE_TIME ? Date.now() : 0
};
break;
case FilterPredicateType.BOOLEAN:
predicate.operation = BooleanOperation.EQUAL;
predicate.value = false;
predicate.value = {
defaultValue: false
};
break;
case FilterPredicateType.COMPLEX:
predicate.operation = ComplexOperation.AND;
@ -228,23 +234,48 @@ export const complexOperationTranslationMap = new Map<ComplexOperation, string>(
]
);
export enum DynamicValueSourceType {
CURRENT_TENANT = 'CURRENT_TENANT',
CURRENT_CUSTOMER = 'CURRENT_CUSTOMER',
CURRENT_USER = 'CURRENT_USER'
}
export const dynamicValueSourceTypeTranslationMap = new Map<DynamicValueSourceType, string>(
[
[DynamicValueSourceType.CURRENT_TENANT, 'filter.current-tenant'],
[DynamicValueSourceType.CURRENT_CUSTOMER, 'filter.current-customer'],
[DynamicValueSourceType.CURRENT_USER, 'filter.current-user']
]
);
export interface DynamicValue<T> {
sourceType: DynamicValueSourceType;
sourceAttribute: string;
}
export interface FilterPredicateValue<T> {
defaultValue: T;
userValue?: T;
dynamicValue?: DynamicValue<T>;
}
export interface StringFilterPredicate {
type: FilterPredicateType.STRING,
operation: StringOperation;
value: string;
value: FilterPredicateValue<string>;
ignoreCase: boolean;
}
export interface NumericFilterPredicate {
type: FilterPredicateType.NUMERIC,
operation: NumericOperation;
value: number;
value: FilterPredicateValue<number>;
}
export interface BooleanFilterPredicate {
type: FilterPredicateType.BOOLEAN,
operation: BooleanOperation;
value: boolean;
value: FilterPredicateValue<boolean>;
}
export interface BaseComplexFilterPredicate<T extends KeyFilterPredicate | KeyFilterPredicateInfo> {

11
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1227,7 +1227,16 @@
"remove-key-filter": "Remove key filter",
"edit-key-filter": "Edit key filter",
"date": "Date",
"time": "Time"
"time": "Time",
"current-tenant": "Current tenant",
"current-customer": "Current customer",
"current-user": "Current user",
"default-value": "Default value",
"dynamic-source-type": "Dynamic source type",
"no-dynamic-value": "No dynamic value",
"source-attribute": "Source attribute",
"switch-to-dynamic-value": "Switch to dynamic value",
"switch-to-default-value": "Switch to default value"
},
"fullscreen": {
"expand": "Expand to fullscreen",

Loading…
Cancel
Save