UI: Added new component copy-device-credentials
This commit is contained in:
		
							parent
							
								
									986cfe5cc1
								
							
						
					
					
						commit
						205be4e5ce
					
				@ -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>
 | 
			
		||||
@ -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'
 | 
			
		||||
      }));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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">
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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}}",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user