Commit 205be4e5cea74108564a6f4fd38973d1cb588713
1 parent
986cfe5c
UI: Added new component copy-device-credentials
Showing
7 changed files
with
179 additions
and
34 deletions
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 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 | +<button mat-raised-button | ||
19 | + ngxClipboard | ||
20 | + [cbContent]="credential" | ||
21 | + (cbOnSuccess)="onCopyCredential()" | ||
22 | + [fxHide]="hideButton" | ||
23 | + [disabled]="disabled || loading"> | ||
24 | + <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon> | ||
25 | + <span>{{ buttonLabel }}</span> | ||
26 | +</button> |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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, Input, OnDestroy, OnInit } from '@angular/core'; | ||
18 | +import { EntityId } from '@shared/models/id/entity-id'; | ||
19 | +import { DeviceService } from '@core/http/device.service'; | ||
20 | +import { DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; | ||
21 | +import { isDefinedAndNotNull, isEqual } from '@core/utils'; | ||
22 | +import { BehaviorSubject, Subject } from 'rxjs'; | ||
23 | +import { distinctUntilChanged, filter, mergeMap, tap } from 'rxjs/operators'; | ||
24 | +import { EntityType } from '@shared/models/entity-type.models'; | ||
25 | +import { ActionNotificationShow } from '@core/notification/notification.actions'; | ||
26 | +import { Store } from '@ngrx/store'; | ||
27 | +import { AppState } from '@core/core.state'; | ||
28 | +import { TranslateService } from '@ngx-translate/core'; | ||
29 | + | ||
30 | +@Component({ | ||
31 | + selector: 'tb-copy-device-credentials', | ||
32 | + templateUrl: './copy-device-credentials.component.html', | ||
33 | + styleUrls: [] | ||
34 | +}) | ||
35 | +export class CopyDeviceCredentialsComponent implements OnDestroy { | ||
36 | + | ||
37 | + private deviceId$ = new BehaviorSubject<EntityId>(null); | ||
38 | + | ||
39 | + private credentials$ = new Subject<DeviceCredentials>(); | ||
40 | + | ||
41 | + private tooltipMessage: string; | ||
42 | + | ||
43 | + public hideButton = true; | ||
44 | + | ||
45 | + public credential: string; | ||
46 | + | ||
47 | + public loading = false; | ||
48 | + | ||
49 | + public buttonLabel: string; | ||
50 | + | ||
51 | + @Input() | ||
52 | + set deviceId(deviceId: EntityId) { | ||
53 | + this.deviceId$.next(deviceId); | ||
54 | + } | ||
55 | + | ||
56 | + @Input() disabled: boolean; | ||
57 | + | ||
58 | + @Input() | ||
59 | + set credentials(credential: DeviceCredentials) { | ||
60 | + this.credentials$.next(credential); | ||
61 | + } | ||
62 | + | ||
63 | + constructor(private store: Store<AppState>, | ||
64 | + private translate: TranslateService, | ||
65 | + private deviceService: DeviceService | ||
66 | + ) { | ||
67 | + this.deviceId$.pipe( | ||
68 | + filter(device => isDefinedAndNotNull(device) && device.entityType === EntityType.DEVICE), | ||
69 | + distinctUntilChanged((prev, curr) => prev.id === curr.id), | ||
70 | + tap(() => this.loading = true), | ||
71 | + mergeMap(device => this.deviceService.getDeviceCredentials(device.id)) | ||
72 | + ).subscribe(deviceCredentials => { | ||
73 | + this.processingValue(deviceCredentials); | ||
74 | + this.loading = false; | ||
75 | + }); | ||
76 | + | ||
77 | + this.credentials$.pipe( | ||
78 | + filter(credential => isDefinedAndNotNull(credential)), | ||
79 | + distinctUntilChanged((prev, curr) => isEqual(prev, curr)) | ||
80 | + ).subscribe(deviceCredentials => { | ||
81 | + console.warn(deviceCredentials); | ||
82 | + this.processingValue(deviceCredentials); | ||
83 | + }); | ||
84 | + } | ||
85 | + | ||
86 | + ngOnDestroy(): void { | ||
87 | + this.deviceId$.unsubscribe(); | ||
88 | + this.credentials$.unsubscribe(); | ||
89 | + } | ||
90 | + | ||
91 | + private processingValue(credential: DeviceCredentials): void { | ||
92 | + switch (credential.credentialsType) { | ||
93 | + case DeviceCredentialsType.ACCESS_TOKEN: | ||
94 | + this.hideButton = false; | ||
95 | + this.credential = credential.credentialsId; | ||
96 | + this.buttonLabel = this.translate.instant('device.copyAccessToken'); | ||
97 | + this.tooltipMessage = this.translate.instant('device.accessTokenCopiedMessage'); | ||
98 | + break; | ||
99 | + case DeviceCredentialsType.MQTT_BASIC: | ||
100 | + this.hideButton = false; | ||
101 | + this.credential = this.convertObjectToString(JSON.parse(credential.credentialsValue)); | ||
102 | + this.buttonLabel = this.translate.instant('device.copy-mqtt-authentication'); | ||
103 | + this.tooltipMessage = this.translate.instant('device.mqtt-authentication-copied-message'); | ||
104 | + break; | ||
105 | + default: | ||
106 | + this.hideButton = true; | ||
107 | + this.credential = null; | ||
108 | + this.buttonLabel = ''; | ||
109 | + this.tooltipMessage = ''; | ||
110 | + } | ||
111 | + } | ||
112 | + | ||
113 | + private convertObjectToString(obj: object): string { | ||
114 | + Object.keys(obj).forEach(k => (!obj[k] && obj[k] !== undefined) && delete obj[k]); | ||
115 | + return JSON.stringify(obj).replace(/"([^"]+)":/g, '$1:'); | ||
116 | + } | ||
117 | + | ||
118 | + onCopyCredential() { | ||
119 | + this.store.dispatch(new ActionNotificationShow( | ||
120 | + { | ||
121 | + message: this.tooltipMessage, | ||
122 | + type: 'success', | ||
123 | + duration: 750, | ||
124 | + verticalPosition: 'bottom', | ||
125 | + horizontalPosition: 'right' | ||
126 | + })); | ||
127 | + } | ||
128 | +} |
@@ -105,7 +105,7 @@ import { AlarmRuleConditionComponent } from './profile/alarm/alarm-rule-conditio | @@ -105,7 +105,7 @@ import { AlarmRuleConditionComponent } from './profile/alarm/alarm-rule-conditio | ||
105 | import { FilterTextComponent } from './filter/filter-text.component'; | 105 | import { FilterTextComponent } from './filter/filter-text.component'; |
106 | import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component'; | 106 | import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component'; |
107 | import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomplete.component'; | 107 | import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomplete.component'; |
108 | -import { DeviceProfileProvisionConfigurationComponent } from "./profile/device-profile-provision-configuration.component"; | 108 | +import { DeviceProfileProvisionConfigurationComponent } from './profile/device-profile-provision-configuration.component'; |
109 | import { AlarmScheduleComponent } from './profile/alarm/alarm-schedule.component'; | 109 | import { AlarmScheduleComponent } from './profile/alarm/alarm-schedule.component'; |
110 | import { DeviceWizardDialogComponent } from './wizard/device-wizard-dialog.component'; | 110 | import { DeviceWizardDialogComponent } from './wizard/device-wizard-dialog.component'; |
111 | import { DeviceCredentialsComponent } from './device/device-credentials.component'; | 111 | import { DeviceCredentialsComponent } from './device/device-credentials.component'; |
@@ -115,6 +115,7 @@ import { EditAlarmDetailsDialogComponent } from './profile/alarm/edit-alarm-deta | @@ -115,6 +115,7 @@ import { EditAlarmDetailsDialogComponent } from './profile/alarm/edit-alarm-deta | ||
115 | import { AlarmRuleConditionDialogComponent } from '@home/components/profile/alarm/alarm-rule-condition-dialog.component'; | 115 | import { AlarmRuleConditionDialogComponent } from '@home/components/profile/alarm/alarm-rule-condition-dialog.component'; |
116 | import { DefaultTenantProfileConfigurationComponent } from './profile/tenant/default-tenant-profile-configuration.component'; | 116 | import { DefaultTenantProfileConfigurationComponent } from './profile/tenant/default-tenant-profile-configuration.component'; |
117 | import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-profile-configuration.component'; | 117 | import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-profile-configuration.component'; |
118 | +import { CopyDeviceCredentialsComponent } from './device/copy-device-credentials.component'; | ||
118 | 119 | ||
119 | @NgModule({ | 120 | @NgModule({ |
120 | declarations: | 121 | declarations: |
@@ -211,6 +212,7 @@ import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-pro | @@ -211,6 +212,7 @@ import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-pro | ||
211 | AlarmScheduleComponent, | 212 | AlarmScheduleComponent, |
212 | DeviceWizardDialogComponent, | 213 | DeviceWizardDialogComponent, |
213 | DeviceCredentialsComponent, | 214 | DeviceCredentialsComponent, |
215 | + CopyDeviceCredentialsComponent, | ||
214 | AlarmScheduleDialogComponent, | 216 | AlarmScheduleDialogComponent, |
215 | EditAlarmDetailsDialogComponent | 217 | EditAlarmDetailsDialogComponent |
216 | ], | 218 | ], |
@@ -293,6 +295,7 @@ import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-pro | @@ -293,6 +295,7 @@ import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-pro | ||
293 | RuleChainAutocompleteComponent, | 295 | RuleChainAutocompleteComponent, |
294 | DeviceWizardDialogComponent, | 296 | DeviceWizardDialogComponent, |
295 | DeviceCredentialsComponent, | 297 | DeviceCredentialsComponent, |
298 | + CopyDeviceCredentialsComponent, | ||
296 | AlarmScheduleInfoComponent, | 299 | AlarmScheduleInfoComponent, |
297 | AlarmScheduleComponent, | 300 | AlarmScheduleComponent, |
298 | AlarmScheduleDialogComponent, | 301 | AlarmScheduleDialogComponent, |
@@ -51,16 +51,17 @@ | @@ -51,16 +51,17 @@ | ||
51 | ngxClipboard | 51 | ngxClipboard |
52 | (cbOnSuccess)="onDeviceIdCopied($event)" | 52 | (cbOnSuccess)="onDeviceIdCopied($event)" |
53 | [cbContent]="entity?.id?.id" | 53 | [cbContent]="entity?.id?.id" |
54 | + [disabled]="(isLoading$ | async)" | ||
54 | [fxShow]="!isEdit"> | 55 | [fxShow]="!isEdit"> |
55 | <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon> | 56 | <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon> |
56 | <span translate>device.copyId</span> | 57 | <span translate>device.copyId</span> |
57 | </button> | 58 | </button> |
58 | - <button mat-raised-button | ||
59 | - (click)="copyAccessToken($event)" | ||
60 | - [fxShow]="!isEdit"> | ||
61 | - <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon> | ||
62 | - <span translate>device.copyAccessToken</span> | ||
63 | - </button> | 59 | + <tb-copy-device-credentials |
60 | + [fxShow]="!isEdit" | ||
61 | + [disabled]="(isLoading$ | async)" | ||
62 | + [credentials]="componentsData.deviceCredential" | ||
63 | + [deviceId]="entity?.id"> | ||
64 | + </tb-copy-device-credentials> | ||
64 | </div> | 65 | </div> |
65 | </div> | 66 | </div> |
66 | <div class="mat-padding" fxLayout="column"> | 67 | <div class="mat-padding" fxLayout="column"> |
@@ -21,10 +21,9 @@ import { EntityComponent } from '../../components/entity/entity.component'; | @@ -21,10 +21,9 @@ import { EntityComponent } from '../../components/entity/entity.component'; | ||
21 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; | 21 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
22 | import { | 22 | import { |
23 | createDeviceConfiguration, | 23 | createDeviceConfiguration, |
24 | - createDeviceProfileConfiguration, createDeviceTransportConfiguration, | 24 | + createDeviceTransportConfiguration, |
25 | DeviceData, | 25 | DeviceData, |
26 | DeviceInfo, | 26 | DeviceInfo, |
27 | - DeviceProfileData, | ||
28 | DeviceProfileInfo, | 27 | DeviceProfileInfo, |
29 | DeviceProfileType, | 28 | DeviceProfileType, |
30 | DeviceTransportType | 29 | DeviceTransportType |
@@ -33,8 +32,6 @@ import { EntityType } from '@shared/models/entity-type.models'; | @@ -33,8 +32,6 @@ import { EntityType } from '@shared/models/entity-type.models'; | ||
33 | import { NULL_UUID } from '@shared/models/id/has-uuid'; | 32 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
34 | import { ActionNotificationShow } from '@core/notification/notification.actions'; | 33 | import { ActionNotificationShow } from '@core/notification/notification.actions'; |
35 | import { TranslateService } from '@ngx-translate/core'; | 34 | import { TranslateService } from '@ngx-translate/core'; |
36 | -import { DeviceService } from '@core/http/device.service'; | ||
37 | -import { ClipboardService } from 'ngx-clipboard'; | ||
38 | import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; | 35 | import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
39 | 36 | ||
40 | @Component({ | 37 | @Component({ |
@@ -46,12 +43,12 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | @@ -46,12 +43,12 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | ||
46 | 43 | ||
47 | entityType = EntityType; | 44 | entityType = EntityType; |
48 | 45 | ||
46 | + componentsData: any; | ||
47 | + | ||
49 | deviceScope: 'tenant' | 'customer' | 'customer_user'; | 48 | deviceScope: 'tenant' | 'customer' | 'customer_user'; |
50 | 49 | ||
51 | constructor(protected store: Store<AppState>, | 50 | constructor(protected store: Store<AppState>, |
52 | protected translate: TranslateService, | 51 | protected translate: TranslateService, |
53 | - private deviceService: DeviceService, | ||
54 | - private clipboardService: ClipboardService, | ||
55 | @Inject('entity') protected entityValue: DeviceInfo, | 52 | @Inject('entity') protected entityValue: DeviceInfo, |
56 | @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<DeviceInfo>, | 53 | @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<DeviceInfo>, |
57 | public fb: FormBuilder) { | 54 | public fb: FormBuilder) { |
@@ -60,6 +57,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | @@ -60,6 +57,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | ||
60 | 57 | ||
61 | ngOnInit() { | 58 | ngOnInit() { |
62 | this.deviceScope = this.entitiesTableConfig.componentsData.deviceScope; | 59 | this.deviceScope = this.entitiesTableConfig.componentsData.deviceScope; |
60 | + this.componentsData = this.entitiesTableConfigValue.componentsData; | ||
63 | super.ngOnInit(); | 61 | super.ngOnInit(); |
64 | } | 62 | } |
65 | 63 | ||
@@ -114,26 +112,6 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | @@ -114,26 +112,6 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { | ||
114 | })); | 112 | })); |
115 | } | 113 | } |
116 | 114 | ||
117 | - copyAccessToken($event) { | ||
118 | - if (this.entity.id) { | ||
119 | - this.deviceService.getDeviceCredentials(this.entity.id.id, true).subscribe( | ||
120 | - (deviceCredentials) => { | ||
121 | - const credentialsId = deviceCredentials.credentialsId; | ||
122 | - if (this.clipboardService.copyFromContent(credentialsId)) { | ||
123 | - this.store.dispatch(new ActionNotificationShow( | ||
124 | - { | ||
125 | - message: this.translate.instant('device.accessTokenCopiedMessage'), | ||
126 | - type: 'success', | ||
127 | - duration: 750, | ||
128 | - verticalPosition: 'bottom', | ||
129 | - horizontalPosition: 'right' | ||
130 | - })); | ||
131 | - } | ||
132 | - } | ||
133 | - ); | ||
134 | - } | ||
135 | - } | ||
136 | - | ||
137 | onDeviceProfileUpdated() { | 115 | onDeviceProfileUpdated() { |
138 | this.entitiesTableConfig.table.updateData(false); | 116 | this.entitiesTableConfig.table.updateData(false); |
139 | } | 117 | } |
@@ -63,6 +63,7 @@ import { DeviceTabsComponent } from '@home/pages/device/device-tabs.component'; | @@ -63,6 +63,7 @@ import { DeviceTabsComponent } from '@home/pages/device/device-tabs.component'; | ||
63 | import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; | 63 | import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; |
64 | import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component'; | 64 | import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component'; |
65 | import { BaseData, HasId } from '@shared/models/base-data'; | 65 | import { BaseData, HasId } from '@shared/models/base-data'; |
66 | +import { isDefinedAndNotNull } from '@core/utils'; | ||
66 | 67 | ||
67 | @Injectable() | 68 | @Injectable() |
68 | export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<DeviceInfo>> { | 69 | export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<DeviceInfo>> { |
@@ -115,7 +116,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | @@ -115,7 +116,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | ||
115 | const routeParams = route.params; | 116 | const routeParams = route.params; |
116 | this.config.componentsData = { | 117 | this.config.componentsData = { |
117 | deviceScope: route.data.devicesType, | 118 | deviceScope: route.data.devicesType, |
118 | - deviceProfileId: null | 119 | + deviceProfileId: null, |
120 | + deviceCredential: null | ||
119 | }; | 121 | }; |
120 | this.customerId = routeParams.customerId; | 122 | this.customerId = routeParams.customerId; |
121 | return this.store.pipe(select(selectAuthUser), take(1)).pipe( | 123 | return this.store.pipe(select(selectAuthUser), take(1)).pipe( |
@@ -479,6 +481,11 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | @@ -479,6 +481,11 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev | ||
479 | deviceId: device.id.id, | 481 | deviceId: device.id.id, |
480 | isReadOnly: this.config.componentsData.deviceScope === 'customer_user' | 482 | isReadOnly: this.config.componentsData.deviceScope === 'customer_user' |
481 | } | 483 | } |
484 | + }).afterClosed().subscribe(deviceCredential => { | ||
485 | + if (isDefinedAndNotNull(deviceCredential)) { | ||
486 | + this.config.table.onEntityUpdated(device); | ||
487 | + this.config.componentsData.deviceCredential = deviceCredential; | ||
488 | + } | ||
482 | }); | 489 | }); |
483 | } | 490 | } |
484 | 491 |
@@ -814,8 +814,10 @@ | @@ -814,8 +814,10 @@ | ||
814 | "details": "Details", | 814 | "details": "Details", |
815 | "copyId": "Copy device Id", | 815 | "copyId": "Copy device Id", |
816 | "copyAccessToken": "Copy access token", | 816 | "copyAccessToken": "Copy access token", |
817 | + "copy-mqtt-authentication": "Copy MQTT authentication", | ||
817 | "idCopiedMessage": "Device Id has been copied to clipboard", | 818 | "idCopiedMessage": "Device Id has been copied to clipboard", |
818 | "accessTokenCopiedMessage": "Device access token has been copied to clipboard", | 819 | "accessTokenCopiedMessage": "Device access token has been copied to clipboard", |
820 | + "mqtt-authentication-copied-message": "Device MQTT authentication has been copied to clipboard", | ||
819 | "assignedToCustomer": "Assigned to customer", | 821 | "assignedToCustomer": "Assigned to customer", |
820 | "unable-delete-device-alias-title": "Unable to delete device alias", | 822 | "unable-delete-device-alias-title": "Unable to delete device alias", |
821 | "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}", | 823 | "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}", |