Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/3767/head
Andrii Shvaika 6 years ago
parent
commit
856555f5fa
  1. 10
      application/src/main/java/org/thingsboard/server/service/apiusage/TenantApiUsageState.java
  2. 2
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  3. 14
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  4. 4
      dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java
  5. 26
      ui-ngx/src/app/modules/home/components/device/copy-device-credentials.component.html
  6. 128
      ui-ngx/src/app/modules/home/components/device/copy-device-credentials.component.ts
  7. 3
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  8. 13
      ui-ngx/src/app/modules/home/pages/device/device.component.html
  9. 30
      ui-ngx/src/app/modules/home/pages/device/device.component.ts
  10. 9
      ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts
  11. 2
      ui-ngx/src/assets/locale/locale.constant-en_US.json

10
application/src/main/java/org/thingsboard/server/service/apiusage/TenantApiUsageState.java

@ -125,6 +125,10 @@ public class TenantApiUsageState {
return apiUsageState.getDbStorageState();
case JS:
return apiUsageState.getJsExecState();
case EMAIL:
return apiUsageState.getEmailExecState();
case SMS:
return apiUsageState.getSmsExecState();
default:
return ApiUsageStateValue.ENABLED;
}
@ -145,6 +149,12 @@ public class TenantApiUsageState {
case JS:
apiUsageState.setJsExecState(value);
break;
case EMAIL:
apiUsageState.setEmailExecState(value);
break;
case SMS:
apiUsageState.setSmsExecState(value);
break;
}
return !currentValue.equals(value);
}

2
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -367,6 +367,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
" db_storage varchar(32)," +
" re_exec varchar(32)," +
" js_exec varchar(32)," +
" email_exec varchar(32)," +
" sms_exec varchar(32)," +
" CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)\n" +
");");
} catch (Exception e) {

14
application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java

@ -304,6 +304,9 @@ public class DefaultMailService implements MailService {
return "invoke";
case RE:
return "process";
case EMAIL:
case SMS:
return "send";
default:
throw new RuntimeException("Not implemented!");
}
@ -319,6 +322,9 @@ public class DefaultMailService implements MailService {
return "invoked";
case RE:
return "processed";
case EMAIL:
case SMS:
return "sent";
default:
throw new RuntimeException("Not implemented!");
}
@ -337,6 +343,10 @@ public class DefaultMailService implements MailService {
return valueInM + " out of " + thresholdInM + " allowed JavaScript functions";
case RE_EXEC_COUNT:
return valueInM + " out of " + thresholdInM + " allowed Rule Engine messages";
case EMAIL_EXEC_COUNT:
return valueInM + " out of " + thresholdInM + " allowed Email messages";
case SMS_EXEC_COUNT:
return valueInM + " out of " + thresholdInM + " allowed SMS messages";
default:
throw new RuntimeException("Not implemented!");
}
@ -353,6 +363,10 @@ public class DefaultMailService implements MailService {
return "JavaScript functions " + getValueAsString(value) + " times";
case RE_EXEC_COUNT:
return getValueAsString(value) + " Rule Engine messages";
case EMAIL_EXEC_COUNT:
return getValueAsString(value) + " Email messages";
case SMS_EXEC_COUNT:
return getValueAsString(value) + " SMS messages";
default:
throw new RuntimeException("Not implemented!");
}

4
dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java

@ -96,6 +96,10 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
new StringDataEntry(ApiFeature.RE.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
new StringDataEntry(ApiFeature.JS.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
new StringDataEntry(ApiFeature.EMAIL.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(),
new StringDataEntry(ApiFeature.SMS.getApiStateKey(), ApiUsageStateValue.ENABLED.name())));
tsService.save(tenantId, saved.getId(), apiUsageStates, 0L);
List<TsKvEntry> profileThresholds = new ArrayList<>();

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

@ -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>

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

@ -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'
}));
}
}

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

@ -118,6 +118,7 @@ import { TenantProfileConfigurationComponent } from '@home/components/profile/te
import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component';
import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component';
import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component';
import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-device-credentials.component';
@NgModule({
declarations:
@ -214,6 +215,7 @@ import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/tw
AlarmScheduleComponent,
DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
AlarmScheduleDialogComponent,
EditAlarmDetailsDialogComponent,
SmsProviderConfigurationComponent,
@ -299,6 +301,7 @@ import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/tw
RuleChainAutocompleteComponent,
DeviceWizardDialogComponent,
DeviceCredentialsComponent,
CopyDeviceCredentialsComponent,
AlarmScheduleInfoComponent,
AlarmScheduleComponent,
AlarmScheduleDialogComponent,

13
ui-ngx/src/app/modules/home/pages/device/device.component.html

@ -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">

30
ui-ngx/src/app/modules/home/pages/device/device.component.ts

@ -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);
}

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

@ -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.componentsData.deviceCredential = deviceCredential;
this.config.table.onEntityUpdated(device);
}
});
}

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

@ -841,8 +841,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…
Cancel
Save