Browse Source

UI: Fixed cf relation aggregation key; Fixed minor bug in validation cf

pull/14276/head
Vladyslav_Prykhodko 11 months ago
parent
commit
1c344b4cc0
  1. 3
      ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts
  2. 29
      ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/related-aggregation-arguments-table.component.ts
  3. 11
      ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts
  4. 5
      ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts
  5. 5
      ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts
  6. 3
      ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.html
  7. 24
      ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.ts
  8. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts
  9. 2
      ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.ts
  10. 2
      ui-ngx/src/app/shared/components/time-unit-input.component.ts
  11. 11
      ui-ngx/src/app/shared/models/calculated-field.models.ts

3
ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts

@ -71,6 +71,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
@Input() hiddenEntityTypes = false;
@Input() defaultValueRequired = false;
@Input() hint: string;
@Input() predefinedEntityFilter: EntityFilter;
@Input() argumentEntityTypes = Object.values(ArgumentEntityType).filter(value => value !== ArgumentEntityType.RelationQuery) as ArgumentEntityType[];
@ViewChild('entityAutocomplete') entityAutocomplete: EntityAutocompleteComponent;
@ -222,6 +223,8 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
}
if (!onInit) {
this.argumentFormGroup.get('refEntityKey').get('key').setValue('');
} else {
entityFilter = this.predefinedEntityFilter;
}
this.entityFilter = entityFilter;
this.cd.markForCheck();

29
ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/related-aggregation-arguments-table.component.ts

@ -14,7 +14,15 @@
/// limitations under the License.
///
import { ChangeDetectorRef, Component, DestroyRef, forwardRef, Renderer2, ViewContainerRef, } from '@angular/core';
import {
ChangeDetectorRef,
Component,
DestroyRef,
forwardRef,
Input,
Renderer2,
ViewContainerRef,
} from '@angular/core';
import { FormBuilder, NG_VALIDATORS, NG_VALUE_ACCESSOR, } from '@angular/forms';
import { TbPopoverService } from '@shared/components/popover.service';
import { EntityService } from '@core/http/entity.service';
@ -23,7 +31,9 @@ import { AppState } from '@core/core.state';
import {
CalculatedFieldArgumentsTableComponent
} from '@home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component';
import { ArgumentEntityType } from '@shared/models/calculated-field.models';
import { ArgumentEntityType, RelationPathLevel } from '@shared/models/calculated-field.models';
import { AliasFilterType } from '@shared/models/alias.models';
import { EntityType } from '@shared/models/entity-type.models';
@Component({
selector: 'tb-related-aggregation-arguments-table',
@ -44,6 +54,21 @@ import { ArgumentEntityType } from '@shared/models/calculated-field.models';
})
export class RelatedAggregationArgumentsTableComponent extends CalculatedFieldArgumentsTableComponent {
@Input({required: true})
set relation(value: RelationPathLevel) {
this.panelAdditionalCtx.predefinedEntityFilter = {
type: AliasFilterType.relationsQuery,
rootStateEntity: false,
rootEntity: this.entityId,
direction: value.direction,
filters: [{
relationType: value.relationType,
entityTypes: [EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.TENANT]
}],
maxLevel: 1,
};
}
constructor(
protected fb: FormBuilder,
protected popoverService: TbPopoverService,

11
ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts

@ -30,7 +30,7 @@ import {
} from '@shared/models/calculated-field.models';
import { oneSpaceInsideRegex } from '@shared/models/regex.constants';
import { EntityType } from '@shared/models/entity-type.models';
import { switchMap } from 'rxjs/operators';
import { pairwise, switchMap } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { Observable } from 'rxjs';
@ -106,7 +106,7 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
onTestScript(): Observable<string> {
const calculatedFieldId = this.data.value?.id?.id;
if (calculatedFieldId) {
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId)
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true})
.pipe(
switchMap(event => {
const args = event?.arguments ? JSON.parse(event.arguments) : null;
@ -121,6 +121,7 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
private applyDialogData(): void {
const { configuration = {} as CalculatedFieldConfiguration, type = CalculatedFieldType.SIMPLE, debugSettings = { failuresEnabled: true, allEnabled: true }, ...value } = this.data.value ?? {};
this.fieldFormGroup.patchValue({ configuration, type, debugSettings, ...value }, {emitEvent: false});
setTimeout(() => this.fieldFormGroup.get('type').updateValueAndValidity({onlySelf: true}));
}
private observeIsLoading(): void {
@ -138,9 +139,11 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
private observeType(): void {
this.fieldFormGroup.get('type').valueChanges.pipe(
pairwise(),
takeUntilDestroyed(this.destroyRef)
).subscribe((type) => {
if (type !== CalculatedFieldType.SIMPLE && type !== CalculatedFieldType.SCRIPT) {
).subscribe(([prevType, nextType]) => {
if (![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(prevType) ||
![CalculatedFieldType.SIMPLE, CalculatedFieldType.SCRIPT].includes(nextType)) {
this.fieldFormGroup.get('configuration').setValue(({} as CalculatedFieldConfiguration), {emitEvent: false});
}
});

5
ui-ngx/src/app/modules/home/components/calculated-fields/components/geofencing-configuration/geofencing-configuration.component.ts

@ -31,6 +31,7 @@ import {
CalculatedFieldOutput,
CalculatedFieldType,
getCalculatedFieldCurrentEntityFilter,
notEmptyObjectValidator,
OutputType
} from '@shared/models/calculated-field.models';
import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry.models';
@ -76,7 +77,7 @@ export class GeofencingConfigurationComponent implements ControlValueAccessor, V
latitudeKeyName: [null, [Validators.required]],
longitudeKeyName: [null, [Validators.required]],
}),
zoneGroups: this.fb.control<Record<string, CalculatedFieldGeofencing>>({}),
zoneGroups: this.fb.control<Record<string, CalculatedFieldGeofencing>>({}, notEmptyObjectValidator()),
scheduledUpdateEnabled: [true],
scheduledUpdateInterval: [this.minAllowedScheduledUpdateIntervalInSecForCF],
output: this.fb.control<CalculatedFieldOutput>({scope: AttributeScope.SERVER_SCOPE, type: OutputType.Timeseries})
@ -114,7 +115,7 @@ export class GeofencingConfigurationComponent implements ControlValueAccessor, V
}
validate(): ValidationErrors | null {
return this.geofencingConfiguration.valid || this.geofencingConfiguration.status === "DISABLED" ? null : { geofencingConfigError: false };
return this.geofencingConfiguration.valid || this.geofencingConfiguration.disabled ? null : { geofencingConfigError: false };
}
writeValue(config: CalculatedFieldGeofencingConfiguration): void {

5
ui-ngx/src/app/modules/home/components/calculated-fields/components/propagation-configuration/propagation-configuration.component.ts

@ -33,6 +33,7 @@ import {
CalculatedFieldType,
getCalculatedFieldArgumentsEditorCompleter,
getCalculatedFieldArgumentsHighlights,
notEmptyObjectValidator,
OutputType,
PropagationDirectionTranslations,
PropagationWithExpression
@ -74,7 +75,7 @@ export class PropagationConfigurationComponent implements ControlValueAccessor,
testScript: () => Observable<string>;
propagateConfiguration = this.fb.group({
arguments: this.fb.control({}),
arguments: this.fb.control({}, notEmptyObjectValidator()),
applyExpressionToResolvedArguments: [false],
relation: this.fb.group({
direction: [EntitySearchDirection.TO, Validators.required],
@ -122,7 +123,7 @@ export class PropagationConfigurationComponent implements ControlValueAccessor,
}
validate(): ValidationErrors | null {
return this.propagateConfiguration.valid || this.propagateConfiguration.status === "DISABLED" ? null : {invalidPropagateConfig: false};
return this.propagateConfiguration.valid || this.propagateConfiguration.disabled ? null : {invalidPropagateConfig: false};
}
writeValue(value: PropagationWithExpression): void {

3
ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.html

@ -47,7 +47,8 @@
<tb-related-aggregation-arguments-table formControlName="arguments"
[entityId]="entityId"
[tenantId]="tenantId"
[entityName]="entityName"/>
[entityName]="entityName"
[relation]="relatedAggregationConfiguration.get('relation').value"/>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" tb-hint-tooltip-icon="{{ 'calculated-fields.hint.metrics' | translate }}">

24
ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.ts

@ -32,6 +32,7 @@ import {
CalculatedFieldType,
getCalculatedFieldArgumentsEditorCompleter,
getCalculatedFieldArgumentsHighlights,
notEmptyObjectValidator,
OutputType,
PropagationDirectionTranslations
} from '@shared/models/calculated-field.models';
@ -72,14 +73,21 @@ export class RelatedEntitiesAggregationComponentComponent implements ControlValu
@Input({required: true})
entityName: string;
readonly ScriptLanguage = ScriptLanguage;
readonly CalculatedFieldType = CalculatedFieldType;
readonly OutputType = OutputType;
readonly Directions = Object.values(EntitySearchDirection) as Array<EntitySearchDirection>;
readonly PropagationDirectionTranslations = PropagationDirectionTranslations;
readonly minAllowedDeduplicationIntervalInSecForCF = getCurrentAuthState(this.store).minAllowedDeduplicationIntervalInSecForCF;
relatedAggregationConfiguration = this.fb.group({
relation: this.fb.group({
direction: [EntitySearchDirection.FROM, Validators.required],
relationType: ['Contains', Validators.required],
}),
arguments: this.fb.control({}),
metrics: this.fb.control({}),
deduplicationIntervalInSec: [],
arguments: this.fb.control({}, notEmptyObjectValidator()),
metrics: this.fb.control({}, notEmptyObjectValidator()),
deduplicationIntervalInSec: [this.minAllowedDeduplicationIntervalInSecForCF],
output: this.fb.control<CalculatedFieldOutput>({
scope: AttributeScope.SERVER_SCOPE,
type: OutputType.Timeseries,
@ -87,14 +95,6 @@ export class RelatedEntitiesAggregationComponentComponent implements ControlValu
useLatestTs: [false]
});
readonly ScriptLanguage = ScriptLanguage;
readonly CalculatedFieldType = CalculatedFieldType;
readonly OutputType = OutputType;
readonly Directions = Object.values(EntitySearchDirection) as Array<EntitySearchDirection>;
readonly PropagationDirectionTranslations = PropagationDirectionTranslations;
readonly minAllowedDeduplicationIntervalInSecForCF = getCurrentAuthState(this.store).minAllowedDeduplicationIntervalInSecForCF;
arguments$ = this.relatedAggregationConfiguration.get('arguments').valueChanges.pipe(
map(argumentsObj => Object.keys(argumentsObj))
);
@ -120,7 +120,7 @@ export class RelatedEntitiesAggregationComponentComponent implements ControlValu
}
validate(): ValidationErrors | null {
return this.relatedAggregationConfiguration.valid || this.relatedAggregationConfiguration.status === "DISABLED" ? null : {invalidPropagateConfig: false};
return this.relatedAggregationConfiguration.valid || this.relatedAggregationConfiguration.disabled ? null : {invalidPropagateConfig: false};
}
writeValue(value: CalculatedFieldRelatedAggregationConfiguration): void {

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/simple-configuration/simple-configuration.component.ts

@ -140,7 +140,7 @@ export class SimpleConfigurationComponent implements ControlValueAccessor, Valid
}
validate(): ValidationErrors | null {
return this.simpleConfiguration.valid || this.simpleConfiguration.status === "DISABLED" ? null : {invalidSimpleConfig: false};
return this.simpleConfiguration.valid || this.simpleConfiguration.disabled ? null : {invalidSimpleConfig: false};
}
writeValue(value: SimpeConfiguration): void {

2
ui-ngx/src/app/shared/components/entity/entity-key-autocomplete.component.ts

@ -84,7 +84,7 @@ export class EntityKeyAutocompleteComponent implements ControlValueAccessor, Val
return this.cachedResult ? of(this.cachedResult) : this.entityService.findEntityKeysByQuery({
pageLink: { page: 0, pageSize: 100 },
entityFilter: this.entityFilter(),
}, this.dataKeyType() === DataKeyType.attribute, this.dataKeyType() === DataKeyType.timeseries, this.keyScopeType());
}, this.dataKeyType() === DataKeyType.attribute, this.dataKeyType() === DataKeyType.timeseries, this.keyScopeType(), {ignoreLoading: true});
}),
map(result => {
this.cachedResult = result;

2
ui-ngx/src/app/shared/components/time-unit-input.component.ts

@ -200,7 +200,7 @@ export class TimeUnitInputComponent implements ControlValueAccessor, Validator,
}
validate(): ValidationErrors | null {
return this.timeInputForm.valid ? null : {
return this.timeInputForm.disabled || this.timeInputForm.valid ? null : {
timeInput: false
};
}

11
ui-ngx/src/app/shared/models/calculated-field.models.ts

@ -30,6 +30,7 @@ import {
endGroupHighlightRule
} from '@shared/models/ace/ace.models';
import { EntitySearchDirection } from '@shared/models/relation.models';
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
interface BaseCalculatedField extends Omit<BaseData<CalculatedFieldId>, 'label'>, HasVersion, HasEntityDebugSettings, HasTenantId, ExportableEntity<CalculatedFieldId> {
entityId: EntityId;
@ -847,3 +848,13 @@ export const calculatedFieldDefaultScript =
'return {\n' +
' "temperatureC": (temperatureF - 32) / 1.8\n' +
'};'
export function notEmptyObjectValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (typeof value === 'object' && value !== null && Object.keys(value).length === 0) {
return {emptyObject: true};
}
return null;
};
}

Loading…
Cancel
Save