UI: Added new component copy-device-credentials

This commit is contained in:
Vladyslav_Prykhodko 2020-11-23 12:13:05 +02:00
parent 986cfe5cc1
commit 205be4e5ce
7 changed files with 179 additions and 34 deletions

View File

@ -0,0 +1,26 @@
<!--
Copyright © 2016-2020 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.
-->
<button mat-raised-button
ngxClipboard
[cbContent]="credential"
(cbOnSuccess)="onCopyCredential()"
[fxHide]="hideButton"
[disabled]="disabled || loading">
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
<span>{{ buttonLabel }}</span>
</button>

View File

@ -0,0 +1,128 @@
///
/// Copyright © 2016-2020 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, Input, OnDestroy, OnInit } from '@angular/core';
import { EntityId } from '@shared/models/id/entity-id';
import { DeviceService } from '@core/http/device.service';
import { DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models';
import { isDefinedAndNotNull, isEqual } from '@core/utils';
import { BehaviorSubject, Subject } from 'rxjs';
import { distinctUntilChanged, filter, mergeMap, tap } from 'rxjs/operators';
import { EntityType } from '@shared/models/entity-type.models';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'tb-copy-device-credentials',
templateUrl: './copy-device-credentials.component.html',
styleUrls: []
})
export class CopyDeviceCredentialsComponent implements OnDestroy {
private deviceId$ = new BehaviorSubject<EntityId>(null);
private credentials$ = new Subject<DeviceCredentials>();
private tooltipMessage: string;
public hideButton = true;
public credential: string;
public loading = false;
public buttonLabel: string;
@Input()
set deviceId(deviceId: EntityId) {
this.deviceId$.next(deviceId);
}
@Input() disabled: boolean;
@Input()
set credentials(credential: DeviceCredentials) {
this.credentials$.next(credential);
}
constructor(private store: Store<AppState>,
private translate: TranslateService,
private deviceService: DeviceService
) {
this.deviceId$.pipe(
filter(device => isDefinedAndNotNull(device) && device.entityType === EntityType.DEVICE),
distinctUntilChanged((prev, curr) => prev.id === curr.id),
tap(() => this.loading = true),
mergeMap(device => this.deviceService.getDeviceCredentials(device.id))
).subscribe(deviceCredentials => {
this.processingValue(deviceCredentials);
this.loading = false;
});
this.credentials$.pipe(
filter(credential => isDefinedAndNotNull(credential)),
distinctUntilChanged((prev, curr) => isEqual(prev, curr))
).subscribe(deviceCredentials => {
console.warn(deviceCredentials);
this.processingValue(deviceCredentials);
});
}
ngOnDestroy(): void {
this.deviceId$.unsubscribe();
this.credentials$.unsubscribe();
}
private processingValue(credential: DeviceCredentials): void {
switch (credential.credentialsType) {
case DeviceCredentialsType.ACCESS_TOKEN:
this.hideButton = false;
this.credential = credential.credentialsId;
this.buttonLabel = this.translate.instant('device.copyAccessToken');
this.tooltipMessage = this.translate.instant('device.accessTokenCopiedMessage');
break;
case DeviceCredentialsType.MQTT_BASIC:
this.hideButton = false;
this.credential = this.convertObjectToString(JSON.parse(credential.credentialsValue));
this.buttonLabel = this.translate.instant('device.copy-mqtt-authentication');
this.tooltipMessage = this.translate.instant('device.mqtt-authentication-copied-message');
break;
default:
this.hideButton = true;
this.credential = null;
this.buttonLabel = '';
this.tooltipMessage = '';
}
}
private convertObjectToString(obj: object): string {
Object.keys(obj).forEach(k => (!obj[k] && obj[k] !== undefined) && delete obj[k]);
return JSON.stringify(obj).replace(/"([^"]+)":/g, '$1:');
}
onCopyCredential() {
this.store.dispatch(new ActionNotificationShow(
{
message: this.tooltipMessage,
type: 'success',
duration: 750,
verticalPosition: 'bottom',
horizontalPosition: 'right'
}));
}
}

View File

@ -105,7 +105,7 @@ import { AlarmRuleConditionComponent } from './profile/alarm/alarm-rule-conditio
import { FilterTextComponent } from './filter/filter-text.component';
import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component';
import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomplete.component';
import { DeviceProfileProvisionConfigurationComponent } from "./profile/device-profile-provision-configuration.component";
import { DeviceProfileProvisionConfigurationComponent } from './profile/device-profile-provision-configuration.component';
import { AlarmScheduleComponent } from './profile/alarm/alarm-schedule.component';
import { DeviceWizardDialogComponent } from './wizard/device-wizard-dialog.component';
import { DeviceCredentialsComponent } from './device/device-credentials.component';
@ -115,6 +115,7 @@ import { EditAlarmDetailsDialogComponent } from './profile/alarm/edit-alarm-deta
import { AlarmRuleConditionDialogComponent } from '@home/components/profile/alarm/alarm-rule-condition-dialog.component';
import { DefaultTenantProfileConfigurationComponent } from './profile/tenant/default-tenant-profile-configuration.component';
import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-profile-configuration.component';
import { CopyDeviceCredentialsComponent } from './device/copy-device-credentials.component';
@NgModule({
declarations:
@ -211,6 +212,7 @@ import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-pro
AlarmScheduleComponent,
DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
AlarmScheduleDialogComponent,
EditAlarmDetailsDialogComponent
],
@ -293,6 +295,7 @@ import { TenantProfileConfigurationComponent } from './profile/tenant/tenant-pro
RuleChainAutocompleteComponent,
DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
AlarmScheduleInfoComponent,
AlarmScheduleComponent,
AlarmScheduleDialogComponent,

View File

@ -51,16 +51,17 @@
ngxClipboard
(cbOnSuccess)="onDeviceIdCopied($event)"
[cbContent]="entity?.id?.id"
[disabled]="(isLoading$ | async)"
[fxShow]="!isEdit">
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
<span translate>device.copyId</span>
</button>
<button mat-raised-button
(click)="copyAccessToken($event)"
[fxShow]="!isEdit">
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
<span translate>device.copyAccessToken</span>
</button>
<tb-copy-device-credentials
[fxShow]="!isEdit"
[disabled]="(isLoading$ | async)"
[credentials]="componentsData.deviceCredential"
[deviceId]="entity?.id">
</tb-copy-device-credentials>
</div>
</div>
<div class="mat-padding" fxLayout="column">

View File

@ -21,10 +21,9 @@ import { EntityComponent } from '../../components/entity/entity.component';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {
createDeviceConfiguration,
createDeviceProfileConfiguration, createDeviceTransportConfiguration,
createDeviceTransportConfiguration,
DeviceData,
DeviceInfo,
DeviceProfileData,
DeviceProfileInfo,
DeviceProfileType,
DeviceTransportType
@ -33,8 +32,6 @@ import { EntityType } from '@shared/models/entity-type.models';
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core';
import { DeviceService } from '@core/http/device.service';
import { ClipboardService } from 'ngx-clipboard';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
@Component({
@ -46,12 +43,12 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
entityType = EntityType;
componentsData: any;
deviceScope: 'tenant' | 'customer' | 'customer_user';
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
private deviceService: DeviceService,
private clipboardService: ClipboardService,
@Inject('entity') protected entityValue: DeviceInfo,
@Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<DeviceInfo>,
public fb: FormBuilder) {
@ -60,6 +57,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
ngOnInit() {
this.deviceScope = this.entitiesTableConfig.componentsData.deviceScope;
this.componentsData = this.entitiesTableConfigValue.componentsData;
super.ngOnInit();
}
@ -114,26 +112,6 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
}));
}
copyAccessToken($event) {
if (this.entity.id) {
this.deviceService.getDeviceCredentials(this.entity.id.id, true).subscribe(
(deviceCredentials) => {
const credentialsId = deviceCredentials.credentialsId;
if (this.clipboardService.copyFromContent(credentialsId)) {
this.store.dispatch(new ActionNotificationShow(
{
message: this.translate.instant('device.accessTokenCopiedMessage'),
type: 'success',
duration: 750,
verticalPosition: 'bottom',
horizontalPosition: 'right'
}));
}
}
);
}
}
onDeviceProfileUpdated() {
this.entitiesTableConfig.table.updateData(false);
}

View File

@ -63,6 +63,7 @@ import { DeviceTabsComponent } from '@home/pages/device/device-tabs.component';
import { HomeDialogsService } from '@home/dialogs/home-dialogs.service';
import { DeviceWizardDialogComponent } from '@home/components/wizard/device-wizard-dialog.component';
import { BaseData, HasId } from '@shared/models/base-data';
import { isDefinedAndNotNull } from '@core/utils';
@Injectable()
export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<DeviceInfo>> {
@ -115,7 +116,8 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
const routeParams = route.params;
this.config.componentsData = {
deviceScope: route.data.devicesType,
deviceProfileId: null
deviceProfileId: null,
deviceCredential: null
};
this.customerId = routeParams.customerId;
return this.store.pipe(select(selectAuthUser), take(1)).pipe(
@ -479,6 +481,11 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
deviceId: device.id.id,
isReadOnly: this.config.componentsData.deviceScope === 'customer_user'
}
}).afterClosed().subscribe(deviceCredential => {
if (isDefinedAndNotNull(deviceCredential)) {
this.config.table.onEntityUpdated(device);
this.config.componentsData.deviceCredential = deviceCredential;
}
});
}

View File

@ -814,8 +814,10 @@
"details": "Details",
"copyId": "Copy device Id",
"copyAccessToken": "Copy access token",
"copy-mqtt-authentication": "Copy MQTT authentication",
"idCopiedMessage": "Device Id has been copied to clipboard",
"accessTokenCopiedMessage": "Device access token has been copied to clipboard",
"mqtt-authentication-copied-message": "Device MQTT authentication has been copied to clipboard",
"assignedToCustomer": "Assigned to customer",
"unable-delete-device-alias-title": "Unable to delete device alias",
"unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",