Browse Source

UI: add queues to tenant profile

pull/6134/head
fe-dev 4 years ago
parent
commit
aba3974a77
  1. 4
      ui-ngx/src/app/modules/common/modules-map.ts
  2. 10
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  3. 4
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  4. 194
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.html
  5. 22
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.scss
  6. 206
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.ts
  7. 42
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html
  8. 31
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.scss
  9. 178
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts
  10. 5
      ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html
  11. 2
      ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.ts
  12. 24
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html
  13. 10
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss
  14. 78
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts
  15. 204
      ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.html
  16. 43
      ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.scss
  17. 6
      ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.ts
  18. 7
      ui-ngx/src/app/shared/models/queue.models.ts
  19. 4
      ui-ngx/src/app/shared/models/tenant.model.ts
  20. 5
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
ui-ngx/src/app/modules/common/modules-map.ts

@ -287,6 +287,8 @@ import * as DisplayWidgetTypesPanelComponent from '@home/components/dashboard-pa
import * as AlarmDurationPredicateValueComponent from '@home/components/profile/alarm/alarm-duration-predicate-value.component'; import * as AlarmDurationPredicateValueComponent from '@home/components/profile/alarm/alarm-duration-predicate-value.component';
import * as DashboardImageDialogComponent from '@home/components/dashboard-page/dashboard-image-dialog.component'; import * as DashboardImageDialogComponent from '@home/components/dashboard-page/dashboard-image-dialog.component';
import * as WidgetContainerComponent from '@home/components/widget/widget-container.component'; import * as WidgetContainerComponent from '@home/components/widget/widget-container.component';
import * as TenantProfileQueuesComponent from '@home/components/profile/queue/tenant-profile-queues.component';
import { TenantProfileQueueComponent } from '@home/components/profile/queue/tenant-profile-queue.component';
import { IModulesMap } from '@modules/common/modules-map.models'; import { IModulesMap } from '@modules/common/modules-map.models';
@ -570,6 +572,8 @@ class ModulesMap implements IModulesMap {
'@home/components/profile/alarm/alarm-duration-predicate-value.component': AlarmDurationPredicateValueComponent, '@home/components/profile/alarm/alarm-duration-predicate-value.component': AlarmDurationPredicateValueComponent,
'@home/components/dashboard-page/dashboard-image-dialog.component': DashboardImageDialogComponent, '@home/components/dashboard-page/dashboard-image-dialog.component': DashboardImageDialogComponent,
'@home/components/widget/widget-container.component': WidgetContainerComponent, '@home/components/widget/widget-container.component': WidgetContainerComponent,
'@home/components/profile/queue/tenant-profile-queues.component': TenantProfileQueuesComponent,
'@home/components/profile/queue/tenant-profile-queue.component': TenantProfileQueueComponent
}; };
init() { init() {

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

@ -148,6 +148,8 @@ import {
} from '@home/components/tokens'; } from '@home/components/tokens';
import { DashboardStateComponent } from '@home/components/dashboard-page/dashboard-state.component'; import { DashboardStateComponent } from '@home/components/dashboard-page/dashboard-state.component';
import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component';
import { TenantProfileQueuesComponent } from '@home/components/profile/queue/tenant-profile-queues.component';
import { TenantProfileQueueComponent } from '@home/components/profile/queue/tenant-profile-queue.component';
@NgModule({ @NgModule({
declarations: declarations:
@ -267,7 +269,9 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
DashboardStateDialogComponent, DashboardStateDialogComponent,
DashboardImageDialogComponent, DashboardImageDialogComponent,
EmbedDashboardDialogComponent, EmbedDashboardDialogComponent,
DisplayWidgetTypesPanelComponent DisplayWidgetTypesPanelComponent,
TenantProfileQueuesComponent,
TenantProfileQueueComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -380,7 +384,9 @@ import { EntityDetailsPageComponent } from '@home/components/entity/entity-detai
DashboardStateDialogComponent, DashboardStateDialogComponent,
DashboardImageDialogComponent, DashboardImageDialogComponent,
EmbedDashboardDialogComponent, EmbedDashboardDialogComponent,
DisplayWidgetTypesPanelComponent DisplayWidgetTypesPanelComponent,
TenantProfileQueuesComponent,
TenantProfileQueueComponent
], ],
providers: [ providers: [
WidgetComponentService, WidgetComponentService,

4
ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts

@ -42,7 +42,7 @@ import { ServiceType } from '@shared/models/queue.models';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { OtaUpdateType } from '@shared/models/ota-package.models'; import { OtaUpdateType } from '@shared/models/ota-package.models';
import { DashboardId } from '@shared/models/id/dashboard-id'; import { DashboardId } from '@shared/models/id/dashboard-id';
import { QueueId } from "@shared/models/id/queue-id"; import { QueueId } from '@shared/models/id/queue-id';
@Component({ @Component({
selector: 'tb-device-profile', selector: 'tb-device-profile',
@ -198,7 +198,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
}}, {emitEvent: false}); }}, {emitEvent: false});
this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false}); this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultDashboardId: entity.defaultDashboardId ? entity.defaultDashboardId.id : null}, {emitEvent: false}); this.entityForm.patchValue({defaultDashboardId: entity.defaultDashboardId ? entity.defaultDashboardId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultQueueId: entity.defaultQueueId ? entity.defaultQueueId.id: null}, {emitEvent: false}); this.entityForm.patchValue({defaultQueueId: entity.defaultQueueId ? entity.defaultQueueId.id : null}, {emitEvent: false});
this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false}); this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false});
this.entityForm.patchValue({softwareId: entity.softwareId}, {emitEvent: false}); this.entityForm.patchValue({softwareId: entity.softwareId}, {emitEvent: false});
this.entityForm.patchValue({description: entity.description}, {emitEvent: false}); this.entityForm.patchValue({description: entity.description}, {emitEvent: false});

194
ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.html

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

22
ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.scss

@ -0,0 +1,22 @@
/**
* 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.
*/
:host ::ng-deep {
.queue-strategy {
.mat-expansion-panel-body {
padding-bottom: 0 !important;
}
}
}

206
ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.ts

@ -0,0 +1,206 @@
///
/// 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.
///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
Validator,
Validators
} from '@angular/forms';
import { DeviceProfileAlarm } from '@shared/models/device.models';
import { MatDialog } from '@angular/material/dialog';
import { UtilsService } from '@core/services/utils.service';
import { QueueProcessingStrategyTypes, QueueSubmitStrategyTypes } from '@shared/models/queue.models';
@Component({
selector: 'tb-tenant-profile-queue',
templateUrl: './tenant-profile-queue.component.html',
styleUrls: ['./tenant-profile-queue.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TenantProfileQueueComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => TenantProfileQueueComponent),
multi: true,
}
]
})
export class TenantProfileQueueComponent implements ControlValueAccessor, OnInit, Validator {
@Input()
disabled: boolean;
@Output()
removeQueue = new EventEmitter();
@Input()
expanded = false;
@Input()
mainQueue = false;
@Input()
newQueue = false;
private modelValue: DeviceProfileAlarm;
queueFormGroup: FormGroup;
submitStrategies: string[] = [];
processingStrategies: string[] = [];
hideBatchSize = false;
private propagateChange = null;
private propagateChangePending = false;
constructor(private dialog: MatDialog,
private utils: UtilsService,
private fb: FormBuilder) {
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
if (this.propagateChangePending) {
this.propagateChangePending = false;
setTimeout(() => {
this.propagateChange(this.modelValue);
}, 0);
}
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.submitStrategies = Object.values(QueueSubmitStrategyTypes);
this.processingStrategies = Object.values(QueueProcessingStrategyTypes);
this.queueFormGroup = this.fb.group(
{
name: ['', [Validators.required]],
pollInterval: [25, [Validators.min(1), Validators.required]],
partitions: [10, [Validators.min(1), Validators.required]],
consumerPerPartition: [false, []],
packProcessingTimeout: [2000, [Validators.min(1), Validators.required]],
submitStrategy: this.fb.group({
type: [null, [Validators.required]],
batchSize: [0, [Validators.min(1), Validators.required]],
}),
processingStrategy: this.fb.group({
type: [null, [Validators.required]],
retries: [3, [Validators.min(0), Validators.required]],
failurePercentage: [ 0, [Validators.min(0), Validators.required, Validators.max(100)]],
pauseBetweenRetries: [3, [Validators.min(1), Validators.required]],
maxPauseBetweenRetries: [3, [Validators.min(1), Validators.required]],
}),
topic: ['']
});
this.queueFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
this.queueFormGroup.get('name').valueChanges.subscribe((value) => this.queueFormGroup.patchValue({topic: `tb_rule_engine.${value}`}));
this.queueFormGroup.get('submitStrategy').get('type').valueChanges.subscribe(() => {
this.submitStrategyTypeChanged();
});
if (this.newQueue) {
this.queueFormGroup.get('name').enable({emitEvent: false});
} else {
this.queueFormGroup.get('name').disable({emitEvent: false});
}
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.queueFormGroup.disable({emitEvent: false});
} else {
this.queueFormGroup.enable({emitEvent: false});
this.queueFormGroup.get('name').disable({emitEvent: false});
}
}
writeValue(value: DeviceProfileAlarm): void {
this.propagateChangePending = false;
this.modelValue = value;
if (!this.modelValue.alarmType) {
this.expanded = true;
}
this.queueFormGroup.reset(this.modelValue || undefined, {emitEvent: false});
if (!this.disabled && !this.queueFormGroup.valid) {
this.updateModel();
}
}
public validate(c: FormControl) {
if (c.parent) {
const queueName = c.value.name;
const profileQueues = [];
c.parent.getRawValue().forEach((queue) => {
profileQueues.push(queue.name);
}
);
if (profileQueues.filter(profileQueue => profileQueue === queueName).length > 1) {
this.queueFormGroup.get('name').setErrors({
unique: true
});
}
}
return (this.queueFormGroup.valid) ? null : {
queue: {
valid: false,
},
};
}
get queueTitle(): string {
const queueName = this.queueFormGroup.get('name').value;
return this.utils.customTranslation(queueName, queueName);
}
private updateModel() {
const value = this.queueFormGroup.value;
this.modelValue = {...this.modelValue, ...value};
if (this.propagateChange) {
this.propagateChange(this.modelValue);
} else {
this.propagateChangePending = true;
}
}
submitStrategyTypeChanged() {
const form = this.queueFormGroup.get('submitStrategy') as FormGroup;
const type: QueueSubmitStrategyTypes = form.get('type').value;
const batchSizeField = form.get('batchSize');
if (type === QueueSubmitStrategyTypes.BATCH) {
batchSizeField.enable();
batchSizeField.patchValue(1000);
this.hideBatchSize = true;
} else {
batchSizeField.patchValue(null);
batchSizeField.disable();
this.hideBatchSize = false;
}
}
}

42
ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html

@ -0,0 +1,42 @@
<!--
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.
-->
<div fxLayout="column">
<div class="tb-tenant-profile-queues">
<mat-accordion [multi]="true">
<div *ngFor="let queuesControl of queuesFormArray().controls; trackBy: trackByQueue;
let $index = index; last as isLast;" fxLayout="column">
<tb-tenant-profile-queue [formControl]="queuesControl"
[mainQueue]="$index === 0"
[newQueue]="newQueue"
(removeQueue)="removeQueue($index)">
</tb-tenant-profile-queue>
</div>
</mat-accordion>
</div>
<div *ngIf="!queuesFormArray().controls.length">
<span translate fxLayoutAlign="center center"
class="tb-prompt">tenant-profile.no-queue</span>
</div>
<div *ngIf="!disabled" style="padding-top: 16px;">
<button mat-raised-button color="primary"
type="button"
(click)="addQueue()">
<span translate>tenant-profile.add-queue</span>
</button>
</div>
</div>

31
ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.scss

@ -0,0 +1,31 @@
/**
* 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.
*/
@import '../../../../../../scss/constants';
:host {
.tb-tenant-profile-queues {
&.mat-padding {
padding: 8px;
@media #{$mat-gt-sm} {
padding: 16px;
}
}
}
.tb-prompt{
margin: 30px 0;
}
}

178
ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts

@ -0,0 +1,178 @@
///
/// 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.
///
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
FormArray,
FormBuilder,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
Validator,
Validators
} from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Subscription } from 'rxjs';
import { QueueInfo } from '@shared/models/queue.models';
@Component({
selector: 'tb-tenant-profile-queues',
templateUrl: './tenant-profile-queues.component.html',
styleUrls: ['./tenant-profile-queues.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TenantProfileQueuesComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => TenantProfileQueuesComponent),
multi: true,
}
]
})
export class TenantProfileQueuesComponent implements ControlValueAccessor, OnInit, Validator {
tenantProfileQueuesFormGroup: FormGroup;
newQueue = false;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
disabled: boolean;
private valueChangeSubscription: Subscription = null;
private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>,
private fb: FormBuilder) {
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.tenantProfileQueuesFormGroup = this.fb.group({
queues: this.fb.array([])
});
}
queuesFormArray(): FormArray {
return this.tenantProfileQueuesFormGroup.get('queues') as FormArray;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.tenantProfileQueuesFormGroup.disable({emitEvent: false});
} else {
this.tenantProfileQueuesFormGroup.enable({emitEvent: false});
}
}
writeValue(queues: Array<QueueInfo> | null): void {
if (this.valueChangeSubscription) {
this.valueChangeSubscription.unsubscribe();
}
const queuesControls: Array<AbstractControl> = [];
if (queues) {
queues.forEach((queue) => {
queuesControls.push(this.fb.control(queue, [Validators.required]));
});
}
this.tenantProfileQueuesFormGroup.setControl('queues', this.fb.array(queuesControls));
if (this.disabled) {
this.tenantProfileQueuesFormGroup.disable({emitEvent: false});
} else {
this.tenantProfileQueuesFormGroup.enable({emitEvent: false});
}
this.valueChangeSubscription = this.tenantProfileQueuesFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
public trackByQueue(index: number, queueControl: AbstractControl): string {
if (queueControl) {
return queueControl.value.id;
} else {
return null;
}
}
public removeQueue(index: number) {
(this.tenantProfileQueuesFormGroup.get('queues') as FormArray).removeAt(index);
}
public addQueue() {
const queue = {
consumerPerPartition: false,
name: '',
packProcessingTimeout: 2000,
partitions: 10,
pollInterval: 25,
processingStrategy: {
failurePercentage: 0,
maxPauseBetweenRetries: 3,
pauseBetweenRetries: 3,
retries: 3,
type: ''
},
submitStrategy: {
batchSize: 0,
type: ''
},
topic: ''
};
this.newQueue = true;
const queuesArray = this.tenantProfileQueuesFormGroup.get('queues') as FormArray;
queuesArray.push(this.fb.control(queue, []));
this.tenantProfileQueuesFormGroup.updateValueAndValidity();
if (!this.tenantProfileQueuesFormGroup.valid) {
this.updateModel();
}
}
public validate(c: FormControl) {
return (this.tenantProfileQueuesFormGroup.valid) ? null : {
queues: {
valid: false,
},
};
}
private updateModel() {
const queues: Array<QueueInfo> = this.tenantProfileQueuesFormGroup.get('queues').value;
this.propagateChange(queues);
}
}

5
ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html

@ -16,11 +16,6 @@
--> -->
<form [formGroup]="tenantProfileDataFormGroup" style="padding-bottom: 16px;"> <form [formGroup]="tenantProfileDataFormGroup" style="padding-bottom: 16px;">
<!-- <tb-json-object-edit *ngIf="isolatedTbRuleEngine"-->
<!-- formControlName="queueConfiguration"-->
<!-- label="{{ 'admin.queue-configuration' | translate }}">-->
<!-- </tb-json-object-edit>-->
<mat-expansion-panel [expanded]="true"> <mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>

2
ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.ts

@ -79,7 +79,7 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit
} }
writeValue(value: TenantProfileData | null): void { writeValue(value: TenantProfileData | null): void {
this.tenantProfileDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false}); this.tenantProfileDataFormGroup.patchValue({configuration: value}, {emitEvent: false});
} }
private updateModel() { private updateModel() {

24
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html

@ -68,10 +68,26 @@
<div class="tb-hint">{{'tenant.isolated-tb-rule-engine-details' | translate}}</div> <div class="tb-hint">{{'tenant.isolated-tb-rule-engine-details' | translate}}</div>
</mat-checkbox> </mat-checkbox>
</div> </div>
<tb-tenant-profile-data <mat-accordion formGroupName="profileData">
formControlName="profileData" <mat-expansion-panel [expanded]="true" *ngIf="entityForm.get('isolatedTbRuleEngine').value">
required> <mat-expansion-panel-header>
</tb-tenant-profile-data> <mat-panel-title>
<div>{{'tenant-profile.queues-with-count' | translate:
{count: entityForm.get('profileData').get('queueConfiguration').value ?
entityForm.get('profileData').get('queueConfiguration').value.length : 0} }}</div>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-tenant-profile-queues
formControlName="queueConfiguration"
></tb-tenant-profile-queues>
</ng-template>
</mat-expansion-panel>
<tb-tenant-profile-data
formControlName="configuration"
required>
</tb-tenant-profile-data>
</mat-accordion>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>tenant-profile.description</mat-label> <mat-label translate>tenant-profile.description</mat-label>
<textarea matInput formControlName="description" rows="2"></textarea> <textarea matInput formControlName="description" rows="2"></textarea>

10
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.scss

@ -25,4 +25,14 @@
white-space: normal; white-space: normal;
} }
} }
.fields-group {
padding: 0 16px 8px;
margin-bottom: 10px;
border: 1px groove rgba(0, 0, 0, .25);
border-radius: 4px;
legend {
color: rgba(0, 0, 0, .7);
width: fit-content;
}
}
} }

78
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts

@ -18,12 +18,7 @@ import { ChangeDetectorRef, Component, Inject, Input, Optional } from '@angular/
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { import { createTenantProfileConfiguration, TenantProfile, TenantProfileType } from '@shared/models/tenant.model';
createTenantProfileConfiguration,
TenantProfile,
TenantProfileData,
TenantProfileType
} from '@shared/models/tenant.model';
import { ActionNotificationShow } from '@app/core/notification/notification.actions'; import { ActionNotificationShow } from '@app/core/notification/notification.actions';
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';
@ -48,11 +43,6 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
super(store, fb, entityValue, entitiesTableConfigValue, cd); super(store, fb, entityValue, entitiesTableConfigValue, cd);
} }
ngOnInit() {
this.showQueueParams();
this.entityForm.get('isolatedTbRuleEngine').valueChanges.subscribe(() => this.showQueueParams());
}
hideDelete() { hideDelete() {
if (this.entitiesTableConfig) { if (this.entitiesTableConfig) {
return !this.entitiesTableConfig.deleteEnabled(this.entity); return !this.entitiesTableConfig.deleteEnabled(this.entity);
@ -62,37 +52,61 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
} }
buildForm(entity: TenantProfile): FormGroup { buildForm(entity: TenantProfile): FormGroup {
return this.fb.group( const mainQueue = [
{
consumerPerPartition: true,
name: 'Main',
packProcessingTimeout: 2000,
partitions: 10,
pollInterval: 25,
processingStrategy: {
failurePercentage: 0,
maxPauseBetweenRetries: 3,
pauseBetweenRetries: 3,
retries: 3,
type: 'SKIP_ALL_FAILURES'
},
submitStrategy: {
batchSize: 1000,
type: 'BURST'
},
topic: 'tb_rule_engine.main'
}
];
const formGroup = this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], isolatedTbCore: [entity ? entity.isolatedTbCore : false, []],
isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []],
profileData: [entity && !this.isAdd ? entity.profileData : { profileData: this.fb.group({
configuration: createTenantProfileConfiguration(TenantProfileType.DEFAULT) configuration: [entity && !this.isAdd ? entity?.profileData.configuration
} as TenantProfileData, []], : createTenantProfileConfiguration(TenantProfileType.DEFAULT), []],
queueConfiguration: [null, []]
}),
description: [entity ? entity.description : '', []], description: [entity ? entity.description : '', []],
} }
); );
formGroup.get('isolatedTbRuleEngine').valueChanges.subscribe((value) => {
if (value) {
formGroup.get('profileData').patchValue({
queueConfiguration: mainQueue
}, {emitEvent: false});
} else {
formGroup.get('profileData').patchValue({
queueConfiguration: null
}, {emitEvent: false});
}
});
return formGroup;
} }
updateForm(entity: TenantProfile) { updateForm(entity: TenantProfile) {
this.entityForm.patchValue({name: entity.name}); this.entityForm.patchValue({name: entity.name}, {emitEvent: false});
this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}); this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}, {emitEvent: false});
this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}); this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}, {emitEvent: false});
this.entityForm.patchValue({profileData: !this.isAdd ? entity.profileData : { this.entityForm.get('profileData').patchValue({configuration: entity.profileData?.configuration}, {emitEvent: false});
configuration: createTenantProfileConfiguration(TenantProfileType.DEFAULT) this.entityForm.get('profileData').patchValue({queueConfiguration: entity.profileData?.queueConfiguration}, {emitEvent: false});
} as TenantProfileData}); this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
this.entityForm.patchValue({description: entity.description});
}
showQueueParams(): boolean {
let isolatedTbRuleEngine: boolean = this.entityForm.get('isolatedTbRuleEngine').value;
if (isolatedTbRuleEngine) {
//enable
} else {
//disable
}
return isolatedTbRuleEngine;
} }
updateFormState() { updateFormState() {

204
ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.html

@ -20,12 +20,12 @@
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')" (click)="onEntityAction($event, 'delete')"
[fxShow]="!hideDelete() && !isEdit"> [fxShow]="!hideDelete() && !isEdit">
{{'queue.delete' | translate }} {{ 'queue.delete' | translate }}
</button> </button>
</div> </div>
<div class="mat-padding" fxLayout="column"> <div class="mat-padding" fxLayout="column">
<form [formGroup]="entityForm"> <form [formGroup]="entityForm" class="queue-form">
<fieldset [disabled]="(isLoading$ | async) || !isEdit"> <fieldset [disabled]="(isLoading$ | async) || !isEdit">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>admin.queue-name</mat-label> <mat-label translate>admin.queue-name</mat-label>
@ -57,10 +57,6 @@
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<!-- <tb-checkbox formControlName="consumerPerPartition" style="display: block; padding-bottom: 16px;">-->
<!-- {{ 'queue.consumer-per-partition' | translate }}-->
<!-- </tb-checkbox>-->
<mat-checkbox class="hinted-checkbox" formControlName="consumerPerPartition"> <mat-checkbox class="hinted-checkbox" formControlName="consumerPerPartition">
<div>{{ 'queue.consumer-per-partition' | translate }}</div> <div>{{ 'queue.consumer-per-partition' | translate }}</div>
<div class="tb-hint">{{'queue.consumer-per-partition-hint' | translate}}</div> <div class="tb-hint">{{'queue.consumer-per-partition-hint' | translate}}</div>
@ -77,115 +73,113 @@
{{ 'queue.pack-processing-timeout-min-value' | translate }} {{ 'queue.pack-processing-timeout-min-value' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<div class="mat-accordion-container">
<mat-accordion [multi]="true"> <mat-accordion [multi]="true">
<mat-expansion-panel #panel1 hideToggle> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title translate>
<mat-label translate>queue.submit-strategy</mat-label> queue.submit-strategy
<mat-icon>{{panel1.expanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}</mat-icon>
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<div formGroupName="submitStrategy"> <ng-template matExpansionPanelContent>
<mat-form-field class="mat-block"> <div formGroupName="submitStrategy">
<mat-label translate>queue.submit-strategy</mat-label> <mat-form-field class="mat-block">
<mat-select formControlName="type" required> <mat-label translate>queue.submit-strategy</mat-label>
<mat-option *ngFor="let strategy of submitStrategies" [value]="strategy"> <mat-select formControlName="type" required>
{{ strategy }} <mat-option *ngFor="let strategy of submitStrategies" [value]="strategy">
</mat-option> {{ strategy }}
</mat-select> </mat-option>
<mat-error *ngIf="entityForm.get('submitStrategy.type').hasError('required')"> </mat-select>
{{ 'queue.submit-strategy-type-required' | translate }} <mat-error *ngIf="entityForm.get('submitStrategy.type').hasError('required')">
</mat-error> {{ 'queue.submit-strategy-type-required' | translate }}
</mat-form-field> </mat-error>
<mat-form-field class="mat-block" *ngIf="hideBatchSize"> </mat-form-field>
<mat-label translate>queue.batch-size</mat-label> <mat-form-field class="mat-block" *ngIf="hideBatchSize">
<input type="number" matInput formControlName="batchSize" required> <mat-label translate>queue.batch-size</mat-label>
<mat-error *ngIf="entityForm.get('submitStrategy.batchSize').hasError('required')"> <input type="number" matInput formControlName="batchSize" required>
{{ 'queue.batch-size-required' | translate }} <mat-error *ngIf="entityForm.get('submitStrategy.batchSize').hasError('required')">
</mat-error> {{ 'queue.batch-size-required' | translate }}
<mat-error *ngIf="entityForm.get('submitStrategy.batchSize').hasError('min') && </mat-error>
!entityForm.get('submitStrategy.batchSize').hasError('required')"> <mat-error *ngIf="entityForm.get('submitStrategy.batchSize').hasError('min') &&
{{ 'queue.batch-size-min-value' | translate }} !entityForm.get('submitStrategy.batchSize').hasError('required')">
</mat-error> {{ 'queue.batch-size-min-value' | translate }}
</mat-form-field> </mat-error>
</div> </mat-form-field>
</div>
</ng-template>
</mat-expansion-panel> </mat-expansion-panel>
<mat-expansion-panel #panel2 hideToggle> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title translate>
<mat-label translate>queue.processing-strategy</mat-label> queue.processing-strategy
<mat-icon>{{panel2.expanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}</mat-icon>
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div formGroupName="processingStrategy"> <div formGroupName="processingStrategy">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>queue.processing-strategy</mat-label> <mat-label translate>queue.processing-strategy</mat-label>
<mat-select formControlName="type" required> <mat-select formControlName="type" required>
<mat-option *ngFor="let strategy of processingStrategies" [value]="strategy"> <mat-option *ngFor="let strategy of processingStrategies" [value]="strategy">
{{ strategy }} {{ strategy }}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="entityForm.get('processingStrategy.type').hasError('required')"> <mat-error *ngIf="entityForm.get('processingStrategy.type').hasError('required')">
{{ 'queue.processing-strategy-type-required' | translate }} {{ 'queue.processing-strategy-type-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>queue.retries</mat-label> <mat-label translate>queue.retries</mat-label>
<input type="number" matInput formControlName="retries" required> <input type="number" matInput formControlName="retries" required>
<mat-error *ngIf="entityForm.get('processingStrategy.retries').hasError('required')"> <mat-error *ngIf="entityForm.get('processingStrategy.retries').hasError('required')">
{{ 'queue.retries-required' | translate }} {{ 'queue.retries-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.retries').hasError('min') && <mat-error *ngIf="entityForm.get('processingStrategy.retries').hasError('min') &&
!entityForm.get('processingStrategy.retries').hasError('required')"> !entityForm.get('processingStrategy.retries').hasError('required')">
{{ 'queue.retries-min-value' | translate }} {{ 'queue.retries-min-value' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>queue.failure-percentage</mat-label> <mat-label translate>queue.failure-percentage</mat-label>
<input type="number" matInput formControlName="failurePercentage" required> <input type="number" matInput formControlName="failurePercentage" required>
<mat-error *ngIf="entityForm.get('processingStrategy.failurePercentage').hasError('required')"> <mat-error *ngIf="entityForm.get('processingStrategy.failurePercentage').hasError('required')">
{{ 'queue.failure-percentage-required' | translate }} {{ 'queue.failure-percentage-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.failurePercentage').hasError('min') && <mat-error *ngIf="entityForm.get('processingStrategy.failurePercentage').hasError('min') &&
!entityForm.get('processingStrategy.failurePercentage').hasError('required') && !entityForm.get('processingStrategy.failurePercentage').hasError('required') &&
!entityForm.get('processingStrategy.failurePercentage').hasError('max')"> !entityForm.get('processingStrategy.failurePercentage').hasError('max')">
{{ 'queue.failure-percentage-min-value' | translate }} {{ 'queue.failure-percentage-min-value' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.failurePercentage').hasError('max') && <mat-error *ngIf="entityForm.get('processingStrategy.failurePercentage').hasError('max') &&
!entityForm.get('processingStrategy.failurePercentage').hasError('required') && !entityForm.get('processingStrategy.failurePercentage').hasError('required') &&
!entityForm.get('processingStrategy.failurePercentage').hasError('min')"> !entityForm.get('processingStrategy.failurePercentage').hasError('min')">
{{ 'queue.failure-percentage-max-value' | translate }} {{ 'queue.failure-percentage-max-value' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>queue.pause-between-retries</mat-label> <mat-label translate>queue.pause-between-retries</mat-label>
<input type="number" matInput formControlName="pauseBetweenRetries" required> <input type="number" matInput formControlName="pauseBetweenRetries" required>
<mat-error *ngIf="entityForm.get('processingStrategy.pauseBetweenRetries').hasError('required')"> <mat-error *ngIf="entityForm.get('processingStrategy.pauseBetweenRetries').hasError('required')">
{{ 'queue.pause-between-retries-required' | translate }} {{ 'queue.pause-between-retries-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.pauseBetweenRetries').hasError('min') && <mat-error *ngIf="entityForm.get('processingStrategy.pauseBetweenRetries').hasError('min') &&
!entityForm.get('processingStrategy.pauseBetweenRetries').hasError('required')"> !entityForm.get('processingStrategy.pauseBetweenRetries').hasError('required')">
{{ 'queue.pause-between-retries-min-value' | translate }} {{ 'queue.pause-between-retries-min-value' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>queue.max-pause-between-retries</mat-label> <mat-label translate>queue.max-pause-between-retries</mat-label>
<input type="number" matInput formControlName="maxPauseBetweenRetries" required> <input type="number" matInput formControlName="maxPauseBetweenRetries" required>
<mat-error *ngIf="entityForm.get('processingStrategy.maxPauseBetweenRetries').hasError('required')"> <mat-error *ngIf="entityForm.get('processingStrategy.maxPauseBetweenRetries').hasError('required')">
{{ 'queue.max-pause-between-retries-required' | translate }} {{ 'queue.max-pause-between-retries-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.maxPauseBetweenRetries').hasError('min') && <mat-error *ngIf="entityForm.get('processingStrategy.maxPauseBetweenRetries').hasError('min') &&
!entityForm.get('processingStrategy.maxPauseBetweenRetries').hasError('required')"> !entityForm.get('processingStrategy.maxPauseBetweenRetries').hasError('required')">
{{ 'queue.max-pause-between-retries-min-value' | translate }} {{ 'queue.max-pause-between-retries-min-value' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</ng-template>
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>
</div>
</fieldset> </fieldset>
</form> </form>
</div> </div>

43
ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.scss

@ -14,46 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
:host ::ng-deep { :host ::ng-deep {
.queue-form {
.mat-expansion-panel:not([class*='mat-elevation-z']) { .mat-expansion-panel-body {
box-shadow: none; padding-bottom: 0 !important;
}
.mat-accordion-container {
margin-bottom: 16px;
}
.mat-expansion-panel {
&:hover {
box-shadow: 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 5px 5px -3px rgba(0, 0, 0, 0.2);
}
&-header {
height: 56px;
border: 1px solid #f2f2f2;
&-title {
align-items: center;
justify-content: space-between;
margin-right: 0;
}
}
&.mat-expanded {
border: none;
box-shadow: 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 5px 5px -3px rgba(0, 0, 0, 0.2);
.mat-expansion-panel {
&-content {
margin-top: 20px;
}
&-body {
padding-bottom: 10px;
}
}
} }
} }
} }

6
ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.ts

@ -23,8 +23,6 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
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 set = Reflect.set;
import { distinctUntilChanged } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-queue', selector: 'tb-queue',
@ -39,7 +37,7 @@ export class QueueComponent extends EntityComponent<QueueInfo> {
processingStrategies: string[] = []; processingStrategies: string[] = [];
QueueSubmitStrategyTypes = QueueSubmitStrategyTypes; QueueSubmitStrategyTypes = QueueSubmitStrategyTypes;
hideBatchSize: boolean = false; hideBatchSize = false;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected translate: TranslateService, protected translate: TranslateService,
@ -140,7 +138,7 @@ export class QueueComponent extends EntityComponent<QueueInfo> {
} }
submitStrategyTypeChanged() { submitStrategyTypeChanged() {
const form = this.entityForm.get("submitStrategy") as FormGroup; const form = this.entityForm.get('submitStrategy') as FormGroup;
const type: QueueSubmitStrategyTypes = form.get('type').value; const type: QueueSubmitStrategyTypes = form.get('type').value;
const batchSizeField = form.get('batchSize'); const batchSizeField = form.get('batchSize');
if (type === QueueSubmitStrategyTypes.BATCH) { if (type === QueueSubmitStrategyTypes.BATCH) {

7
ui-ngx/src/app/shared/models/queue.models.ts

@ -16,7 +16,7 @@
import { BaseData, HasId } from '@shared/models/base-data'; import { BaseData, HasId } from '@shared/models/base-data';
import { TenantId } from '@shared/models/id/tenant-id'; import { TenantId } from '@shared/models/id/tenant-id';
import {QueueId} from "@shared/models/id/queue-id"; import {QueueId} from '@shared/models/id/queue-id';
export enum ServiceType { export enum ServiceType {
TB_CORE = 'TB_CORE', TB_CORE = 'TB_CORE',
@ -43,9 +43,10 @@ export enum QueueProcessingStrategyTypes {
} }
export interface QueueInfo extends BaseData<QueueId> { export interface QueueInfo extends BaseData<QueueId> {
name: string;
packProcessingTimeout: number; packProcessingTimeout: number;
partitions: number; partitions: number;
consumerPerPartition: boolean, consumerPerPartition: boolean;
pollInterval: number; pollInterval: number;
processingStrategy: { processingStrategy: {
type: QueueProcessingStrategyTypes, type: QueueProcessingStrategyTypes,
@ -58,6 +59,6 @@ export interface QueueInfo extends BaseData<QueueId> {
type: QueueSubmitStrategyTypes, type: QueueSubmitStrategyTypes,
batchSize: number, batchSize: number,
}; };
tenantId: TenantId; tenantId?: TenantId;
topic: string; topic: string;
} }

4
ui-ngx/src/app/shared/models/tenant.model.ts

@ -18,7 +18,7 @@ import { ContactBased } from '@shared/models/contact-based.model';
import { TenantId } from './id/tenant-id'; import { TenantId } from './id/tenant-id';
import { TenantProfileId } from '@shared/models/id/tenant-profile-id'; import { TenantProfileId } from '@shared/models/id/tenant-profile-id';
import { BaseData } from '@shared/models/base-data'; import { BaseData } from '@shared/models/base-data';
import {Validators} from "@angular/forms"; import { QueueInfo } from '@shared/models/queue.models';
export enum TenantProfileType { export enum TenantProfileType {
DEFAULT = 'DEFAULT' DEFAULT = 'DEFAULT'
@ -98,7 +98,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
export interface TenantProfileData { export interface TenantProfileData {
configuration: TenantProfileConfiguration; configuration: TenantProfileConfiguration;
queueConfiguration?: any; queueConfiguration?: QueueInfo;
} }
export interface TenantProfile extends BaseData<TenantProfileId> { export interface TenantProfile extends BaseData<TenantProfileId> {

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

@ -2862,7 +2862,10 @@
"max-sms-range": "Maximum number of SMS sent can't be negative", "max-sms-range": "Maximum number of SMS sent can't be negative",
"max-created-alarms": "Maximum number of alarms created (0 - unlimited)", "max-created-alarms": "Maximum number of alarms created (0 - unlimited)",
"max-created-alarms-required": "Maximum number of alarms created is required.", "max-created-alarms-required": "Maximum number of alarms created is required.",
"max-created-alarms-range": "Maximum number of alarms created can't be negative" "max-created-alarms-range": "Maximum number of alarms created can't be negative",
"no-queue": "No Queue configured",
"add-queue": "Add Queue",
"queues-with-count": "Queues ({{count}})"
}, },
"timeinterval": { "timeinterval": {
"seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }",

Loading…
Cancel
Save