UI: add id to queues and fixed bugs

This commit is contained in:
fe-dev 2022-05-25 18:45:00 +03:00
parent b6cbe0ca77
commit d99d4ebb0d
14 changed files with 312 additions and 208 deletions

View File

@ -16,33 +16,16 @@
--> -->
<div fxLayout="column"> <div fxLayout="column">
<div class="tb-tenant-profile-queues"> <div class="tb-tenant-profile-queues"
<mat-accordion multi> *ngFor="let queuesControl of queuesFormArray.controls; trackBy: trackByQueue;
<mat-expansion-panel fxFlex expanded let $index = index; last as isLast;"
*ngFor="let queuesControl of queuesFormArray.controls; trackBy: trackByQueue; [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
let $index = index; last as isLast;"> <tb-queue-form [formControl]="queuesControl"
<mat-expansion-panel-header> (removeQueue)="removeQueue($index)"
<div fxFlex fxLayout="row" fxLayoutAlign="start center"> [mainQueue]="$index === 0"
<mat-panel-title> [expanded]="$index === 0"
{{ getName(queuesControl.value.name) }} [newQueue]="newQueue">
</mat-panel-title> </tb-queue-form>
<span fxFlex></span>
<button *ngIf="!($index === 0) && !this.disabled" mat-icon-button style="min-width: 40px;"
type="button"
(click)="removeQueue($index)"
matTooltip="{{ 'action.remove' | translate }}"
matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
</div>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-queue-form [formControl]="queuesControl"
[newQueue]="newQueue">
</tb-queue-form>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
</div> </div>
<div *ngIf="!queuesFormArray.controls.length"> <div *ngIf="!queuesFormArray.controls.length">
<span translate fxLayoutAlign="center center" <span translate fxLayoutAlign="center center"

View File

@ -33,6 +33,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { QueueInfo } from '@shared/models/queue.models'; import { QueueInfo } from '@shared/models/queue.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { guid } from '@core/utils';
@Component({ @Component({
selector: 'tb-tenant-profile-queues', selector: 'tb-tenant-profile-queues',
@ -131,7 +132,10 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid
} }
public trackByQueue(index: number, queueControl: AbstractControl) { public trackByQueue(index: number, queueControl: AbstractControl) {
return queueControl; if (queueControl) {
return queueControl.value.id;
}
return null;
} }
public removeQueue(index: number) { public removeQueue(index: number) {
@ -140,6 +144,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid
public addQueue() { public addQueue() {
const queue = { const queue = {
id: guid(),
consumerPerPartition: false, consumerPerPartition: false,
name: '', name: '',
packProcessingTimeout: 2000, packProcessingTimeout: 2000,
@ -156,7 +161,10 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid
batchSize: 0, batchSize: 0,
type: '' type: ''
}, },
topic: '' topic: '',
additionalInfo: {
description: ''
}
}; };
this.newQueue = true; this.newQueue = true;
const queuesArray = this.tenantProfileQueuesFormGroup.get('queues') as FormArray; const queuesArray = this.tenantProfileQueuesFormGroup.get('queues') as FormArray;

View File

@ -14,12 +14,13 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { TenantProfileData } from '@shared/models/tenant.model'; import { TenantProfileData } from '@shared/models/tenant.model';
import { Subscription } from 'rxjs';
@Component({ @Component({
selector: 'tb-tenant-profile-data', selector: 'tb-tenant-profile-data',
@ -31,7 +32,7 @@ import { TenantProfileData } from '@shared/models/tenant.model';
multi: true multi: true
}] }]
}) })
export class TenantProfileDataComponent implements ControlValueAccessor, OnInit { export class TenantProfileDataComponent implements ControlValueAccessor, OnInit, OnDestroy {
tenantProfileDataFormGroup: FormGroup; tenantProfileDataFormGroup: FormGroup;
@ -47,6 +48,7 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit
@Input() @Input()
disabled: boolean; disabled: boolean;
private valueChange$: Subscription = null;
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
@ -64,11 +66,17 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit
this.tenantProfileDataFormGroup = this.fb.group({ this.tenantProfileDataFormGroup = this.fb.group({
configuration: [null, Validators.required] configuration: [null, Validators.required]
}); });
this.tenantProfileDataFormGroup.valueChanges.subscribe(() => { this.valueChange$ = this.tenantProfileDataFormGroup.valueChanges.subscribe(() => {
this.updateModel(); this.updateModel();
}); });
} }
ngOnDestroy() {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
}
setDisabledState(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled;
if (this.disabled) { if (this.disabled) {
@ -87,7 +95,7 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit
if (this.tenantProfileDataFormGroup.valid) { if (this.tenantProfileDataFormGroup.valid) {
tenantProfileData = this.tenantProfileDataFormGroup.getRawValue(); tenantProfileData = this.tenantProfileDataFormGroup.getRawValue();
} }
this.propagateChange(tenantProfileData); this.propagateChange(tenantProfileData.configuration);
} }
} }

View File

@ -35,6 +35,10 @@
width: fit-content; width: fit-content;
} }
} }
.mat-expansion-panel-header {
height: 48px;
}
.expansion-panel-block { .expansion-panel-block {
padding-bottom: 16px; padding-bottom: 16px;
} }

View File

@ -23,6 +23,7 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { EntityComponent } from '../entity/entity.component'; import { EntityComponent } from '../entity/entity.component';
import { guid } from '@core/utils';
@Component({ @Component({
selector: 'tb-tenant-profile', selector: 'tb-tenant-profile',
@ -54,6 +55,7 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
buildForm(entity: TenantProfile): FormGroup { buildForm(entity: TenantProfile): FormGroup {
const mainQueue = [ const mainQueue = [
{ {
id: guid(),
consumerPerPartition: true, consumerPerPartition: true,
name: 'Main', name: 'Main',
packProcessingTimeout: 2000, packProcessingTimeout: 2000,
@ -70,7 +72,10 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
batchSize: 1000, batchSize: 1000,
type: 'BURST' type: 'BURST'
}, },
topic: 'tb_rule_engine.main' topic: 'tb_rule_engine.main',
additionalInfo: {
description: ''
}
} }
]; ];
const formGroup = this.fb.group( const formGroup = this.fb.group(

View File

@ -15,161 +15,182 @@
limitations under the License. limitations under the License.
--> -->
<mat-expansion-panel fxFlex [(expanded)]="expanded">
<form [formGroup]="queueFormGroup" fxLayout="column" fxLayoutGap="0.5em"> <mat-expansion-panel-header>
<mat-form-field class="mat-block"> <div fxFlex fxLayout="row" fxLayoutAlign="start center">
<mat-label translate>admin.queue-name</mat-label> <mat-panel-title>
<input matInput formControlName="name" required> {{ queueTitle }}
<mat-error *ngIf="queueFormGroup.get('name').hasError('required')"> </mat-panel-title>
{{ 'queue.name-required' | translate }} <span fxFlex></span>
</mat-error> <button *ngIf="!mainQueue && !disabled" mat-icon-button style="min-width: 40px;"
</mat-form-field> type="button"
<mat-form-field class="mat-block"> (click)="removeQueue.emit()"
<mat-label translate>queue.poll-interval</mat-label> matTooltip="{{ 'action.remove' | translate }}"
<input type="number" matInput formControlName="pollInterval" required> matTooltipPosition="above">
<mat-error *ngIf="queueFormGroup.get('pollInterval').hasError('required')"> <mat-icon>delete</mat-icon>
{{ 'queue.poll-interval-required' | translate }} </button>
</mat-error> </div>
<mat-error *ngIf="queueFormGroup.get('pollInterval').hasError('min') && </mat-expansion-panel-header>
!queueFormGroup.get('pollInterval').hasError('required')"> <ng-template matExpansionPanelContent>
{{ 'queue.poll-interval-min-value' | translate }} <form [formGroup]="queueFormGroup" fxLayout="column" fxLayoutGap="0.5em">
</mat-error> <mat-form-field class="mat-block">
</mat-form-field> <mat-label translate>admin.queue-name</mat-label>
<mat-form-field class="mat-block"> <input matInput formControlName="name" required>
<mat-label translate>queue.partitions</mat-label> <mat-error *ngIf="queueFormGroup.get('name').hasError('required')">
<input type="number" matInput formControlName="partitions" required> {{ 'queue.name-required' | translate }}
<mat-error *ngIf="queueFormGroup.get('partitions').hasError('required')"> </mat-error>
{{ 'queue.partitions-required' | translate }} <mat-error *ngIf="queueFormGroup.get('name').hasError('unique')">
</mat-error> {{ 'queue.name-unique' | translate }}
<mat-error *ngIf="queueFormGroup.get('partitions').hasError('min') && </mat-error>
!queueFormGroup.get('partitions').hasError('required')"> </mat-form-field>
{{ 'queue.partitions-min-value' | translate }} <mat-form-field class="mat-block">
</mat-error> <mat-label translate>queue.poll-interval</mat-label>
</mat-form-field> <input type="number" matInput formControlName="pollInterval" required>
<mat-checkbox class="hinted-checkbox" formControlName="consumerPerPartition"> <mat-error *ngIf="queueFormGroup.get('pollInterval').hasError('required')">
<div>{{ 'queue.consumer-per-partition' | translate }}</div> {{ 'queue.poll-interval-required' | translate }}
<div class="tb-hint">{{'queue.consumer-per-partition-hint' | translate}}</div> </mat-error>
</mat-checkbox> <mat-error *ngIf="queueFormGroup.get('pollInterval').hasError('min') &&
<mat-form-field class="mat-block"> !queueFormGroup.get('pollInterval').hasError('required')">
<mat-label translate>queue.processing-timeout</mat-label> {{ 'queue.poll-interval-min-value' | translate }}
<input type="number" matInput formControlName="packProcessingTimeout" required> </mat-error>
<mat-error *ngIf="queueFormGroup.get('packProcessingTimeout').hasError('required')"> </mat-form-field>
{{ 'queue.pack-processing-timeout-required' | translate }} <mat-form-field class="mat-block">
</mat-error> <mat-label translate>queue.partitions</mat-label>
<mat-error *ngIf="queueFormGroup.get('packProcessingTimeout').hasError('min') && <input type="number" matInput formControlName="partitions" required>
!queueFormGroup.get('packProcessingTimeout').hasError('required')"> <mat-error *ngIf="queueFormGroup.get('partitions').hasError('required')">
{{ 'queue.pack-processing-timeout-min-value' | translate }} {{ 'queue.partitions-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> <mat-error *ngIf="queueFormGroup.get('partitions').hasError('min') &&
<mat-accordion class="queue-strategy" [multi]="true"> !queueFormGroup.get('partitions').hasError('required')">
<mat-expansion-panel> {{ 'queue.partitions-min-value' | translate }}
<mat-expansion-panel-header> </mat-error>
<mat-panel-title translate> </mat-form-field>
queue.submit-strategy <mat-checkbox class="hinted-checkbox" formControlName="consumerPerPartition">
</mat-panel-title> <div>{{ 'queue.consumer-per-partition' | translate }}</div>
</mat-expansion-panel-header> <div class="tb-hint">{{'queue.consumer-per-partition-hint' | translate}}</div>
<ng-template matExpansionPanelContent> </mat-checkbox>
<div formGroupName="submitStrategy"> <mat-form-field class="mat-block">
<mat-form-field class="mat-block"> <mat-label translate>queue.processing-timeout</mat-label>
<mat-label translate>queue.submit-strategy</mat-label> <input type="number" matInput formControlName="packProcessingTimeout" required>
<mat-select formControlName="type" required> <mat-error *ngIf="queueFormGroup.get('packProcessingTimeout').hasError('required')">
<mat-option *ngFor="let strategy of submitStrategies" [value]="strategy"> {{ 'queue.pack-processing-timeout-required' | translate }}
{{ strategy }} </mat-error>
</mat-option> <mat-error *ngIf="queueFormGroup.get('packProcessingTimeout').hasError('min') &&
</mat-select> !queueFormGroup.get('packProcessingTimeout').hasError('required')">
<mat-error *ngIf="queueFormGroup.get('submitStrategy.type').hasError('required')"> {{ 'queue.pack-processing-timeout-min-value' | translate }}
{{ 'queue.submit-strategy-type-required' | translate }} </mat-error>
</mat-error> </mat-form-field>
</mat-form-field> <mat-accordion class="queue-strategy" [multi]="true">
<mat-form-field class="mat-block" *ngIf="hideBatchSize"> <mat-expansion-panel [expanded]="false">
<mat-label translate>queue.batch-size</mat-label> <mat-expansion-panel-header>
<input type="number" matInput formControlName="batchSize" required> <mat-panel-title translate>
<mat-error *ngIf="queueFormGroup.get('submitStrategy.batchSize').hasError('required')"> queue.submit-strategy
{{ 'queue.batch-size-required' | translate }} </mat-panel-title>
</mat-error> </mat-expansion-panel-header>
<mat-error *ngIf="queueFormGroup.get('submitStrategy.batchSize').hasError('min') && <ng-template matExpansionPanelContent>
!queueFormGroup.get('submitStrategy.batchSize').hasError('required')"> <div formGroupName="submitStrategy">
{{ 'queue.batch-size-min-value' | translate }} <mat-form-field class="mat-block">
</mat-error> <mat-label translate>queue.submit-strategy</mat-label>
</mat-form-field> <mat-select formControlName="type" required>
</div> <mat-option *ngFor="let strategy of submitStrategies" [value]="strategy">
</ng-template> {{ strategy }}
</mat-expansion-panel> </mat-option>
<mat-expansion-panel> </mat-select>
<mat-expansion-panel-header> <mat-error *ngIf="queueFormGroup.get('submitStrategy.type').hasError('required')">
<mat-panel-title translate> {{ 'queue.submit-strategy-type-required' | translate }}
queue.processing-strategy </mat-error>
</mat-panel-title> </mat-form-field>
</mat-expansion-panel-header> <mat-form-field class="mat-block" *ngIf="hideBatchSize">
<ng-template matExpansionPanelContent> <mat-label translate>queue.batch-size</mat-label>
<div formGroupName="processingStrategy"> <input type="number" matInput formControlName="batchSize" required>
<mat-form-field class="mat-block"> <mat-error *ngIf="queueFormGroup.get('submitStrategy.batchSize').hasError('required')">
<mat-label translate>queue.processing-strategy</mat-label> {{ 'queue.batch-size-required' | translate }}
<mat-select formControlName="type" required> </mat-error>
<mat-option *ngFor="let strategy of processingStrategies" [value]="strategy"> <mat-error *ngIf="queueFormGroup.get('submitStrategy.batchSize').hasError('min') &&
{{ strategy }} !queueFormGroup.get('submitStrategy.batchSize').hasError('required')">
</mat-option> {{ 'queue.batch-size-min-value' | translate }}
</mat-select> </mat-error>
<mat-error *ngIf="queueFormGroup.get('processingStrategy.type').hasError('required')"> </mat-form-field>
{{ 'queue.processing-strategy-type-required' | translate }} </div>
</mat-error> </ng-template>
</mat-form-field> </mat-expansion-panel>
<mat-form-field class="mat-block"> <mat-expansion-panel [expanded]="false">
<mat-label translate>queue.retries</mat-label> <mat-expansion-panel-header>
<input type="number" matInput formControlName="retries" required> <mat-panel-title translate>
<mat-error *ngIf="queueFormGroup.get('processingStrategy.retries').hasError('required')"> queue.processing-strategy
{{ 'queue.retries-required' | translate }} </mat-panel-title>
</mat-error> </mat-expansion-panel-header>
<mat-error *ngIf="queueFormGroup.get('processingStrategy.retries').hasError('min') && <ng-template matExpansionPanelContent>
!queueFormGroup.get('processingStrategy.retries').hasError('required')"> <div formGroupName="processingStrategy">
{{ 'queue.retries-min-value' | translate }} <mat-form-field class="mat-block">
</mat-error> <mat-label translate>queue.processing-strategy</mat-label>
</mat-form-field> <mat-select formControlName="type" required>
<mat-form-field class="mat-block"> <mat-option *ngFor="let strategy of processingStrategies" [value]="strategy">
<mat-label translate>queue.failure-percentage</mat-label> {{ strategy }}
<input type="number" matInput formControlName="failurePercentage" required> </mat-option>
<mat-error *ngIf="queueFormGroup.get('processingStrategy.failurePercentage').hasError('required')"> </mat-select>
{{ 'queue.failure-percentage-required' | translate }} <mat-error *ngIf="queueFormGroup.get('processingStrategy.type').hasError('required')">
</mat-error> {{ 'queue.processing-strategy-type-required' | translate }}
<mat-error *ngIf="queueFormGroup.get('processingStrategy.failurePercentage').hasError('min') && </mat-error>
!queueFormGroup.get('processingStrategy.failurePercentage').hasError('required') && </mat-form-field>
!queueFormGroup.get('processingStrategy.failurePercentage').hasError('max')"> <mat-form-field class="mat-block">
{{ 'queue.failure-percentage-min-value' | translate }} <mat-label translate>queue.retries</mat-label>
</mat-error> <input type="number" matInput formControlName="retries" required>
<mat-error *ngIf="queueFormGroup.get('processingStrategy.failurePercentage').hasError('max') && <mat-error *ngIf="queueFormGroup.get('processingStrategy.retries').hasError('required')">
!queueFormGroup.get('processingStrategy.failurePercentage').hasError('required') && {{ 'queue.retries-required' | translate }}
!queueFormGroup.get('processingStrategy.failurePercentage').hasError('min')"> </mat-error>
{{ 'queue.failure-percentage-max-value' | translate }} <mat-error *ngIf="queueFormGroup.get('processingStrategy.retries').hasError('min') &&
</mat-error> !queueFormGroup.get('processingStrategy.retries').hasError('required')">
</mat-form-field> {{ 'queue.retries-min-value' | translate }}
<mat-form-field class="mat-block"> </mat-error>
<mat-label translate>queue.pause-between-retries</mat-label> </mat-form-field>
<input type="number" matInput formControlName="pauseBetweenRetries" required> <mat-form-field class="mat-block">
<mat-error *ngIf="queueFormGroup.get('processingStrategy.pauseBetweenRetries').hasError('required')"> <mat-label translate>queue.failure-percentage</mat-label>
{{ 'queue.pause-between-retries-required' | translate }} <input type="number" matInput formControlName="failurePercentage" required>
</mat-error> <mat-error *ngIf="queueFormGroup.get('processingStrategy.failurePercentage').hasError('required')">
<mat-error *ngIf="queueFormGroup.get('processingStrategy.pauseBetweenRetries').hasError('min') && {{ 'queue.failure-percentage-required' | translate }}
!queueFormGroup.get('processingStrategy.pauseBetweenRetries').hasError('required')"> </mat-error>
{{ 'queue.pause-between-retries-min-value' | translate }} <mat-error *ngIf="queueFormGroup.get('processingStrategy.failurePercentage').hasError('min') &&
</mat-error> !queueFormGroup.get('processingStrategy.failurePercentage').hasError('required') &&
</mat-form-field> !queueFormGroup.get('processingStrategy.failurePercentage').hasError('max')">
<mat-form-field class="mat-block"> {{ 'queue.failure-percentage-min-value' | translate }}
<mat-label translate>queue.max-pause-between-retries</mat-label> </mat-error>
<input type="number" matInput formControlName="maxPauseBetweenRetries" required> <mat-error *ngIf="queueFormGroup.get('processingStrategy.failurePercentage').hasError('max') &&
<mat-error *ngIf="queueFormGroup.get('processingStrategy.maxPauseBetweenRetries').hasError('required')"> !queueFormGroup.get('processingStrategy.failurePercentage').hasError('required') &&
{{ 'queue.max-pause-between-retries-required' | translate }} !queueFormGroup.get('processingStrategy.failurePercentage').hasError('min')">
</mat-error> {{ 'queue.failure-percentage-max-value' | translate }}
<mat-error *ngIf="queueFormGroup.get('processingStrategy.maxPauseBetweenRetries').hasError('min') && </mat-error>
!queueFormGroup.get('processingStrategy.maxPauseBetweenRetries').hasError('required')"> </mat-form-field>
{{ 'queue.max-pause-between-retries-min-value' | translate }} <mat-form-field class="mat-block">
</mat-error> <mat-label translate>queue.pause-between-retries</mat-label>
</mat-form-field> <input type="number" matInput formControlName="pauseBetweenRetries" required>
</div> <mat-error *ngIf="queueFormGroup.get('processingStrategy.pauseBetweenRetries').hasError('required')">
</ng-template> {{ 'queue.pause-between-retries-required' | translate }}
</mat-expansion-panel> </mat-error>
</mat-accordion> <mat-error *ngIf="queueFormGroup.get('processingStrategy.pauseBetweenRetries').hasError('min') &&
<mat-form-field class="mat-block" formGroupName="additionalInfo"> !queueFormGroup.get('processingStrategy.pauseBetweenRetries').hasError('required')">
<mat-label translate>queue.description</mat-label> {{ 'queue.pause-between-retries-min-value' | translate }}
<textarea matInput formControlName="description" rows="2"></textarea> </mat-error>
</mat-form-field> </mat-form-field>
</form> <mat-form-field class="mat-block">
<mat-label translate>queue.max-pause-between-retries</mat-label>
<input type="number" matInput formControlName="maxPauseBetweenRetries" required>
<mat-error *ngIf="queueFormGroup.get('processingStrategy.maxPauseBetweenRetries').hasError('required')">
{{ 'queue.max-pause-between-retries-required' | translate }}
</mat-error>
<mat-error *ngIf="queueFormGroup.get('processingStrategy.maxPauseBetweenRetries').hasError('min') &&
!queueFormGroup.get('processingStrategy.maxPauseBetweenRetries').hasError('required')">
{{ 'queue.max-pause-between-retries-min-value' | translate }}
</mat-error>
</mat-form-field>
</div>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
<mat-form-field class="mat-block" formGroupName="additionalInfo">
<mat-label translate>queue.description</mat-label>
<textarea matInput formControlName="description" rows="2"></textarea>
</mat-form-field>
</form>
</ng-template>
</mat-expansion-panel>

View File

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
FormBuilder, FormBuilder,
@ -29,6 +29,7 @@ import { MatDialog } from '@angular/material/dialog';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { QueueInfo, QueueProcessingStrategyTypes, QueueSubmitStrategyTypes } from '@shared/models/queue.models'; import { QueueInfo, QueueProcessingStrategyTypes, QueueSubmitStrategyTypes } from '@shared/models/queue.models';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { Subscription } from 'rxjs';
@Component({ @Component({
selector: 'tb-queue-form', selector: 'tb-queue-form',
@ -47,7 +48,7 @@ import { isDefinedAndNotNull } from '@core/utils';
} }
] ]
}) })
export class QueueFormComponent implements ControlValueAccessor, OnInit, Validator { export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestroy, Validator {
@Input() @Input()
disabled: boolean; disabled: boolean;
@ -55,20 +56,28 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat
@Input() @Input()
newQueue = false; newQueue = false;
@Input()
mainQueue = false;
@Input() @Input()
systemQueue = false; systemQueue = false;
private modelValue: QueueInfo; @Input()
expanded = false;
@Output()
removeQueue = new EventEmitter();
queueFormGroup: FormGroup; queueFormGroup: FormGroup;
submitStrategies: string[] = []; submitStrategies: string[] = [];
processingStrategies: string[] = []; processingStrategies: string[] = [];
queueTitle = '';
hideBatchSize = false; hideBatchSize = false;
private modelValue: QueueInfo;
private propagateChange = null; private propagateChange = null;
private propagateChangePending = false; private propagateChangePending = false;
private valueChange$: Subscription = null;
constructor(private dialog: MatDialog, constructor(private dialog: MatDialog,
private utils: UtilsService, private utils: UtilsService,
@ -114,10 +123,13 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat
description: [''] description: ['']
}) })
}); });
this.queueFormGroup.valueChanges.subscribe(() => { this.valueChange$ = this.queueFormGroup.valueChanges.subscribe(() => {
this.updateModel(); this.updateModel();
}); });
this.queueFormGroup.get('name').valueChanges.subscribe((value) => this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`})); this.queueFormGroup.get('name').valueChanges.subscribe((value) => {
this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`});
this.queueTitle = this.utils.customTranslation(value, value);
});
this.queueFormGroup.get('submitStrategy').get('type').valueChanges.subscribe(() => { this.queueFormGroup.get('submitStrategy').get('type').valueChanges.subscribe(() => {
this.submitStrategyTypeChanged(); this.submitStrategyTypeChanged();
}); });
@ -128,6 +140,13 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat
} }
} }
ngOnDestroy() {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
this.valueChange$ = null;
}
}
setDisabledState(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled;
if (this.disabled) { if (this.disabled) {
@ -141,6 +160,10 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, Validat
writeValue(value: QueueInfo): void { writeValue(value: QueueInfo): void {
this.propagateChangePending = false; this.propagateChangePending = false;
this.modelValue = value; this.modelValue = value;
if (!this.modelValue.name) {
this.expanded = true;
}
this.queueTitle = this.utils.customTranslation(value.name, value.name);
if (isDefinedAndNotNull(this.modelValue)) { if (isDefinedAndNotNull(this.modelValue)) {
this.queueFormGroup.patchValue(this.modelValue, {emitEvent: false}); this.queueFormGroup.patchValue(this.modelValue, {emitEvent: false});
} }

View File

@ -33,6 +33,8 @@ import { TenantProfileComponent } from '../../components/profile/tenant-profile.
import { TenantProfileTabsComponent } from './tenant-profile-tabs.component'; import { TenantProfileTabsComponent } from './tenant-profile-tabs.component';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { ImportExportService } from '@home/components/import-export/import-export.service'; import { ImportExportService } from '@home/components/import-export/import-export.service';
import { map } from 'rxjs/operators';
import { guid } from '@core/utils';
@Injectable() @Injectable()
export class TenantProfilesTableConfigResolver implements Resolve<EntityTableConfig<TenantProfile>> { export class TenantProfilesTableConfigResolver implements Resolve<EntityTableConfig<TenantProfile>> {
@ -84,7 +86,12 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
this.config.deleteEntitiesContent = () => this.translate.instant('tenant-profile.delete-tenant-profiles-text'); this.config.deleteEntitiesContent = () => this.translate.instant('tenant-profile.delete-tenant-profiles-text');
this.config.entitiesFetchFunction = pageLink => this.tenantProfileService.getTenantProfiles(pageLink); this.config.entitiesFetchFunction = pageLink => this.tenantProfileService.getTenantProfiles(pageLink);
this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id); this.config.loadEntity = id => this.tenantProfileService.getTenantProfile(id.id).pipe(
map(tenantProfile => ({
...tenantProfile,
profileData: {...tenantProfile.profileData, queueConfiguration: this.addId(tenantProfile.profileData.queueConfiguration)},
}))
);
this.config.saveEntity = tenantProfile => this.tenantProfileService.saveTenantProfile(tenantProfile); this.config.saveEntity = tenantProfile => this.tenantProfileService.saveTenantProfile(tenantProfile);
this.config.deleteEntity = id => this.tenantProfileService.deleteTenantProfile(id.id); this.config.deleteEntity = id => this.tenantProfileService.deleteTenantProfile(id.id);
this.config.onEntityAction = action => this.onTenantProfileAction(action); this.config.onEntityAction = action => this.onTenantProfileAction(action);
@ -93,6 +100,15 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
this.config.addActionDescriptors = this.configureAddActions(); this.config.addActionDescriptors = this.configureAddActions();
} }
addId(queues) {
const queuesWithId = [];
queues.forEach(value => {
value.id = guid();
queuesWithId.push(value);
});
return queuesWithId;
}
resolve(): EntityTableConfig<TenantProfile> { resolve(): EntityTableConfig<TenantProfile> {
this.config.tableTitle = this.translate.instant('tenant-profile.tenant-profiles'); this.config.tableTitle = this.translate.instant('tenant-profile.tenant-profiles');

View File

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<mat-form-field [formGroup]="selectQueueFormGroup" class="mat-block"> <mat-form-field [formGroup]="selectQueueFormGroup" class="mat-block autocomplete-queue">
<input matInput type="text" placeholder="{{ 'queue.queue-name' | translate }}" <input matInput type="text" placeholder="{{ 'queue.queue-name' | translate }}"
#queueInput #queueInput
formControlName="queueId" formControlName="queueId"
@ -30,10 +30,11 @@
</button> </button>
<mat-autocomplete class="tb-autocomplete" <mat-autocomplete class="tb-autocomplete"
#queueAutocomplete="matAutocomplete" #queueAutocomplete="matAutocomplete"
[displayWith]="displayQueueFn"> [displayWith]="displayQueueFn"
<mat-option *ngFor="let queue of filteredQueues | async" [value]="queue"> >
<mat-option *ngFor="let queue of filteredQueues | async" [value]="queue" class="queue-option">
<span [innerHTML]="queue.name | highlight:searchText"></span> <span [innerHTML]="queue.name | highlight:searchText"></span>
<small style="display: block;">{{getDescription(queue)}}</small> <small class="queue-option-description">{{getDescription(queue)}}</small>
</mat-option> </mat-option>
<mat-option *ngIf="!(filteredQueues | async)?.length" [value]="null" class="tb-not-found"> <mat-option *ngIf="!(filteredQueues | async)?.length" [value]="null" class="tb-not-found">
<div class="tb-not-found-content" (click)="$event.stopPropagation()"> <div class="tb-not-found-content" (click)="$event.stopPropagation()">

View File

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2022 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.
*/
::ng-deep {
.queue-option {
.mat-option-text {
display: inline;
}
.queue-option-description {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}

View File

@ -36,7 +36,7 @@ import { emptyPageData } from '@shared/models/page/page-data';
@Component({ @Component({
selector: 'tb-queue-autocomplete', selector: 'tb-queue-autocomplete',
templateUrl: './queue-autocomplete.component.html', templateUrl: './queue-autocomplete.component.html',
styleUrls: [], styleUrls: ['./queue-autocomplete.component.scss'],
providers: [{ providers: [{
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => QueueAutocompleteComponent), useExisting: forwardRef(() => QueueAutocompleteComponent),
@ -207,7 +207,10 @@ export class QueueAutocompleteComponent implements ControlValueAccessor, OnInit
getDescription(value) { getDescription(value) {
return value.additionalInfo?.description ? value.additionalInfo.description : return value.additionalInfo?.description ? value.additionalInfo.description :
`Submit Strategy: ${value.submitStrategy.type}, Processing Strategy: ${value.processingStrategy.type}`; this.translate.instant(
'queue.alt-description',
{submitStrategy: value.submitStrategy.type, processingStrategy: value.processingStrategy.type}
);
} }
clear() { clear() {

View File

@ -43,6 +43,7 @@ export enum QueueProcessingStrategyTypes {
} }
export interface QueueInfo extends BaseData<QueueId> { export interface QueueInfo extends BaseData<QueueId> {
generatedId?: string;
name: string; name: string;
packProcessingTimeout: number; packProcessingTimeout: number;
partitions: number; partitions: number;

View File

@ -98,7 +98,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
export interface TenantProfileData { export interface TenantProfileData {
configuration: TenantProfileConfiguration; configuration: TenantProfileConfiguration;
queueConfiguration?: QueueInfo; queueConfiguration?: Array<QueueInfo>;
} }
export interface TenantProfile extends BaseData<TenantProfileId> { export interface TenantProfile extends BaseData<TenantProfileId> {

View File

@ -2773,6 +2773,7 @@
"select-name": "Select queue name", "select-name": "Select queue name",
"name": "Name", "name": "Name",
"name-required": "Queue name is required!", "name-required": "Queue name is required!",
"name-unique": "Queue name is not unique!",
"queue-required": "Queue is required!", "queue-required": "Queue is required!",
"topic-required": "Queue topic is required!", "topic-required": "Queue topic is required!",
"poll-interval-required": "Poll interval is required!", "poll-interval-required": "Poll interval is required!",
@ -2819,7 +2820,8 @@
"delete": "Delete queue", "delete": "Delete queue",
"copyId": "Copy queue Id", "copyId": "Copy queue Id",
"idCopiedMessage": "Queue Id has been copied to clipboard", "idCopiedMessage": "Queue Id has been copied to clipboard",
"description": "Description" "description": "Description",
"alt-description": "Submit Strategy: {{submitStrategy}}, Processing Strategy: {{processingStrategy}}"
}, },
"tenant": { "tenant": {
"tenant": "Tenant", "tenant": "Tenant",