Commit ccb04f34a1dd5af8cfa8a30bfd10ad9a449b6c3c

Authored by Igor Kulikov
Committed by GitHub
2 parents 786439f9 5a3dfec7

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

UI: Improvement device credentials
Showing 19 changed files with 388 additions and 200 deletions
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m-server.component.html renamed from 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.ts renamed from ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts
... ... @@ -39,24 +39,24 @@ import { takeUntil } from 'rxjs/operators';
39 39 import { Subject } from 'rxjs';
40 40
41 41 @Component({
42   - selector: 'tb-security-config-lwm2m-server',
43   - templateUrl: './security-config-lwm2m-server.component.html',
  42 + selector: 'tb-device-credentials-lwm2m-server',
  43 + templateUrl: './device-credentials-lwm2m-server.component.html',
44 44 styleUrls: [],
45 45 providers: [
46 46 {
47 47 provide: NG_VALUE_ACCESSOR,
48   - useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent),
  48 + useExisting: forwardRef(() => DeviceCredentialsLwm2mServerComponent),
49 49 multi: true
50 50 },
51 51 {
52 52 provide: NG_VALIDATORS,
53   - useExisting: forwardRef(() => SecurityConfigLwm2mServerComponent),
  53 + useExisting: forwardRef(() => DeviceCredentialsLwm2mServerComponent),
54 54 multi: true
55 55 }
56 56 ]
57 57 })
58 58
59   -export class SecurityConfigLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator {
  59 +export class DeviceCredentialsLwm2mServerComponent implements OnDestroy, ControlValueAccessor, Validator {
60 60
61 61 serverFormGroup: FormGroup;
62 62 securityConfigLwM2MType = Lwm2mSecurityType;
... ...
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.html renamed from ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.html
... ... @@ -15,8 +15,8 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<mat-tab-group>
19   - <mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}" [formGroup]="lwm2mConfigFormGroup">
  18 +<mat-tab-group [formGroup]="lwm2mConfigFormGroup">
  19 + <mat-tab label="{{ 'device.lwm2m-security-config.client-tab' | translate }}">
20 20 <ng-container formGroupName="client">
21 21 <mat-form-field class="mat-block">
22 22 <mat-label translate>device.lwm2m-security-config.endpoint</mat-label>
... ... @@ -79,7 +79,7 @@
79 79 </mat-form-field>
80 80 </ng-container>
81 81 </mat-tab>
82   - <mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}" [formGroup]="lwm2mConfigFormGroup">
  82 + <mat-tab label="{{ 'device.lwm2m-security-config.bootstrap-tab' | translate }}">
83 83 <div style="padding: 2px;" formGroupName="bootstrap">
84 84 <mat-accordion multi="true" class="mat-body-1">
85 85 <mat-expansion-panel>
... ... @@ -89,9 +89,9 @@
89 89 </mat-panel-title>
90 90 </mat-expansion-panel-header>
91 91 <ng-template matExpansionPanelContent>
92   - <tb-security-config-lwm2m-server
  92 + <tb-device-credentials-lwm2m-server
93 93 formControlName="bootstrapServer">
94   - </tb-security-config-lwm2m-server>
  94 + </tb-device-credentials-lwm2m-server>
95 95 </ng-template>
96 96 </mat-expansion-panel>
97 97 <mat-expansion-panel>
... ... @@ -101,23 +101,12 @@
101 101 </mat-panel-title>
102 102 </mat-expansion-panel-header>
103 103 <ng-template matExpansionPanelContent>
104   - <tb-security-config-lwm2m-server
  104 + <tb-device-credentials-lwm2m-server
105 105 formControlName="lwm2mServer">
106   - </tb-security-config-lwm2m-server>
  106 + </tb-device-credentials-lwm2m-server>
107 107 </ng-template>
108 108 </mat-expansion-panel>
109 109 </mat-accordion>
110 110 </div>
111 111 </mat-tab>
112   - <mat-tab label="{{ 'device.lwm2m-security-config.config-json-tab' | translate }}">
113   - <ng-template matTabContent>
114   - <tb-json-object-edit
115   - [ngModel]="lwm2mConfigFormGroup.value"
116   - label="{{ 'device.lwm2m-value' | translate }}"
117   - readonly
118   - [required]="true"
119   - [fillHeight]="false">
120   - </tb-json-object-edit>
121   - </ng-template>
122   - </mat-tab>
123 112 </mat-tab-group>
... ...
ui-ngx/src/app/modules/home/components/device/device-credentials-lwm2m.component.scss renamed from 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.ts renamed from ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.ts
... ... @@ -40,24 +40,24 @@ import { takeUntil } from 'rxjs/operators';
40 40 import { isDefinedAndNotNull } from '@core/utils';
41 41
42 42 @Component({
43   - selector: 'tb-security-config-lwm2m',
44   - templateUrl: './security-config-lwm2m.component.html',
45   - styleUrls: ['./security-config-lwm2m.component.scss'],
  43 + selector: 'tb-device-credentials-lwm2m',
  44 + templateUrl: './device-credentials-lwm2m.component.html',
  45 + styleUrls: ['./device-credentials-lwm2m.component.scss'],
46 46 providers: [
47 47 {
48 48 provide: NG_VALUE_ACCESSOR,
49   - useExisting: forwardRef(() => SecurityConfigLwm2mComponent),
  49 + useExisting: forwardRef(() => DeviceCredentialsLwm2mComponent),
50 50 multi: true
51 51 },
52 52 {
53 53 provide: NG_VALIDATORS,
54   - useExisting: forwardRef(() => SecurityConfigLwm2mComponent),
  54 + useExisting: forwardRef(() => DeviceCredentialsLwm2mComponent),
55 55 multi: true
56 56 }
57 57 ]
58 58 })
59 59
60   -export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Validator, OnDestroy {
  60 +export class DeviceCredentialsLwm2mComponent implements ControlValueAccessor, Validator, OnDestroy {
61 61
62 62 lwm2mConfigFormGroup: FormGroup;
63 63 securityConfigLwM2MType = Lwm2mSecurityType;
... ... @@ -89,6 +89,7 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid
89 89 this.lwm2mConfigFormGroup.disable({emitEvent: false});
90 90 } else {
91 91 this.lwm2mConfigFormGroup.enable({emitEvent: false});
  92 + this.securityConfigClientUpdateValidators(this.lwm2mConfigFormGroup.get('client.securityConfigClientMode').value);
92 93 }
93 94 }
94 95
... ... @@ -126,21 +127,21 @@ export class SecurityConfigLwm2mComponent implements ControlValueAccessor, Valid
126 127 switch (mode) {
127 128 case Lwm2mSecurityType.NO_SEC:
128 129 this.setValidatorsNoSecX509();
129   - this.lwm2mConfigFormGroup.get('client.cert').disable();
  130 + this.lwm2mConfigFormGroup.get('client.cert').disable({emitEvent: false});
130 131 break;
131 132 case Lwm2mSecurityType.X509:
132 133 this.setValidatorsNoSecX509();
133   - this.lwm2mConfigFormGroup.get('client.cert').enable();
  134 + this.lwm2mConfigFormGroup.get('client.cert').enable({emitEvent: false});
134 135 break;
135 136 case Lwm2mSecurityType.PSK:
136 137 this.lenMaxKeyClient = LEN_MAX_PSK;
137 138 this.setValidatorsPskRpk(mode);
138   - this.lwm2mConfigFormGroup.get('client.identity').enable();
  139 + this.lwm2mConfigFormGroup.get('client.identity').enable({emitEvent: false});
139 140 break;
140 141 case Lwm2mSecurityType.RPK:
141 142 this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK;
142 143 this.setValidatorsPskRpk(mode);
143   - this.lwm2mConfigFormGroup.get('client.identity').disable();
  144 + this.lwm2mConfigFormGroup.get('client.identity').disable({emitEvent: false});
144 145 break;
145 146 }
146 147 this.lwm2mConfigFormGroup.get('client.identity').updateValueAndValidity({emitEvent: false});
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2021 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<section [formGroup]="deviceCredentialsMqttFormGroup">
  19 + <mat-form-field class="mat-block">
  20 + <mat-label translate>device.client-id</mat-label>
  21 + <input matInput formControlName="clientId">
  22 + <mat-error *ngIf="deviceCredentialsMqttFormGroup.get('clientId').hasError('pattern')">
  23 + {{ 'device.client-id-pattern' | translate }}
  24 + </mat-error>
  25 + </mat-form-field>
  26 + <mat-form-field class="mat-block">
  27 + <mat-label translate>device.user-name</mat-label>
  28 + <input matInput formControlName="userName" [required]="!!deviceCredentialsMqttFormGroup.get('password').value">
  29 + <mat-error *ngIf="deviceCredentialsMqttFormGroup.get('userName').hasError('required')">
  30 + {{ 'device.user-name-required' | translate }}
  31 + </mat-error>
  32 + </mat-form-field>
  33 + <mat-form-field class="mat-block">
  34 + <mat-label translate>device.password</mat-label>
  35 + <input matInput formControlName="password"
  36 + autocomplete="new-password"
  37 + (ngModelChange)="passwordChanged()"
  38 + [type]="hidePassword ? 'password' : 'text'">
  39 + <button mat-icon-button matSuffix type="button"
  40 + (click)="hidePassword = !hidePassword"
  41 + [attr.aria-pressed]="hidePassword">
  42 + <mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>
  43 + </button>
  44 + </mat-form-field>
  45 + <tb-error style="margin-top: -12px; display: block;"
  46 + [error]="deviceCredentialsMqttFormGroup.hasError('atLeastOne') ?
  47 + ('device.client-id-or-user-name-necessary' | translate) : ''"></tb-error>
  48 +</section>
... ...
  1 +///
  2 +/// Copyright © 2016-2021 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
  18 +import {
  19 + ControlValueAccessor,
  20 + FormBuilder,
  21 + FormGroup,
  22 + NG_VALIDATORS,
  23 + NG_VALUE_ACCESSOR,
  24 + ValidationErrors,
  25 + Validator,
  26 + ValidatorFn,
  27 + Validators
  28 +} from '@angular/forms';
  29 +import { Subject } from 'rxjs';
  30 +import { DeviceCredentialMQTTBasic } from '@shared/models/device.models';
  31 +import { takeUntil } from 'rxjs/operators';
  32 +import { isDefinedAndNotNull, isEmptyStr } from '@core/utils';
  33 +
  34 +@Component({
  35 + selector: 'tb-device-credentials-mqtt-basic',
  36 + templateUrl: './device-credentials-mqtt-basic.component.html',
  37 + providers: [
  38 + {
  39 + provide: NG_VALUE_ACCESSOR,
  40 + useExisting: forwardRef(() => DeviceCredentialsMqttBasicComponent),
  41 + multi: true
  42 + },
  43 + {
  44 + provide: NG_VALIDATORS,
  45 + useExisting: forwardRef(() => DeviceCredentialsMqttBasicComponent),
  46 + multi: true,
  47 + }],
  48 + styleUrls: []
  49 +})
  50 +export class DeviceCredentialsMqttBasicComponent implements ControlValueAccessor, Validator, OnDestroy {
  51 +
  52 + @Input()
  53 + disabled: boolean;
  54 +
  55 + deviceCredentialsMqttFormGroup: FormGroup;
  56 +
  57 + hidePassword = true;
  58 +
  59 + private destroy$ = new Subject();
  60 + private propagateChange = (v: any) => {};
  61 +
  62 + constructor(public fb: FormBuilder) {
  63 + this.deviceCredentialsMqttFormGroup = this.fb.group({
  64 + clientId: [null, [Validators.pattern(/^[A-Za-z0-9]+$/)]],
  65 + userName: [null],
  66 + password: [null]
  67 + }, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])});
  68 + this.deviceCredentialsMqttFormGroup.valueChanges.pipe(
  69 + takeUntil(this.destroy$)
  70 + ).subscribe((value) => {
  71 + this.updateView(value);
  72 + });
  73 + }
  74 +
  75 + ngOnDestroy(): void {
  76 + this.destroy$.next();
  77 + this.destroy$.complete();
  78 + }
  79 +
  80 + registerOnChange(fn: any): void {
  81 + this.propagateChange = fn;
  82 + }
  83 +
  84 + registerOnTouched(fn: any): void {}
  85 +
  86 + setDisabledState(isDisabled: boolean) {
  87 + this.disabled = isDisabled;
  88 + if (this.disabled) {
  89 + this.deviceCredentialsMqttFormGroup.disable({emitEvent: false});
  90 + } else {
  91 + this.deviceCredentialsMqttFormGroup.enable({emitEvent: false});
  92 + }
  93 + }
  94 +
  95 + validate(): ValidationErrors | null {
  96 + return this.deviceCredentialsMqttFormGroup.valid ? null : {
  97 + deviceCredentialsMqttBasic: false
  98 + };
  99 + }
  100 +
  101 + writeValue(mqttBasic: string) {
  102 + if (isDefinedAndNotNull(mqttBasic) && !isEmptyStr(mqttBasic)) {
  103 + const value = JSON.parse(mqttBasic);
  104 + this.deviceCredentialsMqttFormGroup.patchValue(value, {emitEvent: false});
  105 + }
  106 + }
  107 +
  108 + updateView(value: DeviceCredentialMQTTBasic) {
  109 + const formValue = JSON.stringify(value);
  110 + this.propagateChange(formValue);
  111 + }
  112 +
  113 + passwordChanged() {
  114 + const value = this.deviceCredentialsMqttFormGroup.get('password').value;
  115 + if (value !== '') {
  116 + this.deviceCredentialsMqttFormGroup.get('userName').setValidators([Validators.required]);
  117 + } else {
  118 + this.deviceCredentialsMqttFormGroup.get('userName').setValidators([]);
  119 + }
  120 + this.deviceCredentialsMqttFormGroup.get('userName').updateValueAndValidity({emitEvent: false});
  121 + }
  122 +
  123 + private atLeastOne(validator: ValidatorFn, controls: string[] = null) {
  124 + return (group: FormGroup): ValidationErrors | null => {
  125 + if (!controls) {
  126 + controls = Object.keys(group.controls);
  127 + }
  128 + const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k]));
  129 +
  130 + return hasAtLeastOne ? null : {atLeastOne: true};
  131 + };
  132 + }
  133 +}
... ...
... ... @@ -16,7 +16,7 @@
16 16
17 17 -->
18 18 <section [formGroup]="deviceCredentialsFormGroup">
19   - <mat-form-field class="mat-block">
  19 + <mat-form-field class="mat-block" [fxShow]="credentialsTypes?.length > 1">
20 20 <mat-label translate>device.credentials-type</mat-label>
21 21 <mat-select formControlName="credentialsType">
22 22 <mat-option *ngFor="let credentialsType of credentialsTypes" [value]="credentialsType">
... ... @@ -24,58 +24,35 @@
24 24 </mat-option>
25 25 </mat-select>
26 26 </mat-form-field>
27   - <mat-form-field *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.ACCESS_TOKEN"
28   - class="mat-block">
29   - <mat-label translate>device.access-token</mat-label>
30   - <input matInput formControlName="credentialsId" required>
31   - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')">
32   - {{ 'device.access-token-required' | translate }}
33   - </mat-error>
34   - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('pattern')">
35   - {{ 'device.access-token-invalid' | translate }}
36   - </mat-error>
37   - </mat-form-field>
38   - <mat-form-field *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.X509_CERTIFICATE"
39   - class="mat-block">
40   - <mat-label translate>device.rsa-key</mat-label>
41   - <textarea matInput formControlName="credentialsValue" cols="15" rows="5" required></textarea>
42   - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">
43   - {{ 'device.rsa-key-required' | translate }}
44   - </mat-error>
45   - </mat-form-field>
46   - <section *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.MQTT_BASIC" formGroupName="credentialsBasic">
47   - <mat-form-field class="mat-block">
48   - <mat-label translate>device.client-id</mat-label>
49   - <input matInput formControlName="clientId">
50   - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsBasic.clientId').hasError('pattern')">
51   - {{ 'device.client-id-pattern' | translate }}
52   - </mat-error>
53   - </mat-form-field>
54   - <mat-form-field class="mat-block">
55   - <mat-label translate>device.user-name</mat-label>
56   - <input matInput formControlName="userName" [required]="!!deviceCredentialsFormGroup.get('credentialsBasic.password').value">
57   - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsBasic.userName').hasError('required')">
58   - {{ 'device.user-name-required' | translate }}
59   - </mat-error>
60   - </mat-form-field>
61   - <mat-form-field class="mat-block">
62   - <mat-label translate>device.password</mat-label>
63   - <input matInput formControlName="password"
64   - autocomplete="new-password"
65   - (ngModelChange)="passwordChanged()"
66   - [type]="hidePassword ? 'password' : 'text'">
67   - <button mat-icon-button matSuffix type="button"
68   - (click)="hidePassword = !hidePassword"
69   - [attr.aria-pressed]="hidePassword">
70   - <mat-icon>{{hidePassword ? 'visibility_off' : 'visibility'}}</mat-icon>
71   - </button>
72   - </mat-form-field>
73   - <tb-error style="margin-top: -12px; display: block;"
74   - [error]="deviceCredentialsFormGroup.get('credentialsBasic').hasError('atLeastOne') ?
75   - ('device.client-id-or-user-name-necessary' | translate) : ''"></tb-error>
76   - </section>
77   - <div *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.LWM2M_CREDENTIALS">
78   - <tb-security-config-lwm2m formControlName="credentialsValue">
79   - </tb-security-config-lwm2m>
  27 + <div [ngSwitch]="deviceCredentialsFormGroup.get('credentialsType').value">
  28 + <ng-template [ngSwitchCase]="deviceCredentialsType.ACCESS_TOKEN">
  29 + <mat-form-field class="mat-block">
  30 + <mat-label translate>device.access-token</mat-label>
  31 + <input matInput formControlName="credentialsId" required>
  32 + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')">
  33 + {{ 'device.access-token-required' | translate }}
  34 + </mat-error>
  35 + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('pattern')">
  36 + {{ 'device.access-token-invalid' | translate }}
  37 + </mat-error>
  38 + </mat-form-field>
  39 + </ng-template>
  40 + <ng-template [ngSwitchCase]="deviceCredentialsType.X509_CERTIFICATE">
  41 + <mat-form-field class="mat-block">
  42 + <mat-label translate>device.rsa-key</mat-label>
  43 + <textarea matInput formControlName="credentialsValue" cols="15" rows="5" required></textarea>
  44 + <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">
  45 + {{ 'device.rsa-key-required' | translate }}
  46 + </mat-error>
  47 + </mat-form-field>
  48 + </ng-template>
  49 + <ng-template [ngSwitchCase]="deviceCredentialsType.MQTT_BASIC">
  50 + <tb-device-credentials-mqtt-basic formControlName="credentialsValue">
  51 + </tb-device-credentials-mqtt-basic>
  52 + </ng-template>
  53 + <ng-template [ngSwitchCase]="deviceCredentialsType.LWM2M_CREDENTIALS">
  54 + <tb-device-credentials-lwm2m formControlName="credentialsValue">
  55 + </tb-device-credentials-lwm2m>
  56 + </ng-template>
80 57 </div>
81 58 </section>
... ...
... ... @@ -22,16 +22,15 @@ import {
22 22 FormGroup,
23 23 NG_VALIDATORS,
24 24 NG_VALUE_ACCESSOR,
25   - ValidationErrors,
26 25 Validator,
27   - ValidatorFn,
28 26 Validators
29 27 } from '@angular/forms';
30 28 import {
31 29 credentialTypeNames,
32   - DeviceCredentialMQTTBasic,
  30 + credentialTypesByTransportType,
33 31 DeviceCredentials,
34   - DeviceCredentialsType
  32 + DeviceCredentialsType,
  33 + DeviceTransportType
35 34 } from '@shared/models/device.models';
36 35 import { Subject } from 'rxjs';
37 36 import { takeUntil } from 'rxjs/operators';
... ... @@ -58,32 +57,40 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
58 57 @Input()
59 58 disabled: boolean;
60 59
  60 + private deviceTransportTypeValue = DeviceTransportType.DEFAULT;
  61 + get deviceTransportType(): DeviceTransportType {
  62 + return this.deviceTransportTypeValue;
  63 + }
  64 + @Input()
  65 + set deviceTransportType(type: DeviceTransportType) {
  66 + if (type) {
  67 + this.deviceTransportTypeValue = type;
  68 + this.credentialsTypes = credentialTypesByTransportType.get(type);
  69 + const currentType = this.deviceCredentialsFormGroup.get('credentialsType').value;
  70 + if (!this.credentialsTypes.includes(currentType)) {
  71 + this.deviceCredentialsFormGroup.get('credentialsType').patchValue(this.credentialsTypes[0], {onlySelf: true});
  72 + }
  73 + }
  74 + }
  75 +
61 76 private destroy$ = new Subject();
62 77
63 78 deviceCredentialsFormGroup: FormGroup;
64 79
65 80 deviceCredentialsType = DeviceCredentialsType;
66 81
67   - credentialsTypes = Object.values(DeviceCredentialsType);
  82 + credentialsTypes = credentialTypesByTransportType.get(DeviceTransportType.DEFAULT);
68 83
69 84 credentialTypeNamesMap = credentialTypeNames;
70 85
71   - hidePassword = true;
72   -
73 86 private propagateChange = (v: any) => {};
74 87
75 88 constructor(public fb: FormBuilder) {
76 89 this.deviceCredentialsFormGroup = this.fb.group({
77 90 credentialsType: [DeviceCredentialsType.ACCESS_TOKEN],
78 91 credentialsId: [null],
79   - credentialsValue: [null],
80   - credentialsBasic: this.fb.group({
81   - clientId: [null, [Validators.pattern(/^[A-Za-z0-9]+$/)]],
82   - userName: [null],
83   - password: [null]
84   - }, {validators: this.atLeastOne(Validators.required, ['clientId', 'userName'])})
  92 + credentialsValue: [null]
85 93 });
86   - this.deviceCredentialsFormGroup.get('credentialsBasic').disable();
87 94 this.deviceCredentialsFormGroup.valueChanges.pipe(
88 95 takeUntil(this.destroy$)
89 96 ).subscribe(() => {
... ... @@ -109,18 +116,11 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
109 116
110 117 writeValue(value: DeviceCredentials | null): void {
111 118 if (isDefinedAndNotNull(value)) {
112   - let credentialsBasic = {clientId: null, userName: null, password: null};
113   - let credentialsValue = null;
114   - if (value.credentialsType === DeviceCredentialsType.MQTT_BASIC) {
115   - credentialsBasic = JSON.parse(value.credentialsValue) as DeviceCredentialMQTTBasic;
116   - } else {
117   - credentialsValue = value.credentialsValue;
118   - }
  119 + const credentialsType = this.credentialsTypes.includes(value.credentialsType) ? value.credentialsType : this.credentialsTypes[0];
119 120 this.deviceCredentialsFormGroup.patchValue({
120   - credentialsType: value.credentialsType,
  121 + credentialsType,
121 122 credentialsId: value.credentialsId,
122   - credentialsValue,
123   - credentialsBasic
  123 + credentialsValue: value.credentialsValue
124 124 }, {emitEvent: false});
125 125 this.updateValidators();
126 126 }
... ... @@ -128,10 +128,6 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
128 128
129 129 updateView() {
130 130 const deviceCredentialsValue = this.deviceCredentialsFormGroup.value;
131   - if (deviceCredentialsValue.credentialsType === DeviceCredentialsType.MQTT_BASIC) {
132   - deviceCredentialsValue.credentialsValue = JSON.stringify(deviceCredentialsValue.credentialsBasic);
133   - }
134   - delete deviceCredentialsValue.credentialsBasic;
135 131 this.propagateChange(deviceCredentialsValue);
136 132 }
137 133
... ... @@ -163,14 +159,12 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
163 159 credentialsTypeChanged(): void {
164 160 this.deviceCredentialsFormGroup.patchValue({
165 161 credentialsId: null,
166   - credentialsValue: null,
167   - credentialsBasic: {clientId: '', userName: '', password: ''}
  162 + credentialsValue: null
168 163 });
169 164 this.updateValidators();
170 165 }
171 166
172 167 updateValidators(): void {
173   - this.hidePassword = true;
174 168 const credentialsType = this.deviceCredentialsFormGroup.get('credentialsType').value as DeviceCredentialsType;
175 169 switch (credentialsType) {
176 170 case DeviceCredentialsType.ACCESS_TOKEN:
... ... @@ -178,48 +172,13 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit,
178 172 this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
179 173 this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]);
180 174 this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
181   - this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false});
182 175 break;
183   - case DeviceCredentialsType.X509_CERTIFICATE:
184   - case DeviceCredentialsType.LWM2M_CREDENTIALS:
  176 + default:
185 177 this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([Validators.required]);
186 178 this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
187 179 this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]);
188 180 this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
189   - this.deviceCredentialsFormGroup.get('credentialsBasic').disable({emitEvent: false});
190   - break;
191   - case DeviceCredentialsType.MQTT_BASIC:
192   - this.deviceCredentialsFormGroup.get('credentialsBasic').enable({emitEvent: false});
193   - this.deviceCredentialsFormGroup.get('credentialsBasic').updateValueAndValidity({emitEvent: false});
194   - this.deviceCredentialsFormGroup.get('credentialsId').setValidators([]);
195   - this.deviceCredentialsFormGroup.get('credentialsId').updateValueAndValidity({emitEvent: false});
196   - this.deviceCredentialsFormGroup.get('credentialsValue').setValidators([]);
197   - this.deviceCredentialsFormGroup.get('credentialsValue').updateValueAndValidity({emitEvent: false});
198 181 break;
199 182 }
200 183 }
201   -
202   - private atLeastOne(validator: ValidatorFn, controls: string[] = null) {
203   - return (group: FormGroup): ValidationErrors | null => {
204   - if (!controls) {
205   - controls = Object.keys(group.controls);
206   - }
207   - const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k]));
208   -
209   - return hasAtLeastOne ? null : {atLeastOne: true};
210   - };
211   - }
212   -
213   - passwordChanged() {
214   - const value = this.deviceCredentialsFormGroup.get('credentialsBasic.password').value;
215   - if (value !== '') {
216   - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').setValidators([Validators.required]);
217   - } else {
218   - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').setValidators([]);
219   - }
220   - this.deviceCredentialsFormGroup.get('credentialsBasic.userName').updateValueAndValidity({
221   - emitEvent: false,
222   - onlySelf: true
223   - });
224   - }
225 184 }
... ...
  1 +///
  2 +/// Copyright © 2016-2021 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { NgModule } from '@angular/core';
  18 +import { CommonModule } from '@angular/common';
  19 +import { SharedModule } from '@shared/shared.module';
  20 +import { CopyDeviceCredentialsComponent } from './copy-device-credentials.component';
  21 +import { DeviceCredentialsComponent } from './device-credentials.component';
  22 +import { DeviceCredentialsLwm2mComponent } from './device-credentials-lwm2m.component';
  23 +import { DeviceCredentialsLwm2mServerComponent } from './device-credentials-lwm2m-server.component';
  24 +import { DeviceCredentialsMqttBasicComponent } from './device-credentials-mqtt-basic.component';
  25 +
  26 +@NgModule({
  27 + declarations: [
  28 + CopyDeviceCredentialsComponent,
  29 + DeviceCredentialsComponent,
  30 + DeviceCredentialsLwm2mComponent,
  31 + DeviceCredentialsLwm2mServerComponent,
  32 + DeviceCredentialsMqttBasicComponent
  33 + ],
  34 + imports: [
  35 + CommonModule,
  36 + SharedModule
  37 + ],
  38 + exports: [
  39 + CopyDeviceCredentialsComponent,
  40 + DeviceCredentialsComponent,
  41 + DeviceCredentialsLwm2mComponent,
  42 + DeviceCredentialsLwm2mServerComponent,
  43 + DeviceCredentialsMqttBasicComponent
  44 + ]
  45 +})
  46 +export class DeviceCredentialsModule { }
... ...
... ... @@ -110,7 +110,6 @@ import { RuleChainAutocompleteComponent } from '@home/components/rule-chain/rule
110 110 import { DeviceProfileProvisionConfigurationComponent } from '@home/components/profile/device-profile-provision-configuration.component';
111 111 import { AlarmScheduleComponent } from '@home/components/profile/alarm/alarm-schedule.component';
112 112 import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component';
113   -import { DeviceCredentialsComponent } from '@home/components/device/device-credentials.component';
114 113 import { AlarmScheduleInfoComponent } from '@home/components/profile/alarm/alarm-schedule-info.component';
115 114 import { AlarmScheduleDialogComponent } from '@home/components/profile/alarm/alarm-schedule-dialog.component';
116 115 import { EditAlarmDetailsDialogComponent } from '@home/components/profile/alarm/edit-alarm-details-dialog.component';
... ... @@ -120,7 +119,6 @@ import { TenantProfileConfigurationComponent } from '@home/components/profile/te
120 119 import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component';
121 120 import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component';
122 121 import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component';
123   -import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-device-credentials.component';
124 122 import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lwm2m/lwm2m-profile-components.module';
125 123 import { DashboardPageComponent } from '@home/components/dashboard-page/dashboard-page.component';
126 124 import { DashboardToolbarComponent } from '@home/components/dashboard-page/dashboard-toolbar.component';
... ... @@ -139,11 +137,10 @@ import { EdgeDownlinkTableComponent } from '@home/components/edge/edge-downlink-
139 137 import { EdgeDownlinkTableHeaderComponent } from '@home/components/edge/edge-downlink-table-header.component';
140 138 import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-page/widget-types-panel.component';
141 139 import { AlarmDurationPredicateValueComponent } from '@home/components/profile/alarm/alarm-duration-predicate-value.component';
142   -import { SecurityConfigLwm2mComponent } from '@home/components/device/security-config-lwm2m.component';
143   -import { SecurityConfigLwm2mServerComponent } from '@home/components/device/security-config-lwm2m-server.component';
144 140 import { DashboardImageDialogComponent } from '@home/components/dashboard-page/dashboard-image-dialog.component';
145 141 import { WidgetContainerComponent } from '@home/components/widget/widget-container.component';
146 142 import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module';
  143 +import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module';
147 144
148 145 @NgModule({
149 146 declarations:
... ... @@ -245,10 +242,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
245 242 AlarmScheduleComponent,
246 243 AlarmDurationPredicateValueComponent,
247 244 DeviceWizardDialogComponent,
248   - DeviceCredentialsComponent,
249   - CopyDeviceCredentialsComponent,
250   - SecurityConfigLwm2mComponent,
251   - SecurityConfigLwm2mServerComponent,
252 245 AlarmScheduleDialogComponent,
253 246 EditAlarmDetailsDialogComponent,
254 247 SmsProviderConfigurationComponent,
... ... @@ -274,7 +267,8 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
274 267 SharedHomeComponentsModule,
275 268 Lwm2mProfileComponentsModule,
276 269 SnmpDeviceProfileTransportModule,
277   - StatesControllerModule
  270 + StatesControllerModule,
  271 + DeviceCredentialsModule
278 272 ],
279 273 exports: [
280 274 EntitiesTableComponent,
... ... @@ -353,10 +347,6 @@ import { SnmpDeviceProfileTransportModule } from '@home/components/profile/devic
353 347 AddDeviceProfileDialogComponent,
354 348 RuleChainAutocompleteComponent,
355 349 DeviceWizardDialogComponent,
356   - DeviceCredentialsComponent,
357   - CopyDeviceCredentialsComponent,
358   - SecurityConfigLwm2mComponent,
359   - SecurityConfigLwm2mServerComponent,
360 350 AlarmScheduleInfoComponent,
361 351 AlarmScheduleComponent,
362 352 AlarmScheduleDialogComponent,
... ...
... ... @@ -64,7 +64,8 @@
64 64 [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}"
65 65 [addNewProfile]="false"
66 66 [selectDefaultProfile]="true"
67   - [editProfileEnabled]="false">
  67 + [editProfileEnabled]="false"
  68 + (deviceProfileChanged)="deviceProfileChanged($event)">
68 69 </tb-device-profile-autocomplete>
69 70 <mat-form-field fxFlex class="mat-block"
70 71 [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 1}">
... ... @@ -154,6 +155,7 @@
154 155 <mat-checkbox style="padding-bottom: 16px;" formControlName="setCredential">{{ 'device.wizard.add-credentials' | translate }}</mat-checkbox>
155 156 <tb-device-credentials
156 157 [fxShow]="credentialsFormGroup.get('setCredential').value"
  158 + [deviceTransportType]="deviceTransportType"
157 159 formControlName="credential">
158 160 </tb-device-credentials>
159 161 </form>
... ...
... ... @@ -25,6 +25,7 @@ import {
25 25 createDeviceProfileConfiguration,
26 26 createDeviceProfileTransportConfiguration,
27 27 DeviceProfile,
  28 + DeviceProfileInfo,
28 29 DeviceProfileType,
29 30 DeviceProvisionConfiguration,
30 31 DeviceProvisionType,
... ... @@ -91,6 +92,7 @@ export class DeviceWizardDialogComponent extends
91 92 serviceType = ServiceType.TB_RULE_ENGINE;
92 93
93 94 private subscriptions: Subscription[] = [];
  95 + private currentDeviceProfileTransportType = DeviceTransportType.DEFAULT;
94 96
95 97 constructor(protected store: Store<AppState>,
96 98 protected router: Router,
... ... @@ -265,6 +267,20 @@ export class DeviceWizardDialogComponent extends
265 267 }
266 268 }
267 269
  270 + get deviceTransportType(): DeviceTransportType {
  271 + if (this.deviceWizardFormGroup.get('addProfileType').value) {
  272 + return this.transportConfigFormGroup.get('transportType').value;
  273 + } else {
  274 + return this.currentDeviceProfileTransportType;
  275 + }
  276 + }
  277 +
  278 + deviceProfileChanged(deviceProfile: DeviceProfileInfo) {
  279 + if (deviceProfile) {
  280 + this.currentDeviceProfileTransportType = deviceProfile.transportType;
  281 + }
  282 + }
  283 +
268 284 private createDeviceProfile(): Observable<EntityId> {
269 285 if (this.deviceWizardFormGroup.get('addProfileType').value) {
270 286 const deviceProvisionConfiguration: DeviceProvisionConfiguration = this.provisionConfigFormGroup.get('provisionConfiguration').value;
... ...
... ... @@ -17,7 +17,7 @@
17 17 -->
18 18 <form [formGroup]="deviceCredentialsFormGroup" (ngSubmit)="save()" style="min-width: 350px;">
19 19 <mat-toolbar color="primary">
20   - <h2 translate>device.device-credentials</h2>
  20 + <h2>{{ 'device.device-credentials' | translate }}</h2>
21 21 <span fxFlex></span>
22 22 <button mat-icon-button
23 23 (click)="cancel()"
... ... @@ -25,15 +25,26 @@
25 25 <mat-icon class="material-icons">close</mat-icon>
26 26 </button>
27 27 </mat-toolbar>
28   - <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
  28 + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="(isLoading$ | async) && !loadingCredentials">
29 29 </mat-progress-bar>
30   - <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
  30 + <div style="height: 4px;" *ngIf="!(isLoading$ | async) || loadingCredentials"></div>
31 31 <div mat-dialog-content>
32   - <fieldset [disabled]="(isLoading$ | async) || isReadOnly">
33   - <tb-device-credentials
34   - formControlName="credential">
35   - </tb-device-credentials>
36   - </fieldset>
  32 + <section *ngIf="!loadingCredentials; else loadCredentials">
  33 + <fieldset [disabled]="(isLoading$ | async) || isReadOnly">
  34 + <tb-device-credentials
  35 + [deviceTransportType]="deviceTransportType"
  36 + formControlName="credential">
  37 + </tb-device-credentials>
  38 + </fieldset>
  39 + </section>
  40 + <ng-template #loadCredentials>
  41 + <div fxLayout="column" fxLayoutAlign="center center">
  42 + <mat-spinner color="accent" diameter="65" strokeWidth="4" style="margin-bottom: 18px"></mat-spinner>
  43 + <span class="mat-subheading-2" style="margin-bottom: 0">
  44 + {{ 'device.loading-device-credentials' | translate }}
  45 + </span>
  46 + </div>
  47 + </ng-template>
37 48 </div>
38 49 <div mat-dialog-actions fxLayoutAlign="end center">
39 50 <button mat-button color="primary"
... ...
... ... @@ -21,13 +21,16 @@ import { Store } from '@ngrx/store';
21 21 import { AppState } from '@core/core.state';
22 22 import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
23 23 import { DeviceService } from '@core/http/device.service';
24   -import { credentialTypeNames, DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models';
  24 +import { DeviceCredentials, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models';
25 25 import { DialogComponent } from '@shared/components/dialog.component';
26 26 import { Router } from '@angular/router';
  27 +import { DeviceProfileService } from '@core/http/device-profile.service';
  28 +import { forkJoin } from 'rxjs';
27 29
28 30 export interface DeviceCredentialsDialogData {
29 31 isReadOnly: boolean;
30 32 deviceId: string;
  33 + deviceProfileId: string;
31 34 }
32 35
33 36 @Component({
... ... @@ -40,25 +43,18 @@ export class DeviceCredentialsDialogComponent extends
40 43 DialogComponent<DeviceCredentialsDialogComponent, DeviceCredentials> implements OnInit, ErrorStateMatcher {
41 44
42 45 deviceCredentialsFormGroup: FormGroup;
43   -
  46 + deviceTransportType: DeviceTransportType;
44 47 isReadOnly: boolean;
  48 + loadingCredentials = true;
45 49
46   - deviceCredentials: DeviceCredentials;
47   -
48   - submitted = false;
49   -
50   - deviceCredentialsType = DeviceCredentialsType;
51   -
52   - credentialsTypes = Object.keys(DeviceCredentialsType);
53   -
54   - credentialTypeNamesMap = credentialTypeNames;
55   -
56   - hidePassword = true;
  50 + private deviceCredentials: DeviceCredentials;
  51 + private submitted = false;
57 52
58 53 constructor(protected store: Store<AppState>,
59 54 protected router: Router,
60 55 @Inject(MAT_DIALOG_DATA) public data: DeviceCredentialsDialogData,
61 56 private deviceService: DeviceService,
  57 + private deviceProfileService: DeviceProfileService,
62 58 @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
63 59 public dialogRef: MatDialogRef<DeviceCredentialsDialogComponent, DeviceCredentials>,
64 60 public fb: FormBuilder) {
... ... @@ -84,14 +80,17 @@ export class DeviceCredentialsDialogComponent extends
84 80 }
85 81
86 82 loadDeviceCredentials() {
87   - this.deviceService.getDeviceCredentials(this.data.deviceId).subscribe(
88   - (deviceCredentials) => {
89   - this.deviceCredentials = deviceCredentials;
90   - this.deviceCredentialsFormGroup.patchValue({
91   - credential: deviceCredentials
92   - }, {emitEvent: false});
93   - }
94   - );
  83 + const task = [];
  84 + task.push(this.deviceService.getDeviceCredentials(this.data.deviceId));
  85 + task.push(this.deviceProfileService.getDeviceProfileInfo(this.data.deviceProfileId));
  86 + forkJoin(task).subscribe(([deviceCredentials, deviceProfile]: [DeviceCredentials, DeviceProfileInfo]) => {
  87 + this.deviceTransportType = deviceProfile.transportType;
  88 + this.deviceCredentials = deviceCredentials;
  89 + this.deviceCredentialsFormGroup.patchValue({
  90 + credential: deviceCredentials
  91 + }, {emitEvent: false});
  92 + this.loadingCredentials = false;
  93 + });
95 94 }
96 95
97 96 cancel(): void {
... ...
... ... @@ -33,6 +33,7 @@ import { MqttDeviceTransportConfigurationComponent } from './data/mqtt-device-tr
33 33 import { CoapDeviceTransportConfigurationComponent } from './data/coap-device-transport-configuration.component';
34 34 import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component';
35 35 import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-transport-configuration.component';
  36 +import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module';
36 37
37 38 @NgModule({
38 39 declarations: [
... ... @@ -55,6 +56,7 @@ import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-tr
55 56 SharedModule,
56 57 HomeComponentsModule,
57 58 HomeDialogsModule,
  59 + DeviceCredentialsModule,
58 60 DeviceRoutingModule
59 61 ]
60 62 })
... ...
... ... @@ -112,7 +112,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
112 112 ));
113 113 };
114 114 this.config.onEntityAction = action => this.onDeviceAction(action);
115   - this.config.detailsReadonly = () => (this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user');
  115 + this.config.detailsReadonly = () =>
  116 + (this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user');
116 117
117 118 this.config.headerComponent = DeviceTableHeaderComponent;
118 119
... ... @@ -528,6 +529,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
528 529 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
529 530 data: {
530 531 deviceId: device.id.id,
  532 + deviceProfileId: device.deviceProfileId.id,
531 533 isReadOnly: this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user'
532 534 }
533 535 }).afterClosed().subscribe(deviceCredentials => {
... ...
... ... @@ -682,6 +682,20 @@ export const credentialTypeNames = new Map<DeviceCredentialsType, string>(
682 682 ]
683 683 );
684 684
  685 +export const credentialTypesByTransportType = new Map<DeviceTransportType, DeviceCredentialsType[]>(
  686 + [
  687 + [DeviceTransportType.DEFAULT, [
  688 + DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE, DeviceCredentialsType.MQTT_BASIC
  689 + ]],
  690 + [DeviceTransportType.MQTT, [
  691 + DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE, DeviceCredentialsType.MQTT_BASIC
  692 + ]],
  693 + [DeviceTransportType.COAP, [DeviceCredentialsType.ACCESS_TOKEN, DeviceCredentialsType.X509_CERTIFICATE]],
  694 + [DeviceTransportType.LWM2M, [DeviceCredentialsType.LWM2M_CREDENTIALS]],
  695 + [DeviceTransportType.SNMP, [DeviceCredentialsType.ACCESS_TOKEN]]
  696 + ]
  697 +);
  698 +
685 699 export interface DeviceCredentials extends BaseData<DeviceCredentialsId> {
686 700 deviceId: DeviceId;
687 701 credentialsType: DeviceCredentialsType;
... ...
... ... @@ -941,13 +941,13 @@
941 941 "unassign-devices-title": "Are you sure you want to unassign { count, plural, 1 {1 device} other {# devices} }?",
942 942 "unassign-devices-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the customer.",
943 943 "device-credentials": "Device Credentials",
  944 + "loading-device-credentials": "Loading device credentials...",
944 945 "credentials-type": "Credentials type",
945 946 "access-token": "Access token",
946 947 "access-token-required": "Access token is required.",
947 948 "access-token-invalid": "Access token length must be from 1 to 20 characters.",
948 949 "rsa-key": "RSA public key",
949 950 "rsa-key-required": "RSA public key is required.",
950   - "lwm2m-value": "LwM2M Security config",
951 951 "lwm2m-security-config": {
952 952 "identity": "Client Identity",
953 953 "identity-required": "Client Identity is required.",
... ... @@ -971,7 +971,6 @@
971 971 "client-secret-key-required": "Client Secret Key is required.",
972 972 "client-secret-key-pattern": "Client Secret Key must be hexadecimal format.",
973 973 "client-secret-key-length": "Client Secret Key must be {{ count }} characters.",
974   - "config-json-tab": "Json Client Security Config",
975 974 "client-public-key": "Client public key",
976 975 "client-public-key-hint": "If client public key is empty, the trusted certificate will be used"
977 976 },
... ...