Browse Source

Merge branch 'feature/queue-entity' of github.com:YevhenBondarenko/thingsboard into feature/queue-entity

pull/6134/head
YevhenBondarenko 4 years ago
parent
commit
2839878b9e
  1. 3
      ui-ngx/src/app/core/http/entity.service.ts
  2. 4
      ui-ngx/src/app/modules/common/modules-map.ts
  3. 6
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  4. 3
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  5. 194
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.html
  6. 36
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.html
  7. 8
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.scss
  8. 32
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts
  9. 17
      ui-ngx/src/app/modules/home/components/profile/tenant-profile-data.component.html
  10. 23
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html
  11. 2
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts
  12. 171
      ui-ngx/src/app/modules/home/components/queue/queue-form.component.html
  13. 0
      ui-ngx/src/app/modules/home/components/queue/queue-form.component.scss
  14. 57
      ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts
  15. 2
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts
  16. 36
      ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts
  17. 181
      ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.html
  18. 22
      ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.scss
  19. 109
      ui-ngx/src/app/modules/home/pages/admin/queue/queue.component.ts
  20. 33
      ui-ngx/src/app/modules/home/pages/admin/queue/queues-table-config.resolver.ts
  21. 2
      ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html
  22. 10
      ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts
  23. 2
      ui-ngx/src/app/shared/models/device.models.ts
  24. 3
      ui-ngx/src/app/shared/models/entity-type.models.ts
  25. 6
      ui-ngx/src/assets/locale/locale.constant-en_US.json

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

@ -87,8 +87,7 @@ import { bodyContentEdgeEventActionTypes, Edge, EdgeEvent, EdgeEventType } from
import { RuleChainMetaData, RuleChainType } from '@shared/models/rule-chain.models';
import { WidgetService } from '@core/http/widget.service';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { QueueService } from "@core/http/queue.service";
import { ServiceType } from "@shared/models/queue.models";
import { QueueService } from '@core/http/queue.service';
@Injectable({
providedIn: 'root'

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

@ -288,7 +288,7 @@ import * as AlarmDurationPredicateValueComponent from '@home/components/profile/
import * as DashboardImageDialogComponent from '@home/components/dashboard-page/dashboard-image-dialog.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 * as QueueFormComponent from '@home/components/queue/queue-form.component';
import { IModulesMap } from '@modules/common/modules-map.models';
@ -573,7 +573,7 @@ class ModulesMap implements IModulesMap {
'@home/components/dashboard-page/dashboard-image-dialog.component': DashboardImageDialogComponent,
'@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
'@home/components/queue/queue-form.component': QueueFormComponent
};
init() {

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

@ -149,7 +149,7 @@ import {
import { DashboardStateComponent } from '@home/components/dashboard-page/dashboard-state.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';
import { QueueFormComponent } from '@home/components/queue/queue-form.component';
@NgModule({
declarations:
@ -271,7 +271,7 @@ import { TenantProfileQueueComponent } from '@home/components/profile/queue/tena
EmbedDashboardDialogComponent,
DisplayWidgetTypesPanelComponent,
TenantProfileQueuesComponent,
TenantProfileQueueComponent
QueueFormComponent
],
imports: [
CommonModule,
@ -386,7 +386,7 @@ import { TenantProfileQueueComponent } from '@home/components/profile/queue/tena
EmbedDashboardDialogComponent,
DisplayWidgetTypesPanelComponent,
TenantProfileQueuesComponent,
TenantProfileQueueComponent
QueueFormComponent
],
providers: [
WidgetComponentService,

3
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -50,7 +50,7 @@ import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { deepTrim } from '@core/utils';
import { ServiceType } from '@shared/models/queue.models';
import { DashboardId } from '@shared/models/id/dashboard-id';
import { QueueId } from "@shared/models/id/queue-id";
import { QueueId } from '@shared/models/id/queue-id';
export interface AddDeviceProfileDialogData {
deviceProfileName: string;
@ -188,7 +188,6 @@ export class AddDeviceProfileDialogComponent extends
name: this.deviceProfileDetailsFormGroup.get('name').value,
type: this.deviceProfileDetailsFormGroup.get('type').value,
image: this.deviceProfileDetailsFormGroup.get('image').value,
// defaultQueueId: this.deviceProfileDetailsFormGroup.get('defaultQueueId').value,
transportType: this.transportConfigFormGroup.get('transportType').value,
provisionType: deviceProvisionConfiguration.type,
provisionDeviceKey,

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

@ -1,194 +0,0 @@
<!--
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>

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

@ -17,18 +17,34 @@
-->
<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 multi>
<mat-expansion-panel fxFlex expanded
*ngFor="let queuesControl of queuesFormArray.controls; trackBy: trackByQueue;
let $index = index; last as isLast;">
<mat-expansion-panel-header>
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<mat-panel-title>
{{ getName(queuesControl.value.name) }}
</mat-panel-title>
<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 *ngIf="!queuesFormArray().controls.length">
<div *ngIf="!queuesFormArray.controls.length">
<span translate fxLayoutAlign="center center"
class="tb-prompt">tenant-profile.no-queue</span>
</div>

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

@ -13,15 +13,11 @@
* 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;
}
.mat-expansion-panel-body {
padding-bottom: 0 !important;
}
}

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

@ -14,16 +14,16 @@
/// limitations under the License.
///
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
FormArray,
FormBuilder,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
Validators
} from '@angular/forms';
@ -32,6 +32,7 @@ import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Subscription } from 'rxjs';
import { QueueInfo } from '@shared/models/queue.models';
import { UtilsService } from '@core/services/utils.service';
@Component({
selector: 'tb-tenant-profile-queues',
@ -50,7 +51,7 @@ import { QueueInfo } from '@shared/models/queue.models';
}
]
})
export class TenantProfileQueuesComponent implements ControlValueAccessor, OnInit, Validator {
export class TenantProfileQueuesComponent implements ControlValueAccessor, Validator, OnDestroy {
tenantProfileQueuesFormGroup: FormGroup;
newQueue = false;
@ -67,11 +68,12 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, OnIni
@Input()
disabled: boolean;
private valueChangeSubscription: Subscription = null;
private valueChangeSubscription$: Subscription = null;
private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>,
private utils: UtilsService,
private fb: FormBuilder) {
}
@ -79,6 +81,12 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, OnIni
this.propagateChange = fn;
}
ngOnDestroy() {
if (this.valueChangeSubscription$) {
this.valueChangeSubscription$.unsubscribe();
}
}
registerOnTouched(fn: any): void {
}
@ -88,7 +96,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, OnIni
});
}
queuesFormArray(): FormArray {
get queuesFormArray(): FormArray {
return this.tenantProfileQueuesFormGroup.get('queues') as FormArray;
}
@ -102,8 +110,8 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, OnIni
}
writeValue(queues: Array<QueueInfo> | null): void {
if (this.valueChangeSubscription) {
this.valueChangeSubscription.unsubscribe();
if (this.valueChangeSubscription$) {
this.valueChangeSubscription$.unsubscribe();
}
const queuesControls: Array<AbstractControl> = [];
if (queues) {
@ -117,7 +125,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, OnIni
} else {
this.tenantProfileQueuesFormGroup.enable({emitEvent: false});
}
this.valueChangeSubscription = this.tenantProfileQueuesFormGroup.valueChanges.subscribe(() => {
this.valueChangeSubscription$ = this.tenantProfileQueuesFormGroup.valueChanges.subscribe(value => {
this.updateModel();
});
}
@ -163,14 +171,18 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, OnIni
}
}
public validate(c: FormControl) {
return (this.tenantProfileQueuesFormGroup.valid) ? null : {
public validate(c: AbstractControl): ValidationErrors | null {
return this.tenantProfileQueuesFormGroup.valid ? null : {
queues: {
valid: false,
},
};
}
getName(value) {
return this.utils.customTranslation(value, value);
}
private updateModel() {
const queues: Array<QueueInfo> = this.tenantProfileQueuesFormGroup.get('queues').value;
this.propagateChange(queues);

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

@ -16,17 +16,8 @@
-->
<form [formGroup]="tenantProfileDataFormGroup" style="padding-bottom: 16px;">
<mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div translate>tenant-profile.profile-configuration</div>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<tb-tenant-profile-configuration
formControlName="configuration"
required>
</tb-tenant-profile-configuration>
</ng-template>
</mat-expansion-panel>
<tb-tenant-profile-configuration
formControlName="configuration"
required>
</tb-tenant-profile-configuration>
</form>

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

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

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

@ -81,7 +81,7 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
profileData: this.fb.group({
configuration: [entity && !this.isAdd ? entity?.profileData.configuration
: createTenantProfileConfiguration(TenantProfileType.DEFAULT), []],
queueConfiguration: [null, []]
queueConfiguration: [entity && !this.isAdd ? entity?.profileData.queueConfiguration : null, []]
}),
description: [entity ? entity.description : '', []],
}

171
ui-ngx/src/app/modules/home/components/queue/queue-form.component.html

@ -0,0 +1,171 @@
<!--
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.
-->
<form [formGroup]="queueFormGroup" fxLayout="column" fxLayoutGap="0.5em">
<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>
</form>

0
ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.scss → ui-ngx/src/app/modules/home/components/queue/queue-form.component.scss

57
ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queue.component.ts → ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
@ -25,46 +25,40 @@ import {
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';
import { QueueInfo, QueueProcessingStrategyTypes, QueueSubmitStrategyTypes } from '@shared/models/queue.models';
import { isDefinedAndNotNull } from '@core/utils';
@Component({
selector: 'tb-tenant-profile-queue',
templateUrl: './tenant-profile-queue.component.html',
styleUrls: ['./tenant-profile-queue.component.scss'],
selector: 'tb-queue-form',
templateUrl: './queue-form.component.html',
styleUrls: ['./queue-form.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TenantProfileQueueComponent),
useExisting: forwardRef(() => QueueFormComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => TenantProfileQueueComponent),
useExisting: forwardRef(() => QueueFormComponent),
multi: true,
}
]
})
export class TenantProfileQueueComponent implements ControlValueAccessor, OnInit, Validator {
export class QueueFormComponent implements ControlValueAccessor, OnInit, Validator {
@Input()
disabled: boolean;
@Output()
removeQueue = new EventEmitter();
@Input()
expanded = false;
newQueue = false;
@Input()
mainQueue = false;
systemQueue = false;
@Input()
newQueue = false;
private modelValue: DeviceProfileAlarm;
private modelValue: QueueInfo;
queueFormGroup: FormGroup;
@ -106,7 +100,7 @@ export class TenantProfileQueueComponent implements ControlValueAccessor, OnInit
packProcessingTimeout: [2000, [Validators.min(1), Validators.required]],
submitStrategy: this.fb.group({
type: [null, [Validators.required]],
batchSize: [0, [Validators.min(1), Validators.required]],
batchSize: [null],
}),
processingStrategy: this.fb.group({
type: [null, [Validators.required]],
@ -141,20 +135,20 @@ export class TenantProfileQueueComponent implements ControlValueAccessor, OnInit
}
}
writeValue(value: DeviceProfileAlarm): void {
writeValue(value: QueueInfo): void {
this.propagateChangePending = false;
this.modelValue = value;
if (!this.modelValue.alarmType) {
this.expanded = true;
if (isDefinedAndNotNull(this.modelValue)) {
this.queueFormGroup.patchValue(this.modelValue, {emitEvent: false});
}
this.queueFormGroup.reset(this.modelValue || undefined, {emitEvent: false});
this.submitStrategyTypeChanged();
if (!this.disabled && !this.queueFormGroup.valid) {
this.updateModel();
}
}
public validate(c: FormControl) {
if (c.parent) {
if (c.parent && !this.systemQueue) {
const queueName = c.value.name;
const profileQueues = [];
c.parent.getRawValue().forEach((queue) => {
@ -174,11 +168,6 @@ export class TenantProfileQueueComponent implements ControlValueAccessor, OnInit
};
}
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};
@ -194,12 +183,14 @@ export class TenantProfileQueueComponent implements ControlValueAccessor, OnInit
const type: QueueSubmitStrategyTypes = form.get('type').value;
const batchSizeField = form.get('batchSize');
if (type === QueueSubmitStrategyTypes.BATCH) {
batchSizeField.enable();
batchSizeField.patchValue(1000);
batchSizeField.enable({emitEvent: false});
batchSizeField.patchValue(1000, {emitEvent: false});
batchSizeField.setValidators([Validators.min(1), Validators.required]);
this.hideBatchSize = true;
} else {
batchSizeField.patchValue(null);
batchSizeField.disable();
batchSizeField.patchValue(null, {emitEvent: false});
batchSizeField.disable({emitEvent: false});
batchSizeField.clearValidators();
this.hideBatchSize = false;
}
}

2
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts

@ -49,7 +49,7 @@ import { MediaBreakpoints } from '@shared/models/constants';
import { RuleChainId } from '@shared/models/id/rule-chain-id';
import { ServiceType } from '@shared/models/queue.models';
import { deepTrim } from '@core/utils';
import { QueueId } from "@shared/models/id/queue-id";
import { QueueId } from '@shared/models/id/queue-id';
@Component({
selector: 'tb-device-wizard',

36
ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts

@ -187,19 +187,41 @@ const routes: Routes = [
},
{
path: 'queues',
component: EntitiesTableComponent,
canDeactivate: [ConfirmOnExitGuard],
data: {
auth: [Authority.SYS_ADMIN],
title: 'admin.queues',
breadcrumb: {
label: 'admin.queues',
icon: 'swap_calls'
}
},
resolve: {
entitiesTableConfig: QueuesTableConfigResolver
}
children: [
{
path: '',
component: EntitiesTableComponent,
data: {
auth: [Authority.SYS_ADMIN],
title: 'admin.queues'
},
resolve: {
entitiesTableConfig: QueuesTableConfigResolver
}
},
{
path: ':entityId',
component: EntityDetailsPageComponent,
canDeactivate: [ConfirmOnExitGuard],
data: {
breadcrumb: {
labelFunction: entityDetailsPageBreadcrumbLabelFunction,
icon: 'swap_calls'
} as BreadCrumbConfig<EntityDetailsPageComponent>,
auth: [Authority.SYS_ADMIN],
title: 'admin.queues'
},
resolve: {
entitiesTableConfig: QueuesTableConfigResolver
}
}
]
}
]
}

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

@ -16,170 +16,33 @@
-->
<div class="tb-details-buttons" fxLayout.xs="column">
<button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'open')"
[fxShow]="!isEdit && !isDetailsPage">
{{'common.open-details-page' | translate }}
</button>
<button mat-raised-button color="primary" fxFlex.xs
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')"
[fxShow]="!hideDelete() && !isEdit">
{{ 'queue.delete' | translate }}
</button>
<div fxLayout="row" fxLayout.xs="column">
<button mat-raised-button
ngxClipboard
(cbOnSuccess)="onQueueIdCopied($event)"
[cbContent]="entity?.id?.id"
[fxShow]="!isEdit">
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
<span translate>queue.copyId</span>
</button>
</div>
</div>
<div class="mat-padding" fxLayout="column">
<form [formGroup]="entityForm" class="queue-form">
<fieldset [disabled]="(isLoading$ | async) || !isEdit">
<mat-form-field class="mat-block">
<mat-label translate>admin.queue-name</mat-label>
<input matInput formControlName="name" required>
<mat-error *ngIf="entityForm.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="entityForm.get('pollInterval').hasError('required')">
{{ 'queue.poll-interval-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('pollInterval').hasError('min') &&
!entityForm.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="entityForm.get('partitions').hasError('required')">
{{ 'queue.partitions-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('partitions').hasError('min') &&
!entityForm.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="entityForm.get('packProcessingTimeout').hasError('required')">
{{ 'queue.pack-processing-timeout-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('packProcessingTimeout').hasError('min') &&
!entityForm.get('packProcessingTimeout').hasError('required')">
{{ 'queue.pack-processing-timeout-min-value' | translate }}
</mat-error>
</mat-form-field>
<mat-accordion [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="entityForm.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="entityForm.get('submitStrategy.batchSize').hasError('required')">
{{ 'queue.batch-size-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('submitStrategy.batchSize').hasError('min') &&
!entityForm.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="entityForm.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="entityForm.get('processingStrategy.retries').hasError('required')">
{{ 'queue.retries-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.retries').hasError('min') &&
!entityForm.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="entityForm.get('processingStrategy.failurePercentage').hasError('required')">
{{ 'queue.failure-percentage-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.failurePercentage').hasError('min') &&
!entityForm.get('processingStrategy.failurePercentage').hasError('required') &&
!entityForm.get('processingStrategy.failurePercentage').hasError('max')">
{{ 'queue.failure-percentage-min-value' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.failurePercentage').hasError('max') &&
!entityForm.get('processingStrategy.failurePercentage').hasError('required') &&
!entityForm.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="entityForm.get('processingStrategy.pauseBetweenRetries').hasError('required')">
{{ 'queue.pause-between-retries-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.pauseBetweenRetries').hasError('min') &&
!entityForm.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="entityForm.get('processingStrategy.maxPauseBetweenRetries').hasError('required')">
{{ 'queue.max-pause-between-retries-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('processingStrategy.maxPauseBetweenRetries').hasError('min') &&
!entityForm.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>
</fieldset>
</form>
<div class="mat-padding" fxLayout="column" [formGroup]="entityForm">
<tb-queue-form
[newQueue]="isAdd"
[systemQueue]="true"
formControlName="queue">
</tb-queue-form>
</div>

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

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

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

@ -16,18 +16,19 @@
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
import { EntityType } from '@shared/models/entity-type.models';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { FormBuilder, FormGroup } from '@angular/forms';
import { EntityComponent } from '@home/components/entity/entity.component';
import { QueueInfo, QueueProcessingStrategyTypes, QueueSubmitStrategyTypes } from '@shared/models/queue.models';
import { QueueInfo } from '@shared/models/queue.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { ActionNotificationShow } from '@core/notification/notification.actions';
@Component({
selector: 'tb-queue',
templateUrl: './queue.component.html',
styleUrls: ['./queue.component.scss']
styleUrls: []
})
export class QueueComponent extends EntityComponent<QueueInfo> {
entityForm: FormGroup;
@ -36,9 +37,6 @@ export class QueueComponent extends EntityComponent<QueueInfo> {
submitStrategies: string[] = [];
processingStrategies: string[] = [];
QueueSubmitStrategyTypes = QueueSubmitStrategyTypes;
hideBatchSize = false;
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
@Inject('entity') protected entityValue: QueueInfo,
@ -46,62 +44,16 @@ export class QueueComponent extends EntityComponent<QueueInfo> {
protected cd: ChangeDetectorRef,
public fb: FormBuilder) {
super(store, fb, entityValue, entitiesTableConfigValue, cd);
this.submitStrategies = Object.values(QueueSubmitStrategyTypes);
this.processingStrategies = Object.values(QueueProcessingStrategyTypes);
}
ngOnInit() {
super.ngOnInit();
this.entityForm.get('submitStrategy').get('type').valueChanges.subscribe(() => {
this.submitStrategyTypeChanged();
});
}
buildForm(entity: QueueInfo): FormGroup {
return this.fb.group(
{
name: [entity ? entity.name : '', [Validators.required]],
pollInterval: [
entity && entity.pollInterval ? entity.pollInterval : 25,
[Validators.min(1), Validators.required]
],
partitions: [
entity && entity.partitions ? entity.partitions : 10,
[Validators.min(1), Validators.required]
],
consumerPerPartition: [entity ? entity.consumerPerPartition : false, []],
packProcessingTimeout: [
entity && entity.packProcessingTimeout ? entity.packProcessingTimeout : 2000,
[Validators.min(1), Validators.required]
],
submitStrategy: this.fb.group({
type: [entity ? entity.submitStrategy?.type : null, [Validators.required]],
batchSize: [
entity && entity.submitStrategy?.batchSize ? entity.submitStrategy?.batchSize : 1000,
[Validators.min(1), Validators.required]
],
}),
processingStrategy: this.fb.group({
type: [entity ? entity.processingStrategy?.type : null, [Validators.required]],
retries: [
entity && entity.processingStrategy?.retries ? entity.processingStrategy?.retries : 3,
[Validators.min(0), Validators.required]
],
failurePercentage: [
entity && entity.processingStrategy?.failurePercentage ? entity.processingStrategy?.failurePercentage : 0,
[Validators.min(0), Validators.required, Validators.max(100)]
],
pauseBetweenRetries: [
entity && entity.processingStrategy?.pauseBetweenRetries ? entity.processingStrategy?.pauseBetweenRetries : 3,
[Validators.min(1), Validators.required]
],
maxPauseBetweenRetries: [
entity && entity.processingStrategy?.maxPauseBetweenRetries ? entity.processingStrategy?.maxPauseBetweenRetries : 3,
[Validators.min(1), Validators.required]
],
})
}
);
return this.fb.group({
queue: [entity]
});
}
hideDelete() {
@ -114,41 +66,22 @@ export class QueueComponent extends EntityComponent<QueueInfo> {
updateForm(entity: QueueInfo) {
this.entityForm.patchValue({
name: entity.name,
pollInterval: entity.pollInterval,
partitions: entity.partitions,
consumerPerPartition: entity.consumerPerPartition,
packProcessingTimeout: entity.packProcessingTimeout,
submitStrategy: {
type: entity.submitStrategy?.type,
batchSize: entity.submitStrategy?.batchSize,
},
processingStrategy: {
type: entity.processingStrategy?.type,
retries: entity.processingStrategy?.retries,
failurePercentage: entity.processingStrategy?.failurePercentage,
pauseBetweenRetries: entity.processingStrategy?.pauseBetweenRetries,
maxPauseBetweenRetries: entity.processingStrategy?.maxPauseBetweenRetries,
}
}, {emitEvent: true});
queue: entity
}, {emitEvent: false});
}
if (!this.isAdd) {
this.entityForm.get('name').disable({emitEvent: false});
}
prepareFormValue(formValue: any) {
return super.prepareFormValue(formValue.queue);
}
submitStrategyTypeChanged() {
const form = this.entityForm.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;
}
onQueueIdCopied($event) {
this.store.dispatch(new ActionNotificationShow(
{
message: this.translate.instant('queue.idCopiedMessage'),
type: 'success',
duration: 750,
verticalPosition: 'bottom',
horizontalPosition: 'right'
}));
}
}

33
ui-ngx/src/app/modules/home/pages/admin/queue/queues-table-config.resolver.ts

@ -15,11 +15,8 @@
///
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
import {
EntityTableColumn,
EntityTableConfig
} from '@home/models/entity/entities-table-config.models';
import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router';
import { EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { QueueInfo, ServiceType } from '@shared/models/queue.models';
import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@ -34,6 +31,7 @@ import { TranslateService } from '@ngx-translate/core';
import { QueueComponent } from './queue.component';
import { QueueService } from '@core/http/queue.service';
import { selectAuthUser } from '@core/auth/auth.selectors';
import { EntityAction } from '@home/models/entity/entity-component.models';
@Injectable()
export class QueuesTableConfigResolver implements Resolve<EntityTableConfig<QueueInfo>> {
@ -48,6 +46,7 @@ export class QueuesTableConfigResolver implements Resolve<EntityTableConfig<Queu
private customerService: CustomerService,
private dialogService: DialogService,
private homeDialogs: HomeDialogsService,
private router: Router,
private translate: TranslateService) {
this.config.entityType = EntityType.QUEUE;
@ -59,6 +58,8 @@ export class QueuesTableConfigResolver implements Resolve<EntityTableConfig<Queu
this.config.deleteEntityContent = () => this.translate.instant('queue.delete-queue-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('queue.delete-queues-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('queue.delete-queues-text');
this.config.onEntityAction = action => this.onQueueAction(action);
}
resolve(route: ActivatedRouteSnapshot): Observable<EntityTableConfig<QueueInfo>> {
@ -100,16 +101,28 @@ export class QueuesTableConfigResolver implements Resolve<EntityTableConfig<Queu
configureEntityFunctions(): void {
this.config.entitiesFetchFunction = pageLink => this.queueService.getTenantQueuesByServiceType(pageLink, this.queueType);
this.config.loadEntity = id => this.queueService.getQueueById(id.id);
this.config.saveEntity = queue => this.queueService.saveQueue(this.addTopicForQueue(queue), this.queueType).pipe(
this.config.saveEntity = queue => this.queueService.saveQueue(queue, this.queueType).pipe(
mergeMap((savedQueue) => this.queueService.getQueueById(savedQueue.id.id)
));
this.config.deleteEntity = id => this.queueService.deleteQueue(id.id);
this.config.deleteEnabled = (queue) => queue && queue.name !== 'Main';
this.config.entitySelectionEnabled = (queue) => queue && queue.name !== 'Main';
}
onQueueAction(action: EntityAction<QueueInfo>): boolean {
switch (action.action) {
case 'open':
this.openQueue(action.event, action.entity);
return true;
}
return false;
}
private addTopicForQueue(queue: QueueInfo): QueueInfo {
const modifiedQueue = Object.assign({}, queue);
modifiedQueue.topic = `tb_rule_engine.${queue.name}`;
return modifiedQueue;
private openQueue($event: Event, queue) {
if ($event) {
$event.stopPropagation();
}
const url = this.router.createUrlTree(['settings', 'queues', queue.id.id]);
this.router.navigateByUrl(url);
}
}

2
ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.html

@ -42,7 +42,7 @@
<ng-template #searchNotEmpty>
<span>
{{ translate.get('queue.no-queues-matching',
{entity: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
{queue: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
</span>
</ng-template>
</div>

10
ui-ngx/src/app/shared/components/queue/queue-autocomplete.component.ts

@ -27,11 +27,11 @@ import { EntityType } from '@shared/models/entity-type.models';
import { BaseData } from '@shared/models/base-data';
import { EntityService } from '@core/http/entity.service';
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import {QueueInfo, ServiceType} from "@shared/models/queue.models";
import { QueueService } from "@core/http/queue.service";
import { PageLink } from "@shared/models/page/page-link";
import { Direction } from "@shared/models/page/sort-order";
import { emptyPageData } from "@shared/models/page/page-data";
import { QueueInfo, ServiceType } from '@shared/models/queue.models';
import { QueueService } from '@core/http/queue.service';
import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order';
import { emptyPageData } from '@shared/models/page/page-data';
@Component({
selector: 'tb-queue-autocomplete',

2
ui-ngx/src/app/shared/models/device.models.ts

@ -29,7 +29,7 @@ import * as _moment from 'moment';
import { AbstractControl, ValidationErrors } from '@angular/forms';
import { OtaPackageId } from '@shared/models/id/ota-package-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';
import { DataType } from '@shared/models/constants';
import {
getDefaultProfileClientLwM2mSettingsConfig,

3
ui-ngx/src/app/shared/models/entity-type.models.ts

@ -428,7 +428,8 @@ export const baseDetailsPageByEntityType = new Map<EntityType, string>([
[EntityType.EDGE, '/edgeInstances'],
[EntityType.ENTITY_VIEW, '/entityViews'],
[EntityType.TB_RESOURCE, '/settings/resources-library'],
[EntityType.OTA_PACKAGE, '/otaUpdates']
[EntityType.OTA_PACKAGE, '/otaUpdates'],
[EntityType.QUEUE, '/settings/queues']
]);
export interface EntitySubtype {

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

@ -2719,13 +2719,15 @@
"partitions": "Partitions",
"consumer-per-partition": "Consumer per partition",
"consumer-per-partition-hint": "Enable separate consumer(s) per each partition",
"processing-timeout": "Processing timeout",
"processing-timeout": "Processing timeout, ms",
"batch-size": "Batch size",
"retries": "Retries (0 - unlimited)",
"failure-percentage": "Failure Percentage",
"pause-between-retries": "Pause between retries",
"max-pause-between-retries": "Maximal pause between retries",
"delete": "Delete queue"
"delete": "Delete queue",
"copyId": "Copy queue Id",
"idCopiedMessage": "Queue Id has been copied to clipboard"
},
"tenant": {
"tenant": "Tenant",

Loading…
Cancel
Save