Browse Source

Merge pull request #4871 from vvlladd28/improvement/device-credential/select-type

UI: Improvement device credentials
pull/4901/head
Igor Kulikov 5 years ago
committed by GitHub
parent
commit
ccb04f34a1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 0
      ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.html
  2. 10
      ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.ts
  3. 25
      ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.html
  4. 0
      ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.scss
  5. 21
      ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.ts
  6. 48
      ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html
  7. 133
      ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.ts
  8. 85
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.html
  9. 93
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts
  10. 46
      ui-ngx/src/app/modules/home/components/device/device-credentials.module.ts
  11. 16
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  12. 4
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html
  13. 16
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts
  14. 27
      ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html
  15. 41
      ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.ts
  16. 2
      ui-ngx/src/app/modules/home/pages/device/device.module.ts
  17. 4
      ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts
  18. 14
      ui-ngx/src/app/shared/models/device.models.ts
  19. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

0
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.html → ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.html

10
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts → ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.ts

@ -39,24 +39,24 @@ import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@Component({ @Component({
selector: 'tb-security-config-lwm2m-server', selector: 'tb-device-credentials-lwm2m-server',
templateUrl: './security-config-lwm2m-server.component.html', templateUrl: './device-credentials-lwm2m-server.component.html',
styleUrls: [], styleUrls: [],
providers: [ providers: [
{ {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent), useExisting: forwardRef(() => DeviceCredentialsLwm2mServerComponent),
multi: true multi: true
}, },
{ {
provide: NG_VALIDATORS, provide: NG_VALIDATORS,
useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent), useExisting: forwardRef(() => DeviceCredentialsLwm2mServerComponent),
multi: true multi: true
} }
] ]
}) })
export class SecurityConfigLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator { export class DeviceCredentialsLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator {
serverFormGroup: FormGroup; serverFormGroup: FormGroup;
securityConfigLwM2MType = Lwm2mSecurityType; securityConfigLwM2MType = Lwm2mSecurityType;

25
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html → ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<mat-tab-group> <mat-tab-group [formGroup]="lwm2mConfigFormGroup">
<mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}" [formGroup]="lwm2mConfigFormGroup"> <mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}">
<ng-container formGroupName="client"> <ng-container formGroupName="client">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>device.lwm2m-security-config.endpoint</mat-label> <mat-label translate>device.lwm2m-security-config.endpoint</mat-label>
@ -79,7 +79,7 @@
</mat-form-field> </mat-form-field>
</ng-container> </ng-container>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}" [formGroup]="lwm2mConfigFormGroup"> <mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}">
<div style="padding: 2px;" formGroupName="bootstrap"> <div style="padding: 2px;" formGroupName="bootstrap">
<mat-accordion multi="true" class="mat-body-1"> <mat-accordion multi="true" class="mat-body-1">
<mat-expansion-panel> <mat-expansion-panel>
@ -89,9 +89,9 @@
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-security-config-lwm2m-server <tb-device-credentials-lwm2m-server
formControlName="bootstrapServer"> formControlName="bootstrapServer">
</tb-security-config-lwm2m-server> </tb-device-credentials-lwm2m-server>
</ng-template> </ng-template>
</mat-expansion-panel> </mat-expansion-panel>
<mat-expansion-panel> <mat-expansion-panel>
@ -101,23 +101,12 @@
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<tb-security-config-lwm2m-server <tb-device-credentials-lwm2m-server
formControlName="lwm2mServer"> formControlName="lwm2mServer">
</tb-security-config-lwm2m-server> </tb-device-credentials-lwm2m-server>
</ng-template> </ng-template>
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>
</div> </div>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'device.lwm2m-security-config.config-json-tab' | translate }}">
<ng-template matTabContent>
<tb-json-object-edit
[ngModel]="lwm2mConfigFormGroup.value"
label="{{ 'device.lwm2m-value' | translate }}"
readonly
[required]="true"
[fillHeight]="false">
</tb-json-object-edit>
</ng-template>
</mat-tab>
</mat-tab-group> </mat-tab-group>

0
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss → ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.scss

21
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts → ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.ts

@ -40,24 +40,24 @@ import { takeUntil } from 'rxjs/operators';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
@Component({ @Component({
selector: 'tb-security-config-lwm2m', selector: 'tb-device-credentials-lwm2m',
templateUrl: './security-config-lwm2m.component.html', templateUrl: './device-credentials-lwm2m.component.html',
styleUrls: ['./security-config-lwm2m.component.scss'], styleUrls: ['./device-credentials-lwm2m.component.scss'],
providers: [ providers: [
{ {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SecurityConfigLwm2mComponent), useExisting: forwardRef(() => DeviceCredentialsLwm2mComponent),
multi: true multi: true
}, },
{ {
provide: NG_VALIDATORS, provide: NG_VALIDATORS,
useExisting: forwardRef(() => SecurityConfigLwm2mComponent), useExisting: forwardRef(() => DeviceCredentialsLwm2mComponent),
multi: true multi: true
} }
] ]
}) })
export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Validator, OnDestroy { export class DeviceCredentialsLwm2mComponent implements ControlValueAccessor, Validator, OnDestroy {
lwm2mConfigFormGroup: FormGroup; lwm2mConfigFormGroup: FormGroup;
securityConfigLwM2MType = Lwm2mSecurityType; securityConfigLwM2MType = Lwm2mSecurityType;
@ -89,6 +89,7 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid
this.lwm2mConfigFormGroup.disable({emitEvent: false}); this.lwm2mConfigFormGroup.disable({emitEvent: false});
} else { } else {
this.lwm2mConfigFormGroup.enable({emitEvent: false}); this.lwm2mConfigFormGroup.enable({emitEvent: false});
this.securityConfigClientUpdateValidators(this.lwm2mConfigFormGroup.get('client.securityConfigClientMode').value);
} }
} }
@ -126,21 +127,21 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid
switch (mode) { switch (mode) {
case Lwm2mSecurityType.NO_SEC: case Lwm2mSecurityType.NO_SEC:
this.setValidatorsNoSecX509(); this.setValidatorsNoSecX509();
this.lwm2mConfigFormGroup.get('client.cert').disable(); this.lwm2mConfigFormGroup.get('client.cert').disable({emitEvent: false});
break; break;
case Lwm2mSecurityType.X509: case Lwm2mSecurityType.X509:
this.setValidatorsNoSecX509(); this.setValidatorsNoSecX509();
this.lwm2mConfigFormGroup.get('client.cert').enable(); this.lwm2mConfigFormGroup.get('client.cert').enable({emitEvent: false});
break; break;
case Lwm2mSecurityType.PSK: case Lwm2mSecurityType.PSK:
this.lenMaxKeyClient = LEN_MAX_PSK; this.lenMaxKeyClient = LEN_MAX_PSK;
this.setValidatorsPskRpk(mode); this.setValidatorsPskRpk(mode);
this.lwm2mConfigFormGroup.get('client.identity').enable(); this.lwm2mConfigFormGroup.get('client.identity').enable({emitEvent: false});
break; break;
case Lwm2mSecurityType.RPK: case Lwm2mSecurityType.RPK:
this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK; this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
this.setValidatorsPskRpk(mode); this.setValidatorsPskRpk(mode);
this.lwm2mConfigFormGroup.get('client.identity').disable(); this.lwm2mConfigFormGroup.get('client.identity').disable({emitEvent: false});
break; break;
} }
this.lwm2mConfigFormGroup.get('client.identity').updateValueAndValidity({emitEvent: false}); this.lwm2mConfigFormGroup.get('client.identity').updateValueAndValidity({emitEvent: false});

48
ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.html

@ -0,0 +1,48 @@
<!--
Copyright © 2016-2021 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.
-->
<section [formGroup]="deviceCredentialsMqttFormGroup">
<mat-form-field class="mat-block">
<mat-label translate>device.client-id</mat-label>
<input matInput formControlName="clientId">
<mat-error *ngIf="deviceCredentialsMqttFormGroup.get('clientId').hasError('pattern')">
{{ 'device.client-id-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>device.user-name</mat-label>
<input matInput formControlName="userName" [required]="!!deviceCredentialsMqttFormGroup.get('password').value">
<mat-error *ngIf="deviceCredentialsMqttFormGroup.get('userName').hasError('required')">
{{ 'device.user-name-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>device.password</mat-label>
<input matInput formControlName="password"
autocomplete="new-password"
(ngModelChange)="passwordChanged()"
[type]="hidePassword ? 'password' : 'text'">
<button mat-icon-button matSuffix type="button"
(click)="hidePassword = !hidePassword"
[attr.aria-pressed]="hidePassword">
<mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
<tb-error style="margin-top: -12px; display: block;"
[error]="deviceCredentialsMqttFormGroup.hasError('atLeastOne') ?
('device.client-id-or-user-name-necessary' | translate) : ''"></tb-error>
</section>

133
ui-ngx/src/app/modules/home/components/device/device-credentials-mqtt-basic.component.ts

@ -0,0 +1,133 @@
///
/// Copyright © 2016-2021 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, OnDestroy } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
ValidatorFn,
Validators
} from '@angular/forms';
import { Subject } from 'rxjs';
import { DeviceCredentialMQTTBasic } from '@shared/models/device.models';
import { takeUntil } from 'rxjs/operators';
import { isDefinedAndNotNull, isEmptyStr } from '@core/utils';
@Component({
selector: 'tb-device-credentials-mqtt-basic',
templateUrl: './device-credentials-mqtt-basic.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DeviceCredentialsMqttBasicComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DeviceCredentialsMqttBasicComponent),
multi: true,
}],
styleUrls: []
})
export class DeviceCredentialsMqttBasicComponent implements ControlValueAccessor, Validator, OnDestroy {
@Input()
disabled: boolean;
deviceCredentialsMqttFormGroup: FormGroup;
hidePassword = true;
private destroy$ = new Subject();
private propagateChange = (v: any) => {};
constructor(public fb: FormBuilder) {
this.deviceCredentialsMqttFormGroup = this.fb.group({
clientId: [null, [Validators.pattern(/^[A-Za-z0-9]+$/)]],
userName: [null],
password: [null]
}, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])});
this.deviceCredentialsMqttFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
this.updateView(value);
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {}
setDisabledState(isDisabled: boolean) {
this.disabled = isDisabled;
if (this.disabled) {
this.deviceCredentialsMqttFormGroup.disable({emitEvent: false});
} else {
this.deviceCredentialsMqttFormGroup.enable({emitEvent: false});
}
}
validate(): ValidationErrors | null {
return this.deviceCredentialsMqttFormGroup.valid ? null : {
deviceCredentialsMqttBasic: false
};
}
writeValue(mqttBasic: string) {
if (isDefinedAndNotNull(mqttBasic) && !isEmptyStr(mqttBasic)) {
const value = JSON.parse(mqttBasic);
this.deviceCredentialsMqttFormGroup.patchValue(value, {emitEvent: false});
}
}
updateView(value: DeviceCredentialMQTTBasic) {
const formValue = JSON.stringify(value);
this.propagateChange(formValue);
}
passwordChanged() {
const value = this.deviceCredentialsMqttFormGroup.get('password').value;
if (value !== '') {
this.deviceCredentialsMqttFormGroup.get('userName').setValidators([Validators.required]);
} else {
this.deviceCredentialsMqttFormGroup.get('userName').setValidators([]);
}
this.deviceCredentialsMqttFormGroup.get('userName').updateValueAndValidity({emitEvent: false});
}
private atLeastOne(validator: ValidatorFn, controls: string[] = null) {
return (group: FormGroup): ValidationErrors | null => {
if (!controls) {
controls = Object.keys(group.controls);
}
const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k]));
return hasAtLeastOne ? null : {atLeastOne: true};
};
}
}

85
ui-ngx/src/app/modules/home/components/device/device-credentials.component.html

@ -16,7 +16,7 @@
--> -->
<section [formGroup]="deviceCredentialsFormGroup"> <section [formGroup]="deviceCredentialsFormGroup">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block" [fxShow]="credentialsTypes?.length > 1">
<mat-label translate>device.credentials-type</mat-label> <mat-label translate>device.credentials-type</mat-label>
<mat-select formControlName="credentialsType"> <mat-select formControlName="credentialsType">
<mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType"> <mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType">
@ -24,58 +24,35 @@
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.ACCESS_TOKEN" <div [ngSwitch]="deviceCredentialsFormGroup.get('credentialsType').value">
class="mat-block"> <ng-template [ngSwitchCase]="deviceCredentialsType.ACCESS_TOKEN">
<mat-label translate>device.access-token</mat-label> <mat-form-field class="mat-block">
<input matInput formControlName="credentialsId" required> <mat-label translate>device.access-token</mat-label>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')"> <input matInput formControlName="credentialsId" required>
{{ 'device.access-token-required' | translate }} <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')">
</mat-error> {{ 'device.access-token-required' | translate }}
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('pattern')"> </mat-error>
{{ 'device.access-token-invalid' | translate }} <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('pattern')">
</mat-error> {{ 'device.access-token-invalid' | translate }}
</mat-form-field> </mat-error>
<mat-form-field *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.X509_CERTIFICATE" </mat-form-field>
class="mat-block"> </ng-template>
<mat-label translate>device.rsa-key</mat-label> <ng-template [ngSwitchCase]="deviceCredentialsType.X509_CERTIFICATE">
<textarea matInput formControlName="credentialsValue" cols="15" rows="5" required></textarea> <mat-form-field class="mat-block">
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')"> <mat-label translate>device.rsa-key</mat-label>
{{ 'device.rsa-key-required' | translate }} <textarea matInput formControlName="credentialsValue" cols="15" rows="5" required></textarea>
</mat-error> <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">
</mat-form-field> {{ 'device.rsa-key-required' | translate }}
<section *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.MQTT_BASIC" formGroupName="credentialsBasic"> </mat-error>
<mat-form-field class="mat-block"> </mat-form-field>
<mat-label translate>device.client-id</mat-label> </ng-template>
<input matInput formControlName="clientId"> <ng-template [ngSwitchCase]="deviceCredentialsType.MQTT_BASIC">
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsBasic.clientId').hasError('pattern')"> <tb-device-credentials-mqtt-basic formControlName="credentialsValue">
{{ 'device.client-id-pattern' | translate }} </tb-device-credentials-mqtt-basic>
</mat-error> </ng-template>
</mat-form-field> <ng-template [ngSwitchCase]="deviceCredentialsType.LWM2M_CREDENTIALS">
<mat-form-field class="mat-block"> <tb-device-credentials-lwm2m formControlName="credentialsValue">
<mat-label translate>device.user-name</mat-label> </tb-device-credentials-lwm2m>
<input matInput formControlName="userName" [required]="!!deviceCredentialsFormGroup.get('credentialsBasic.password').value"> </ng-template>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsBasic.userName').hasError('required')">
{{ 'device.user-name-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>device.password</mat-label>
<input matInput formControlName="password"
autocomplete="new-password"
(ngModelChange)="passwordChanged()"
[type]="hidePassword ? 'password' : 'text'">
<button mat-icon-button matSuffix type="button"
(click)="hidePassword = !hidePassword"
[attr.aria-pressed]="hidePassword">
<mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
<tb-error style="margin-top: -12px; display: block;"
[error]="deviceCredentialsFormGroup.get('credentialsBasic').hasError('atLeastOne') ?
('device.client-id-or-user-name-necessary' | translate) : ''"></tb-error>
</section>
<div *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.LWM2M_CREDENTIALS">
<tb-security-config-lwm2m formControlName="credentialsValue">
</tb-security-config-lwm2m>
</div> </div>
</section> </section>

93
ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts

@ -22,16 +22,15 @@ import {
FormGroup, FormGroup,
NG_VALIDATORS, NG_VALIDATORS,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
ValidationErrors,
Validator, Validator,
ValidatorFn,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { import {
credentialTypeNames, credentialTypeNames,
DeviceCredentialMQTTBasic, credentialTypesByTransportType,
DeviceCredentials, DeviceCredentials,
DeviceCredentialsType DeviceCredentialsType,
DeviceTransportType
} from '@shared/models/device.models'; } from '@shared/models/device.models';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
@ -58,32 +57,40 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
@Input() @Input()
disabled: boolean; disabled: boolean;
private deviceTransportTypeValue = DeviceTransportType.DEFAULT;
get deviceTransportType(): DeviceTransportType {
return this.deviceTransportTypeValue;
}
@Input()
set deviceTransportType(type: DeviceTransportType) {
if (type) {
this.deviceTransportTypeValue = type;
this.credentialsTypes = credentialTypesByTransportType.get(type);
const currentType = this.deviceCredentialsFormGroup.get('credentialsType').value;
if (!this.credentialsTypes.includes(currentType)) {
this.deviceCredentialsFormGroup.get('credentialsType').patchValue(this.credentialsTypes[0], {onlySelf: true});
}
}
}
private destroy$ = new Subject(); private destroy$ = new Subject();
deviceCredentialsFormGroup: FormGroup; deviceCredentialsFormGroup: FormGroup;
deviceCredentialsType = DeviceCredentialsType; deviceCredentialsType = DeviceCredentialsType;
credentialsTypes = Object.values(DeviceCredentialsType); credentialsTypes = credentialTypesByTransportType.get(DeviceTransportType.DEFAULT);
credentialTypeNamesMap = credentialTypeNames; credentialTypeNamesMap = credentialTypeNames;
hidePassword = true;
private propagateChange = (v: any) => {}; private propagateChange = (v: any) => {};
constructor(public fb: FormBuilder) { constructor(public fb: FormBuilder) {
this.deviceCredentialsFormGroup = this.fb.group({ this.deviceCredentialsFormGroup = this.fb.group({
credentialsType: [DeviceCredentialsType.ACCESS_TOKEN], credentialsType: [DeviceCredentialsType.ACCESS_TOKEN],
credentialsId: [null], credentialsId: [null],
credentialsValue: [null], credentialsValue: [null]
credentialsBasic: this.fb.group({
clientId: [null, [Validators.pattern(/^[A-Za-z0-9]+$/)]],
userName: [null],
password: [null]
}, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])})
}); });
this.deviceCredentialsFormGroup.get('credentialsBasic').disable();
this.deviceCredentialsFormGroup.valueChanges.pipe( this.deviceCredentialsFormGroup.valueChanges.pipe(
takeUntil(this.destroy$) takeUntil(this.destroy$)
).subscribe(() => { ).subscribe(() => {
@ -109,18 +116,11 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
writeValue(value: DeviceCredentials | null): void { writeValue(value: DeviceCredentials | null): void {
if (isDefinedAndNotNull(value)) { if (isDefinedAndNotNull(value)) {
let credentialsBasic = {clientId: null, userName: null, password: null}; const credentialsType = this.credentialsTypes.includes(value.credentialsType) ? value.credentialsType : this.credentialsTypes[0];
let credentialsValue = null;
if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) {
credentialsBasic = JSON.parse(value.credentialsValue) as DeviceCredentialMQTTBasic;
} else {
credentialsValue = value.credentialsValue;
}
this.deviceCredentialsFormGroup.patchValue({ this.deviceCredentialsFormGroup.patchValue({
credentialsType: value.credentialsType, credentialsType,
credentialsId: value.credentialsId, credentialsId: value.credentialsId,
credentialsValue, credentialsValue: value.credentialsValue
credentialsBasic
}, {emitEvent: false}); }, {emitEvent: false});
this.updateValidators(); this.updateValidators();
} }
@ -128,10 +128,6 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
updateView() { updateView() {
const deviceCredentialsValue = this.deviceCredentialsFormGroup.value; const deviceCredentialsValue = this.deviceCredentialsFormGroup.value;
if (deviceCredentialsValue.credentialsType === DeviceCredentialsType.MQTT_BASIC) {
deviceCredentialsValue.credentialsValue = JSON.stringify(deviceCredentialsValue.credentialsBasic);
}
delete deviceCredentialsValue.credentialsBasic;
this.propagateChange(deviceCredentialsValue); this.propagateChange(deviceCredentialsValue);
} }
@ -163,14 +159,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
credentialsTypeChanged(): void { credentialsTypeChanged(): void {
this.deviceCredentialsFormGroup.patchValue({ this.deviceCredentialsFormGroup.patchValue({
credentialsId: null, credentialsId: null,
credentialsValue: null, credentialsValue: null
credentialsBasic: {clientId: '', userName: '', password: ''}
}); });
this.updateValidators(); this.updateValidators();
} }
updateValidators(): void { updateValidators(): void {
this.hidePassword = true;
const credentialsType = this.deviceCredentialsFormGroup.get('credentialsType').value as DeviceCredentialsType; const credentialsType = this.deviceCredentialsFormGroup.get('credentialsType').value as DeviceCredentialsType;
switch (credentialsType) { switch (credentialsType) {
case DeviceCredentialsType.ACCESS_TOKEN: case DeviceCredentialsType.ACCESS_TOKEN:
@ -178,48 +172,13 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false}); this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]); this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]);
this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false});
break; break;
case DeviceCredentialsType.X509_CERTIFICATE: default:
case DeviceCredentialsType.LWM2M_CREDENTIALS:
this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]); this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]);
this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false}); this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]); this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]);
this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false}); this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false});
break;
case DeviceCredentialsType.MQTT_BASIC:
this.deviceCredentialsFormGroup.get('credentialsBasic').enable({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsBasic').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]);
this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]);
this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
break; break;
} }
} }
private atLeastOne(validator: ValidatorFn, controls: string[] = null) {
return (group: FormGroup): ValidationErrors | null => {
if (!controls) {
controls = Object.keys(group.controls);
}
const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k]));
return hasAtLeastOne ? null : {atLeastOne: true};
};
}
passwordChanged() {
const value = this.deviceCredentialsFormGroup.get('credentialsBasic.password').value;
if (value !== '') {
this.deviceCredentialsFormGroup.get('credentialsBasic.userName').setValidators([Validators.required]);
} else {
this.deviceCredentialsFormGroup.get('credentialsBasic.userName').setValidators([]);
}
this.deviceCredentialsFormGroup.get('credentialsBasic.userName').updateValueAndValidity({
emitEvent: false,
onlySelf: true
});
}
} }

46
ui-ngx/src/app/modules/home/components/device/device-credentials.module.ts

@ -0,0 +1,46 @@
///
/// Copyright © 2016-2021 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 { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { CopyDeviceCredentialsComponent } from './copy-device-credentials.component';
import { DeviceCredentialsComponent } from './device-credentials.component';
import { DeviceCredentialsLwm2mComponent } from './device-credentials-lwm2m.component';
import { DeviceCredentialsLwm2mServerComponent } from './device-credentials-lwm2m-server.component';
import { DeviceCredentialsMqttBasicComponent } from './device-credentials-mqtt-basic.component';
@NgModule({
declarations: [
CopyDeviceCredentialsComponent,
DeviceCredentialsComponent,
DeviceCredentialsLwm2mComponent,
DeviceCredentialsLwm2mServerComponent,
DeviceCredentialsMqttBasicComponent
],
imports: [
CommonModule,
SharedModule
],
exports: [
CopyDeviceCredentialsComponent,
DeviceCredentialsComponent,
DeviceCredentialsLwm2mComponent,
DeviceCredentialsLwm2mServerComponent,
DeviceCredentialsMqttBasicComponent
]
})
export class DeviceCredentialsModule { }

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

@ -110,7 +110,6 @@ import { RuleChainAutocompleteComponent } from '@home/components/rule-chain/rule
import { DeviceProfileProvisionConfigurationComponent } from '@home/components/profile/device-profile-provision-configuration.component'; import { DeviceProfileProvisionConfigurationComponent } from '@home/components/profile/device-profile-provision-configuration.component';
import { AlarmScheduleComponent } from '@home/components/profile/alarm/alarm-schedule.component'; import { AlarmScheduleComponent } from '@home/components/profile/alarm/alarm-schedule.component';
import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component'; import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component';
import { DeviceCredentialsComponent } from '@home/components/device/device-credentials.component';
import { AlarmScheduleInfoComponent } from '@home/components/profile/alarm/alarm-schedule-info.component'; import { AlarmScheduleInfoComponent } from '@home/components/profile/alarm/alarm-schedule-info.component';
import { AlarmScheduleDialogComponent } from '@home/components/profile/alarm/alarm-schedule-dialog.component'; import { AlarmScheduleDialogComponent } from '@home/components/profile/alarm/alarm-schedule-dialog.component';
import { EditAlarmDetailsDialogComponent } from '@home/components/profile/alarm/edit-alarm-details-dialog.component'; import { EditAlarmDetailsDialogComponent } from '@home/components/profile/alarm/edit-alarm-details-dialog.component';
@ -120,7 +119,6 @@ import { TenantProfileConfigurationComponent } from '@home/components/profile/te
import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component'; import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component';
import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component'; import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component';
import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component'; import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component';
import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-device-credentials.component';
import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lwm2m/lwm2m-profile-components.module'; import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lwm2m/lwm2m-profile-components.module';
import { DashboardPageComponent } from '@home/components/dashboard-page/dashboard-page.component'; import { DashboardPageComponent } from '@home/components/dashboard-page/dashboard-page.component';
import { DashboardToolbarComponent } from '@home/components/dashboard-page/dashboard-toolbar.component'; import { DashboardToolbarComponent } from '@home/components/dashboard-page/dashboard-toolbar.component';
@ -139,11 +137,10 @@ import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink-
import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component'; import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component';
import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component'; import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component';
import { AlarmDurationPredicateValueComponent } from '@home/components/profile/alarm/alarm-duration-predicate-value.component'; import { AlarmDurationPredicateValueComponent } from '@home/components/profile/alarm/alarm-duration-predicate-value.component';
import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
import { SecurityConfigLwm2mServerComponent } from '@home/components/device/security-config-lwm2m-server.component';
import { DashboardImageDialogComponent } from '@home/components/dashboard-page/dashboard-image-dialog.component'; import { DashboardImageDialogComponent } from '@home/components/dashboard-page/dashboard-image-dialog.component';
import { WidgetContainerComponent } from '@home/components/widget/widget-container.component'; import { WidgetContainerComponent } from '@home/components/widget/widget-container.component';
import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module'; import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module';
import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module';
@NgModule({ @NgModule({
declarations: declarations:
@ -245,10 +242,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
AlarmScheduleComponent, AlarmScheduleComponent,
AlarmDurationPredicateValueComponent, AlarmDurationPredicateValueComponent,
DeviceWizardDialogComponent, DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleDialogComponent, AlarmScheduleDialogComponent,
EditAlarmDetailsDialogComponent, EditAlarmDetailsDialogComponent,
SmsProviderConfigurationComponent, SmsProviderConfigurationComponent,
@ -274,7 +267,8 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
SharedHomeComponentsModule, SharedHomeComponentsModule,
Lwm2mProfileComponentsModule, Lwm2mProfileComponentsModule,
SnmpDeviceProfileTransportModule, SnmpDeviceProfileTransportModule,
StatesControllerModule StatesControllerModule,
DeviceCredentialsModule
], ],
exports: [ exports: [
EntitiesTableComponent, EntitiesTableComponent,
@ -353,10 +347,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
AddDeviceProfileDialogComponent, AddDeviceProfileDialogComponent,
RuleChainAutocompleteComponent, RuleChainAutocompleteComponent,
DeviceWizardDialogComponent, DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
SecurityConfigLwm2mComponent,
SecurityConfigLwm2mServerComponent,
AlarmScheduleInfoComponent, AlarmScheduleInfoComponent,
AlarmScheduleComponent, AlarmScheduleComponent,
AlarmScheduleDialogComponent, AlarmScheduleDialogComponent,

4
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html

@ -64,7 +64,8 @@
[ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}" [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}"
[addNewProfile]="false" [addNewProfile]="false"
[selectDefaultProfile]="true" [selectDefaultProfile]="true"
[editProfileEnabled]="false"> [editProfileEnabled]="false"
(deviceProfileChanged)="deviceProfileChanged($event)">
</tb-device-profile-autocomplete> </tb-device-profile-autocomplete>
<mat-form-field fxFlex class="mat-block" <mat-form-field fxFlex class="mat-block"
[ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 1}"> [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 1}">
@ -154,6 +155,7 @@
<mat-checkbox style="padding-bottom: 16px;" formControlName="setCredential">{{ 'device.wizard.add-credentials' | translate }}</mat-checkbox> <mat-checkbox style="padding-bottom: 16px;" formControlName="setCredential">{{ 'device.wizard.add-credentials' | translate }}</mat-checkbox>
<tb-device-credentials <tb-device-credentials
[fxShow]="credentialsFormGroup.get('setCredential').value" [fxShow]="credentialsFormGroup.get('setCredential').value"
[deviceTransportType]="deviceTransportType"
formControlName="credential"> formControlName="credential">
</tb-device-credentials> </tb-device-credentials>
</form> </form>

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

@ -25,6 +25,7 @@ import {
createDeviceProfileConfiguration, createDeviceProfileConfiguration,
createDeviceProfileTransportConfiguration, createDeviceProfileTransportConfiguration,
DeviceProfile, DeviceProfile,
DeviceProfileInfo,
DeviceProfileType, DeviceProfileType,
DeviceProvisionConfiguration, DeviceProvisionConfiguration,
DeviceProvisionType, DeviceProvisionType,
@ -91,6 +92,7 @@ export class DeviceWizardDialogComponent extends
serviceType = ServiceType.TB_RULE_ENGINE; serviceType = ServiceType.TB_RULE_ENGINE;
private subscriptions: Subscription[] = []; private subscriptions: Subscription[] = [];
private currentDeviceProfileTransportType = DeviceTransportType.DEFAULT;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@ -265,6 +267,20 @@ export class DeviceWizardDialogComponent extends
} }
} }
get deviceTransportType(): DeviceTransportType {
if (this.deviceWizardFormGroup.get('addProfileType').value) {
return this.transportConfigFormGroup.get('transportType').value;
} else {
return this.currentDeviceProfileTransportType;
}
}
deviceProfileChanged(deviceProfile: DeviceProfileInfo) {
if (deviceProfile) {
this.currentDeviceProfileTransportType = deviceProfile.transportType;
}
}
private createDeviceProfile(): Observable<EntityId> { private createDeviceProfile(): Observable<EntityId> {
if (this.deviceWizardFormGroup.get('addProfileType').value) { if (this.deviceWizardFormGroup.get('addProfileType').value) {
const deviceProvisionConfiguration: DeviceProvisionConfiguration = this.provisionConfigFormGroup.get('provisionConfiguration').value; const deviceProvisionConfiguration: DeviceProvisionConfiguration = this.provisionConfigFormGroup.get('provisionConfiguration').value;

27
ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html

@ -17,7 +17,7 @@
--> -->
<form [formGroup]="deviceCredentialsFormGroup" (ngSubmit)="save()" style="min-width: 350px;"> <form [formGroup]="deviceCredentialsFormGroup" (ngSubmit)="save()" style="min-width: 350px;">
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<h2 translate>device.device-credentials</h2> <h2>{{ 'device.device-credentials' | translate }}</h2>
<span fxFlex></span> <span fxFlex></span>
<button mat-icon-button <button mat-icon-button
(click)="cancel()" (click)="cancel()"
@ -25,15 +25,26 @@
<mat-icon class="material-icons">close</mat-icon> <mat-icon class="material-icons">close</mat-icon>
</button> </button>
</mat-toolbar> </mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> <mat-progress-bar color="warn" mode="indeterminate" *ngIf="(isLoading$ | async) && !loadingCredentials">
</mat-progress-bar> </mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> <div style="height: 4px;" *ngIf="!(isLoading$ | async) || loadingCredentials"></div>
<div mat-dialog-content> <div mat-dialog-content>
<fieldset [disabled]="(isLoading$ | async) || isReadOnly"> <section *ngIf="!loadingCredentials; else loadCredentials">
<tb-device-credentials <fieldset [disabled]="(isLoading$ | async) || isReadOnly">
formControlName="credential"> <tb-device-credentials
</tb-device-credentials> [deviceTransportType]="deviceTransportType"
</fieldset> formControlName="credential">
</tb-device-credentials>
</fieldset>
</section>
<ng-template #loadCredentials>
<div fxLayout="column" fxLayoutAlign="center center">
<mat-spinner color="accent" diameter="65" strokeWidth="4" style="margin-bottom: 18px"></mat-spinner>
<span class="mat-subheading-2" style="margin-bottom: 0">
{{ 'device.loading-device-credentials' | translate }}
</span>
</div>
</ng-template>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-button color="primary" <button mat-button color="primary"

41
ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.ts

@ -21,13 +21,16 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms'; import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
import { DeviceService } from '@core/http/device.service'; import { DeviceService } from '@core/http/device.service';
import { credentialTypeNames, DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; import { DeviceCredentials, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { forkJoin } from 'rxjs';
export interface DeviceCredentialsDialogData { export interface DeviceCredentialsDialogData {
isReadOnly: boolean; isReadOnly: boolean;
deviceId: string; deviceId: string;
deviceProfileId: string;
} }
@Component({ @Component({
@ -40,25 +43,18 @@ export class DeviceCredentialsDialogComponent extends
DialogComponent<DeviceCredentialsDialogComponent, DeviceCredentials> implements OnInit, ErrorStateMatcher { DialogComponent<DeviceCredentialsDialogComponent, DeviceCredentials> implements OnInit, ErrorStateMatcher {
deviceCredentialsFormGroup: FormGroup; deviceCredentialsFormGroup: FormGroup;
deviceTransportType: DeviceTransportType;
isReadOnly: boolean; isReadOnly: boolean;
loadingCredentials = true;
deviceCredentials: DeviceCredentials; private deviceCredentials: DeviceCredentials;
private submitted = false;
submitted = false;
deviceCredentialsType = DeviceCredentialsType;
credentialsTypes = Object.keys(DeviceCredentialsType);
credentialTypeNamesMap = credentialTypeNames;
hidePassword = true;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogData, @Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogData,
private deviceService: DeviceService, private deviceService: DeviceService,
private deviceProfileService: DeviceProfileService,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<DeviceCredentialsDialogComponent, DeviceCredentials>, public dialogRef: MatDialogRef<DeviceCredentialsDialogComponent, DeviceCredentials>,
public fb: FormBuilder) { public fb: FormBuilder) {
@ -84,14 +80,17 @@ export class DeviceCredentialsDialogComponent extends
} }
loadDeviceCredentials() { loadDeviceCredentials() {
this.deviceService.getDeviceCredentials(this.data.deviceId).subscribe( const task = [];
(deviceCredentials) => { task.push(this.deviceService.getDeviceCredentials(this.data.deviceId));
this.deviceCredentials = deviceCredentials; task.push(this.deviceProfileService.getDeviceProfileInfo(this.data.deviceProfileId));
this.deviceCredentialsFormGroup.patchValue({ forkJoin(task).subscribe(([deviceCredentials, deviceProfile]: [DeviceCredentials, DeviceProfileInfo]) => {
credential: deviceCredentials this.deviceTransportType = deviceProfile.transportType;
}, {emitEvent: false}); this.deviceCredentials = deviceCredentials;
} this.deviceCredentialsFormGroup.patchValue({
); credential: deviceCredentials
}, {emitEvent: false});
this.loadingCredentials = false;
});
} }
cancel(): void { cancel(): void {

2
ui-ngx/src/app/modules/home/pages/device/device.module.ts

@ -33,6 +33,7 @@ import { MqttDeviceTransportConfigurationComponent } from './data/mqtt-device-tr
import { CoapDeviceTransportConfigurationComponent } from './data/coap-device-transport-configuration.component'; import { CoapDeviceTransportConfigurationComponent } from './data/coap-device-transport-configuration.component';
import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component'; import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component';
import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-transport-configuration.component'; import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-transport-configuration.component';
import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -55,6 +56,7 @@ import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-tr
SharedModule, SharedModule,
HomeComponentsModule, HomeComponentsModule,
HomeDialogsModule, HomeDialogsModule,
DeviceCredentialsModule,
DeviceRoutingModule DeviceRoutingModule
] ]
}) })

4
ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts

@ -112,7 +112,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
)); ));
}; };
this.config.onEntityAction = action => this.onDeviceAction(action); this.config.onEntityAction = action => this.onDeviceAction(action);
this.config.detailsReadonly = () => (this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user'); this.config.detailsReadonly = () =>
(this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user');
this.config.headerComponent = DeviceTableHeaderComponent; this.config.headerComponent = DeviceTableHeaderComponent;
@ -528,6 +529,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {
deviceId: device.id.id, deviceId: device.id.id,
deviceProfileId: device.deviceProfileId.id,
isReadOnly: this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user' isReadOnly: this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user'
} }
}).afterClosed().subscribe(deviceCredentials => { }).afterClosed().subscribe(deviceCredentials => {

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

@ -682,6 +682,20 @@ export const credentialTypeNames = new Map<DeviceCredentialsType, string>(
] ]
); );
export const credentialTypesByTransportType = new Map<DeviceTransportType, DeviceCredentialsType[]>(
[
[DeviceTransportType.DEFAULT, [
DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE, DeviceCredentialsType.MQTT_BASIC
]],
[DeviceTransportType.MQTT, [
DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE, DeviceCredentialsType.MQTT_BASIC
]],
[DeviceTransportType.COAP, [DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE]],
[DeviceTransportType.LWM2M, [DeviceCredentialsType.LWM2M_CREDENTIALS]],
[DeviceTransportType.SNMP, [DeviceCredentialsType.ACCESS_TOKEN]]
]
);
export interface DeviceCredentials extends BaseData<DeviceCredentialsId> { export interface DeviceCredentials extends BaseData<DeviceCredentialsId> {
deviceId: DeviceId; deviceId: DeviceId;
credentialsType: DeviceCredentialsType; credentialsType: DeviceCredentialsType;

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

@ -941,13 +941,13 @@
"unassign-devices-title": "Are you sure you want to unassign { count, plural, 1 {1 device} other {# devices} }?", "unassign-devices-title": "Are you sure you want to unassign { count, plural, 1 {1 device} other {# devices} }?",
"unassign-devices-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the customer.", "unassign-devices-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the customer.",
"device-credentials": "Device Credentials", "device-credentials": "Device Credentials",
"loading-device-credentials": "Loading device credentials...",
"credentials-type": "Credentials type", "credentials-type": "Credentials type",
"access-token": "Access token", "access-token": "Access token",
"access-token-required": "Access token is required.", "access-token-required": "Access token is required.",
"access-token-invalid": "Access token length must be from 1 to 20 characters.", "access-token-invalid": "Access token length must be from 1 to 20 characters.",
"rsa-key": "RSA public key", "rsa-key": "RSA public key",
"rsa-key-required": "RSA public key is required.", "rsa-key-required": "RSA public key is required.",
"lwm2m-value": "LwM2M Security config",
"lwm2m-security-config": { "lwm2m-security-config": {
"identity": "Client Identity", "identity": "Client Identity",
"identity-required": "Client Identity is required.", "identity-required": "Client Identity is required.",
@ -971,7 +971,6 @@
"client-secret-key-required": "Client Secret Key is required.", "client-secret-key-required": "Client Secret Key is required.",
"client-secret-key-pattern": "Client Secret Key must be hexadecimal format.", "client-secret-key-pattern": "Client Secret Key must be hexadecimal format.",
"client-secret-key-length": "Client Secret Key must be {{ count }} characters.", "client-secret-key-length": "Client Secret Key must be {{ count }} characters.",
"config-json-tab": "Json Client Security Config",
"client-public-key": "Client public key", "client-public-key": "Client public key",
"client-public-key-hint": "If client public key is empty, the trusted certificate will be used" "client-public-key-hint": "If client public key is empty, the trusted certificate will be used"
}, },

Loading…
Cancel
Save