created tenant profile configuration
This commit is contained in:
parent
1f6c0c54cd
commit
41212e11c0
@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* 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,
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* 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,
|
||||
|
||||
@ -20,7 +20,7 @@ import lombok.Setter;
|
||||
import org.thingsboard.server.common.data.ApiUsageRecordKey;
|
||||
import org.thingsboard.server.common.data.ApiUsageState;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
|
||||
|
||||
@ -30,7 +30,8 @@ import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.asset.Asset;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
@ -127,6 +128,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
public void createDefaultTenantProfiles() throws Exception {
|
||||
tenantProfileService.findOrCreateDefaultTenantProfile(TenantId.SYS_TENANT_ID);
|
||||
|
||||
TenantProfileData tenantProfileData = new TenantProfileData();
|
||||
DefaultTenantProfileConfiguration configuration = new DefaultTenantProfileConfiguration();
|
||||
tenantProfileData.setConfiguration(configuration);
|
||||
|
||||
TenantProfile isolatedTbCoreProfile = new TenantProfile();
|
||||
isolatedTbCoreProfile.setDefault(false);
|
||||
isolatedTbCoreProfile.setName("Isolated TB Core");
|
||||
@ -134,6 +139,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile");
|
||||
isolatedTbCoreProfile.setIsolatedTbCore(true);
|
||||
isolatedTbCoreProfile.setIsolatedTbRuleEngine(false);
|
||||
isolatedTbCoreProfile.setProfileData(tenantProfileData);
|
||||
try {
|
||||
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreProfile);
|
||||
} catch (DataValidationException e) {
|
||||
@ -147,6 +153,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile");
|
||||
isolatedTbRuleEngineProfile.setIsolatedTbCore(false);
|
||||
isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
|
||||
isolatedTbRuleEngineProfile.setProfileData(tenantProfileData);
|
||||
|
||||
try {
|
||||
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbRuleEngineProfile);
|
||||
} catch (DataValidationException e) {
|
||||
@ -160,6 +168,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile");
|
||||
isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true);
|
||||
isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
|
||||
isolatedTbCoreAndTbRuleEngineProfile.setProfileData(tenantProfileData);
|
||||
|
||||
try {
|
||||
tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreAndTbRuleEngineProfile);
|
||||
} catch (DataValidationException e) {
|
||||
|
||||
@ -23,7 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.thingsboard.server.common.data.EntityInfo;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
|
||||
@ -21,6 +21,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.thingsboard.server.common.data;
|
||||
|
||||
public enum TenantProfileType {
|
||||
DEFAULT
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.thingsboard.server.common.data.tenant.profile;
|
||||
|
||||
import lombok.Data;
|
||||
import org.thingsboard.server.common.data.TenantProfileType;
|
||||
|
||||
@Data
|
||||
public class DefaultTenantProfileConfiguration implements TenantProfileConfiguration {
|
||||
|
||||
private long maxDevices;
|
||||
private long maxAssets;
|
||||
|
||||
private String transportTenantMsgRateLimit;
|
||||
private String transportTenantTelemetryMsgRateLimit;
|
||||
private String transportTenantTelemetryDataPointsRateLimit;
|
||||
private String transportDeviceMsgRateLimit;
|
||||
private String transportDeviceTelemetryMsgRateLimit;
|
||||
private String transportDeviceTelemetryDataPointsRateLimit;
|
||||
|
||||
private long maxTransportMessages;
|
||||
private long maxTransportDataPoints;
|
||||
private long maxREExecutions;
|
||||
private long maxJSExecutions;
|
||||
private long maxDPStorageDays;
|
||||
private int maxRuleNodeExecutionsPerMessage;
|
||||
|
||||
@Override
|
||||
public TenantProfileType getType() {
|
||||
return TenantProfileType.DEFAULT;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.thingsboard.server.common.data.tenant.profile;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.thingsboard.server.common.data.TenantProfileType;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonTypeInfo(
|
||||
use = JsonTypeInfo.Id.NAME,
|
||||
include = JsonTypeInfo.As.PROPERTY,
|
||||
property = "type")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = DefaultTenantProfileConfiguration.class, name = "DEFAULT")})
|
||||
public interface TenantProfileConfiguration {
|
||||
|
||||
@JsonIgnore
|
||||
TenantProfileType getType();
|
||||
|
||||
}
|
||||
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data;
|
||||
package org.thingsboard.server.common.data.tenant.profile;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
@ -26,6 +26,8 @@ import java.util.Map;
|
||||
@Data
|
||||
public class TenantProfileData {
|
||||
|
||||
private TenantProfileConfiguration configuration;
|
||||
|
||||
@JsonIgnore
|
||||
private Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
@ -18,9 +18,9 @@ package org.thingsboard.server.common.transport.limits;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
import org.thingsboard.server.common.transport.TransportTenantProfileCache;
|
||||
import org.thingsboard.server.common.transport.profile.TenantProfileUpdateResult;
|
||||
import org.thingsboard.server.queue.util.TbTransportComponent;
|
||||
|
||||
@ -22,14 +22,13 @@ import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.TypeDef;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
import org.thingsboard.server.dao.model.BaseSqlEntity;
|
||||
import org.thingsboard.server.dao.model.ModelConstants;
|
||||
import org.thingsboard.server.dao.model.SearchTextEntity;
|
||||
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
|
||||
import org.thingsboard.server.dao.util.mapping.JsonBinaryType;
|
||||
import org.thingsboard.server.dao.util.mapping.JsonStringType;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
|
||||
@ -23,20 +23,18 @@ import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.EntityInfo;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||
import org.thingsboard.server.dao.service.DataValidator;
|
||||
import org.thingsboard.server.dao.service.PaginatedRemover;
|
||||
import org.thingsboard.server.dao.service.Validator;
|
||||
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
@ -21,13 +21,12 @@ import org.junit.Test;
|
||||
import org.thingsboard.server.common.data.EntityInfo;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.TenantProfileData;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -188,8 +187,8 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest {
|
||||
Assert.assertTrue(pageData.getData().isEmpty());
|
||||
tenantProfiles.addAll(pageData.getData());
|
||||
|
||||
for (int i=0;i<28;i++) {
|
||||
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i);
|
||||
for (int i = 0; i < 28; i++) {
|
||||
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile" + i);
|
||||
tenantProfiles.add(tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile));
|
||||
}
|
||||
|
||||
@ -224,8 +223,8 @@ public class BaseTenantProfileServiceTest extends AbstractServiceTest {
|
||||
|
||||
List<TenantProfile> tenantProfiles = new ArrayList<>();
|
||||
|
||||
for (int i=0;i<28;i++) {
|
||||
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i);
|
||||
for (int i = 0; i < 28; i++) {
|
||||
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile" + i);
|
||||
tenantProfiles.add(tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile));
|
||||
}
|
||||
|
||||
|
||||
@ -113,6 +113,8 @@ import { AlarmScheduleInfoComponent } from './profile/alarm/alarm-schedule-info.
|
||||
import { AlarmScheduleDialogComponent } from '@home/components/profile/alarm/alarm-schedule-dialog.component';
|
||||
import { EditAlarmDetailsDialogComponent } from './profile/alarm/edit-alarm-details-dialog.component';
|
||||
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';
|
||||
|
||||
@NgModule({
|
||||
declarations:
|
||||
@ -182,6 +184,8 @@ import { AlarmRuleConditionDialogComponent } from '@home/components/profile/alar
|
||||
FilterUserInfoDialogComponent,
|
||||
FilterPredicateValueComponent,
|
||||
TenantProfileAutocompleteComponent,
|
||||
DefaultTenantProfileConfigurationComponent,
|
||||
TenantProfileConfigurationComponent,
|
||||
TenantProfileDataComponent,
|
||||
TenantProfileComponent,
|
||||
TenantProfileDialogComponent,
|
||||
|
||||
@ -204,7 +204,6 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor,
|
||||
createTenantProfile($event: Event, profileName: string) {
|
||||
$event.preventDefault();
|
||||
const tenantProfile: TenantProfile = {
|
||||
id: null,
|
||||
name: profileName
|
||||
};
|
||||
this.openTenantProfileDialog(tenantProfile, true);
|
||||
|
||||
@ -16,9 +16,15 @@
|
||||
|
||||
-->
|
||||
<form [formGroup]="tenantProfileDataFormGroup" style="padding-bottom: 16px;">
|
||||
<tb-json-object-edit
|
||||
[required]="required"
|
||||
label="{{ 'tenant-profile.data' | translate }}"
|
||||
formControlName="tenantProfileData">
|
||||
</tb-json-object-edit>
|
||||
<mat-expansion-panel [expanded]="true">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div translate>tenant-profile.profile-configuration</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<tb-tenant-profile-configuration
|
||||
formControlName="configuration"
|
||||
required>
|
||||
</tb-tenant-profile-configuration>
|
||||
</mat-expansion-panel>
|
||||
</form>
|
||||
|
||||
@ -62,7 +62,7 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit
|
||||
|
||||
ngOnInit() {
|
||||
this.tenantProfileDataFormGroup = this.fb.group({
|
||||
tenantProfileData: [null, Validators.required]
|
||||
configuration: [null, Validators.required]
|
||||
});
|
||||
this.tenantProfileDataFormGroup.valueChanges.subscribe(() => {
|
||||
this.updateModel();
|
||||
@ -79,13 +79,13 @@ export class TenantProfileDataComponent implements ControlValueAccessor, OnInit
|
||||
}
|
||||
|
||||
writeValue(value: TenantProfileData | null): void {
|
||||
this.tenantProfileDataFormGroup.get('tenantProfileData').patchValue(value, {emitEvent: false});
|
||||
this.tenantProfileDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false});
|
||||
}
|
||||
|
||||
private updateModel() {
|
||||
let tenantProfileData: TenantProfileData = null;
|
||||
if (this.tenantProfileDataFormGroup.valid) {
|
||||
tenantProfileData = this.tenantProfileDataFormGroup.getRawValue().tenantProfileData;
|
||||
tenantProfileData = this.tenantProfileDataFormGroup.getRawValue();
|
||||
}
|
||||
this.propagateChange(tenantProfileData);
|
||||
}
|
||||
|
||||
@ -18,7 +18,12 @@ import { Component, Inject, Input, Optional } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { TenantProfile } from '@app/shared/models/tenant.model';
|
||||
import {
|
||||
createTenantProfileConfiguration,
|
||||
TenantProfile,
|
||||
TenantProfileData,
|
||||
TenantProfileType
|
||||
} from '@app/shared/models/tenant.model';
|
||||
import { ActionNotificationShow } from '@app/core/notification/notification.actions';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
|
||||
@ -56,7 +61,9 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
|
||||
name: [entity ? entity.name : '', [Validators.required]],
|
||||
isolatedTbCore: [entity ? entity.isolatedTbCore : false, []],
|
||||
isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []],
|
||||
profileData: [entity && !this.isAdd ? entity.profileData : {}, []],
|
||||
profileData: [entity && !this.isAdd ? entity.profileData : {
|
||||
configuration: createTenantProfileConfiguration(TenantProfileType.DEFAULT)
|
||||
} as TenantProfileData, []],
|
||||
description: [entity ? entity.description : '', []],
|
||||
}
|
||||
);
|
||||
@ -66,7 +73,9 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
|
||||
this.entityForm.patchValue({name: entity.name});
|
||||
this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore});
|
||||
this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine});
|
||||
this.entityForm.patchValue({profileData: entity.profileData});
|
||||
this.entityForm.patchValue({profileData: !this.isAdd ? entity.profileData: {
|
||||
configuration: createTenantProfileConfiguration(TenantProfileType.DEFAULT)
|
||||
} as TenantProfileData});
|
||||
this.entityForm.patchValue({description: entity.description});
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,141 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="defaultTenantProfileConfigurationFormGroup">
|
||||
<section formGroupName="configuration" fxLayout="column">
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.maximum-devices</mat-label>
|
||||
<input matInput required min="0" step="1"
|
||||
formControlName="maxDevices"
|
||||
type="number">
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxDevices').hasError('required')">
|
||||
{{ 'tenant-profile.maximum-devices-required' | translate}}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxDevices').hasError('min')">
|
||||
{{ 'tenant-profile.maximum-devices-range' | translate}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.maximum-assets</mat-label>
|
||||
<input matInput required min="0" step="1"
|
||||
formControlName="maxAssets"
|
||||
type="number">
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxAssets').hasError('required')">
|
||||
{{ 'tenant-profile.maximum-assets-required' | translate}}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxAssets').hasError('min')">
|
||||
{{ 'tenant-profile.maximum-assets-range' | translate}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.max-transport-messages</mat-label>
|
||||
<input matInput required min="0" step="1"
|
||||
formControlName="maxTransportMessages"
|
||||
type="number">
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxTransportMessages').hasError('required')">
|
||||
{{ 'tenant-profile.max-transport-messages-range' | translate}}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxTransportMessages').hasError('min')">
|
||||
{{ 'tenant-profile.max-transport-messages-required' | translate}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.max-transport-data-points</mat-label>
|
||||
<input matInput required min="0" step="1"
|
||||
formControlName="maxTransportDataPoints"
|
||||
type="number">
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxTransportDataPoints').hasError('required')">
|
||||
{{ 'tenant-profile.max-transport-data-points-required' | translate}}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxTransportDataPoints').hasError('min')">
|
||||
{{ 'tenant-profile.max-transport-data-points-range' | translate}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.max-r-e-executions</mat-label>
|
||||
<input matInput required min="0" step="1"
|
||||
formControlName="maxREExecutions"
|
||||
type="number">
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxREExecutions').hasError('required')">
|
||||
{{ 'tenant-profile.max-r-e-executions-required' | translate}}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxREExecutions').hasError('min')">
|
||||
{{ 'tenant-profile.max-r-e-executions-range' | translate}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.max-j-s-executions</mat-label>
|
||||
<input matInput required min="0" step="1"
|
||||
formControlName="maxJSExecutions"
|
||||
type="number">
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxJSExecutions').hasError('required')">
|
||||
{{ 'tenant-profile.max-j-s-executions-required' | translate}}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxJSExecutions').hasError('min')">
|
||||
{{ 'tenant-profile.max-j-s-executions-range' | translate}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.max-d-p-storage-days</mat-label>
|
||||
<input matInput required min="0" step="1"
|
||||
formControlName="maxDPStorageDays"
|
||||
type="number">
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxDPStorageDays').hasError('required')">
|
||||
{{ 'tenant-profile.max-d-p-storage-days-required' | translate}}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxDPStorageDays').hasError('min')">
|
||||
{{ 'tenant-profile.max-d-p-storage-days-range' | translate}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.max-rule-node-executions-per-message</mat-label>
|
||||
<input matInput required min="0" step="1"
|
||||
formControlName="maxRuleNodeExecutionsPerMessage"
|
||||
type="number">
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxRuleNodeExecutionsPerMessage').hasError('required')">
|
||||
{{ 'tenant-profile.max-rule-node-executions-per-message-required' | translate}}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('configuration.maxRuleNodeExecutionsPerMessage').hasError('min')">
|
||||
{{ 'tenant-profile.max-rule-node-executions-per-message-range' | translate}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.transport-tenant-msg-rate-limit</mat-label>
|
||||
<input matInput formControlName="transportTenantMsgRateLimit">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.transport-tenant-telemetry-msg-rate-limit</mat-label>
|
||||
<input matInput formControlName="transportTenantTelemetryMsgRateLimit">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.transport-tenant-telemetry-data-points-rate-limit</mat-label>
|
||||
<input matInput formControlName="transportTenantTelemetryDataPointsRateLimit">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.transport-device-msg-rate-limit</mat-label>
|
||||
<input matInput formControlName="transportDeviceMsgRateLimit">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.transport-device-telemetry-msg-rate-limit</mat-label>
|
||||
<input matInput formControlName="transportDeviceTelemetryMsgRateLimit">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="mat-block">
|
||||
<mat-label translate>tenant-profile.transport-device-telemetry-data-points-rate-limit</mat-label>
|
||||
<input matInput formControlName="transportDeviceTelemetryDataPointsRateLimit">
|
||||
</mat-form-field>
|
||||
</section>
|
||||
</section>
|
||||
@ -0,0 +1,115 @@
|
||||
///
|
||||
/// 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, forwardRef, Input, OnInit } from '@angular/core';
|
||||
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@app/core/core.state';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import {
|
||||
DefaultTenantProfileConfiguration,
|
||||
TenantProfileConfiguration,
|
||||
TenantProfileType
|
||||
} from '@shared/models/tenant.model';
|
||||
import { isDefinedAndNotNull } from '@core/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-default-tenant-profile-configuration',
|
||||
templateUrl: './default-tenant-profile-configuration.component.html',
|
||||
styleUrls: [],
|
||||
providers: [{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => DefaultTenantProfileConfigurationComponent),
|
||||
multi: true
|
||||
}]
|
||||
})
|
||||
export class DefaultTenantProfileConfigurationComponent implements ControlValueAccessor, OnInit {
|
||||
|
||||
defaultTenantProfileConfigurationFormGroup: FormGroup;
|
||||
|
||||
private requiredValue: boolean;
|
||||
get required(): boolean {
|
||||
return this.requiredValue;
|
||||
}
|
||||
@Input()
|
||||
set required(value: boolean) {
|
||||
this.requiredValue = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
private propagateChange = (v: any) => { };
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
private fb: FormBuilder) {
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.defaultTenantProfileConfigurationFormGroup = this.fb.group({
|
||||
configuration: this.fb.group({
|
||||
maxDevices: [null, [Validators.required, Validators.min(0)]],
|
||||
maxAssets: [null, [Validators.required, Validators.min(0)]],
|
||||
transportTenantMsgRateLimit: [null, []],
|
||||
transportTenantTelemetryMsgRateLimit: [null, []],
|
||||
transportTenantTelemetryDataPointsRateLimit: [null, []],
|
||||
transportDeviceMsgRateLimit: [null, []],
|
||||
transportDeviceTelemetryMsgRateLimit: [null, []],
|
||||
transportDeviceTelemetryDataPointsRateLimit: [null, []],
|
||||
maxTransportMessages: [null, [Validators.required, Validators.min(0)]],
|
||||
maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]],
|
||||
maxREExecutions: [null, [Validators.required, Validators.min(0)]],
|
||||
maxJSExecutions: [null, [Validators.required, Validators.min(0)]],
|
||||
maxDPStorageDays: [null, [Validators.required, Validators.min(0)]],
|
||||
maxRuleNodeExecutionsPerMessage: [null, [Validators.required, Validators.min(0)]]
|
||||
})
|
||||
});
|
||||
this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => {
|
||||
this.updateModel();
|
||||
});
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
if (this.disabled) {
|
||||
this.defaultTenantProfileConfigurationFormGroup.disable({emitEvent: false});
|
||||
} else {
|
||||
this.defaultTenantProfileConfigurationFormGroup.enable({emitEvent: false});
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: DefaultTenantProfileConfiguration | null): void {
|
||||
if (isDefinedAndNotNull(value)) {
|
||||
this.defaultTenantProfileConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
|
||||
}
|
||||
}
|
||||
|
||||
private updateModel() {
|
||||
let configuration: TenantProfileConfiguration = null;
|
||||
if (this.defaultTenantProfileConfigurationFormGroup.valid) {
|
||||
configuration = this.defaultTenantProfileConfigurationFormGroup.getRawValue().configuration;
|
||||
configuration.type = TenantProfileType.DEFAULT;
|
||||
}
|
||||
this.propagateChange(configuration);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<div [formGroup]="tenantProfileConfigurationFormGroup">
|
||||
<div [ngSwitch]="type">
|
||||
<ng-template [ngSwitchCase]="tenantProfileType.DEFAULT">
|
||||
<tb-default-tenant-profile-configuration
|
||||
[required]="required"
|
||||
formControlName="configuration">
|
||||
</tb-default-tenant-profile-configuration>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,103 @@
|
||||
///
|
||||
/// 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, forwardRef, Input, OnInit } from '@angular/core';
|
||||
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@app/core/core.state';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { deepClone } from '@core/utils';
|
||||
import { TenantProfileConfiguration, TenantProfileType } from '@shared/models/tenant.model';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-tenant-profile-configuration',
|
||||
templateUrl: './tenant-profile-configuration.component.html',
|
||||
styleUrls: [],
|
||||
providers: [{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => TenantProfileConfigurationComponent),
|
||||
multi: true
|
||||
}]
|
||||
})
|
||||
export class TenantProfileConfigurationComponent implements ControlValueAccessor, OnInit {
|
||||
|
||||
tenantProfileType = TenantProfileType;
|
||||
|
||||
tenantProfileConfigurationFormGroup: FormGroup;
|
||||
|
||||
private requiredValue: boolean;
|
||||
get required(): boolean {
|
||||
return this.requiredValue;
|
||||
}
|
||||
@Input()
|
||||
set required(value: boolean) {
|
||||
this.requiredValue = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
type: TenantProfileType;
|
||||
|
||||
private propagateChange = (v: any) => { };
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
private fb: FormBuilder) {
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.tenantProfileConfigurationFormGroup = this.fb.group({
|
||||
configuration: [null, Validators.required]
|
||||
});
|
||||
this.tenantProfileConfigurationFormGroup.valueChanges.subscribe(() => {
|
||||
this.updateModel();
|
||||
});
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
if (this.disabled) {
|
||||
this.tenantProfileConfigurationFormGroup.disable({emitEvent: false});
|
||||
} else {
|
||||
this.tenantProfileConfigurationFormGroup.enable({emitEvent: false});
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: TenantProfileConfiguration | null): void {
|
||||
this.type = value?.type;
|
||||
const configuration = deepClone(value);
|
||||
if (configuration) {
|
||||
delete configuration.type;
|
||||
}
|
||||
this.tenantProfileConfigurationFormGroup.patchValue({configuration}, {emitEvent: false});
|
||||
}
|
||||
|
||||
private updateModel() {
|
||||
let configuration: TenantProfileConfiguration = null;
|
||||
if (this.tenantProfileConfigurationFormGroup.valid) {
|
||||
configuration = this.tenantProfileConfigurationFormGroup.getRawValue().configuration;
|
||||
configuration.type = this.type;
|
||||
}
|
||||
this.propagateChange(configuration);
|
||||
}
|
||||
}
|
||||
@ -19,8 +19,59 @@ import { TenantId } from './id/tenant-id';
|
||||
import { TenantProfileId } from '@shared/models/id/tenant-profile-id';
|
||||
import { BaseData } from '@shared/models/base-data';
|
||||
|
||||
export enum TenantProfileType {
|
||||
DEFAULT = 'DEFAULT'
|
||||
}
|
||||
|
||||
export interface DefaultTenantProfileConfiguration {
|
||||
maxDevices: number;
|
||||
maxAssets: number;
|
||||
|
||||
transportTenantMsgRateLimit?: string;
|
||||
transportTenantTelemetryMsgRateLimit?: string;
|
||||
transportTenantTelemetryDataPointsRateLimit?: string;
|
||||
transportDeviceMsgRateLimit?: string;
|
||||
transportDeviceTelemetryMsgRateLimit?: string;
|
||||
transportDeviceTelemetryDataPointsRateLimit?: string;
|
||||
|
||||
maxTransportMessages: number;
|
||||
maxTransportDataPoints: number;
|
||||
maxREExecutions: number;
|
||||
maxJSExecutions: number;
|
||||
maxDPStorageDays: number;
|
||||
maxRuleNodeExecutionsPerMessage: number;
|
||||
}
|
||||
|
||||
export type TenantProfileConfigurations = DefaultTenantProfileConfiguration;
|
||||
|
||||
export interface TenantProfileConfiguration extends TenantProfileConfigurations {
|
||||
type: TenantProfileType;
|
||||
}
|
||||
|
||||
export function createTenantProfileConfiguration(type: TenantProfileType): TenantProfileConfiguration {
|
||||
let configuration: TenantProfileConfiguration = null;
|
||||
if (type) {
|
||||
switch (type) {
|
||||
case TenantProfileType.DEFAULT:
|
||||
const defaultConfiguration: DefaultTenantProfileConfiguration = {
|
||||
maxDevices: 0,
|
||||
maxAssets: 0,
|
||||
maxTransportMessages: 0,
|
||||
maxTransportDataPoints: 0,
|
||||
maxREExecutions: 0,
|
||||
maxJSExecutions: 0,
|
||||
maxDPStorageDays: 0,
|
||||
maxRuleNodeExecutionsPerMessage: 0
|
||||
};
|
||||
configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT};
|
||||
break;
|
||||
}
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
export interface TenantProfileData {
|
||||
[key: string]: string;
|
||||
configuration: TenantProfileConfiguration;
|
||||
}
|
||||
|
||||
export interface TenantProfile extends BaseData<TenantProfileId> {
|
||||
|
||||
@ -1923,6 +1923,7 @@
|
||||
"name": "Name",
|
||||
"name-required": "Name is required.",
|
||||
"data": "Profile data",
|
||||
"profile-configuration": "Profile configuration",
|
||||
"description": "Description",
|
||||
"default": "Default",
|
||||
"delete-tenant-profile-title": "Are you sure you want to delete the tenant profile '{{tenantProfileName}}'?",
|
||||
@ -1932,7 +1933,37 @@
|
||||
"set-default-tenant-profile-title": "Are you sure you want to make the tenant profile '{{tenantProfileName}}' default?",
|
||||
"set-default-tenant-profile-text": "After the confirmation the tenant profile will be marked as default and will be used for new tenants with no profile specified.",
|
||||
"no-tenant-profiles-found": "No tenant profiles found.",
|
||||
"create-new-tenant-profile": "Create a new one!"
|
||||
"create-new-tenant-profile": "Create a new one!",
|
||||
"maximum-devices": "Maximum number of devices (0 - unlimited)",
|
||||
"maximum-devices-required": "Maximum number of devices is required.",
|
||||
"maximum-devices-range": "Minimum number of devices can't be negative",
|
||||
"maximum-assets": "Maximum number of assets (0 - unlimited)",
|
||||
"maximum-assets-required": "Maximum number of assets is required.",
|
||||
"maximum-assets-range": "Maximum number of assets can't be negative",
|
||||
"transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.",
|
||||
"transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.",
|
||||
"transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.",
|
||||
"transport-device-msg-rate-limit": "Transport device messages rate limit.",
|
||||
"transport-device-telemetry-msg-rate-limit": "Transport device telemetry messages rate limit.",
|
||||
"transport-device-telemetry-data-points-rate-limit": "Transport device telemetry data points rate limit.",
|
||||
"max-transport-messages": "Maximum number of transport messages (0 - unlimited)",
|
||||
"max-transport-messages-required": "Maximum number of transport messages is required.",
|
||||
"max-transport-messages-range": "Maximum number of transport messages can't be negative",
|
||||
"max-transport-data-points": "Maximum number of transport data points (0 - unlimited)",
|
||||
"max-transport-data-points-required": "Maximum number of transport data points is required.",
|
||||
"max-transport-data-points-range": "Minimum number of transport data points can't be negative",
|
||||
"max-r-e-executions": "Maximum number of Rule Engine executions (0 - unlimited)",
|
||||
"max-r-e-executions-required": "Maximum number of Rule Engine executions is required.",
|
||||
"max-r-e-executions-range": "Minimum number of Rule Engine executions can't be negative",
|
||||
"max-j-s-executions": "Maximum number of JavaScript executions (0 - unlimited)",
|
||||
"max-j-s-executions-required": "Maximum number of JavaScript executions is required.",
|
||||
"max-j-s-executions-range": "Minimum number of JavaScript executions can't be negative",
|
||||
"max-d-p-storage-days": "Maximum number of data points storage days (0 - unlimited)",
|
||||
"max-d-p-storage-days-required": "Maximum number of data points storage days is required.",
|
||||
"max-d-p-storage-days-range": "Minimum number of data points storage days can't be negative",
|
||||
"max-rule-node-executions-per-message": "Maximum number of rule node executions per message (0 - unlimited)",
|
||||
"max-rule-node-executions-per-message-required": "Maximum number of rule node executions per message is required.",
|
||||
"max-rule-node-executions-per-message-range": "Minimum number of rule node executions per message can't be negative"
|
||||
},
|
||||
"timeinterval": {
|
||||
"seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user