Commit 2b868646bb42c4f5af36ecae3871b5051c19a504

Authored by Igor Kulikov
Committed by GitHub
2 parents 6f780b8f eb4f4baa

Merge pull request #3760 from thingsboard/feature/sms-email-limits

Feature/sms email limits
Showing 21 changed files with 167 additions and 44 deletions
... ... @@ -34,6 +34,7 @@ import org.thingsboard.rule.engine.api.TbRelationTypes;
34 34 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
35 35 import org.thingsboard.server.actors.ActorSystemContext;
36 36 import org.thingsboard.server.actors.TbActorRef;
  37 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
37 38 import org.thingsboard.server.common.data.Customer;
38 39 import org.thingsboard.server.common.data.DataConstants;
39 40 import org.thingsboard.server.common.data.Device;
... ...
... ... @@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils;
23 23 import org.jetbrains.annotations.NotNull;
24 24 import org.springframework.beans.factory.annotation.Autowired;
25 25 import org.springframework.context.MessageSource;
  26 +import org.springframework.context.annotation.Lazy;
26 27 import org.springframework.core.NestedRuntimeException;
27 28 import org.springframework.mail.javamail.JavaMailSenderImpl;
28 29 import org.springframework.mail.javamail.MimeMessageHelper;
... ... @@ -40,6 +41,8 @@ import org.thingsboard.server.common.data.id.EntityId;
40 41 import org.thingsboard.server.common.data.id.TenantId;
41 42 import org.thingsboard.server.dao.exception.IncorrectParameterException;
42 43 import org.thingsboard.server.dao.settings.AdminSettingsService;
  44 +import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
  45 +import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
43 46
44 47 import javax.annotation.PostConstruct;
45 48 import javax.mail.MessagingException;
... ... @@ -58,18 +61,26 @@ public class DefaultMailService implements MailService {
58 61 public static final String UTF_8 = "UTF-8";
59 62 public static final int _10K = 10000;
60 63 public static final int _1M = 1000000;
61   - @Autowired
62   - private MessageSource messages;
63 64
  65 + private final MessageSource messages;
  66 + private final Configuration freemarkerConfig;
  67 + private final AdminSettingsService adminSettingsService;
  68 + private final TbApiUsageClient apiUsageClient;
  69 +
  70 + @Lazy
64 71 @Autowired
65   - private Configuration freemarkerConfig;
  72 + private TbApiUsageStateService apiUsageStateService;
66 73
67 74 private JavaMailSenderImpl mailSender;
68 75
69 76 private String mailFrom;
70 77
71   - @Autowired
72   - private AdminSettingsService adminSettingsService;
  78 + public DefaultMailService(MessageSource messages, Configuration freemarkerConfig, AdminSettingsService adminSettingsService, TbApiUsageClient apiUsageClient) {
  79 + this.messages = messages;
  80 + this.freemarkerConfig = freemarkerConfig;
  81 + this.adminSettingsService = adminSettingsService;
  82 + this.apiUsageClient = apiUsageClient;
  83 + }
73 84
74 85 @PostConstruct
75 86 private void init() {
... ... @@ -148,8 +159,11 @@ public class DefaultMailService implements MailService {
148 159 }
149 160
150 161 @Override
151   - public void sendEmail(String email, String subject, String message) throws ThingsboardException {
152   - sendMail(mailSender, mailFrom, email, subject, message);
  162 + public void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException {
  163 + if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) {
  164 + sendMail(mailSender, mailFrom, email, subject, message);
  165 + apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1);
  166 + }
153 167 }
154 168
155 169 @Override
... ... @@ -223,20 +237,23 @@ public class DefaultMailService implements MailService {
223 237 }
224 238
225 239 @Override
226   - public void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException {
227   - MimeMessage mailMsg = mailSender.createMimeMessage();
228   - MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8");
229   - helper.setFrom(StringUtils.isBlank(from) ? mailFrom : from);
230   - helper.setTo(to.split("\\s*,\\s*"));
231   - if (!StringUtils.isBlank(cc)) {
232   - helper.setCc(cc.split("\\s*,\\s*"));
233   - }
234   - if (!StringUtils.isBlank(bcc)) {
235   - helper.setBcc(bcc.split("\\s*,\\s*"));
  240 + public void send(TenantId tenantId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException {
  241 + if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) {
  242 + MimeMessage mailMsg = mailSender.createMimeMessage();
  243 + MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8");
  244 + helper.setFrom(StringUtils.isBlank(from) ? mailFrom : from);
  245 + helper.setTo(to.split("\\s*,\\s*"));
  246 + if (!StringUtils.isBlank(cc)) {
  247 + helper.setCc(cc.split("\\s*,\\s*"));
  248 + }
  249 + if (!StringUtils.isBlank(bcc)) {
  250 + helper.setBcc(bcc.split("\\s*,\\s*"));
  251 + }
  252 + helper.setSubject(subject);
  253 + helper.setText(body);
  254 + mailSender.send(helper.getMimeMessage());
  255 + apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1);
236 256 }
237   - helper.setSubject(subject);
238   - helper.setText(body);
239   - mailSender.send(helper.getMimeMessage());
240 257 }
241 258
242 259 @Override
... ...
... ... @@ -17,7 +17,6 @@ package org.thingsboard.server.service.sms;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
19 19 import lombok.extern.slf4j.Slf4j;
20   -import org.springframework.beans.factory.annotation.Autowired;
21 20 import org.springframework.core.NestedRuntimeException;
22 21 import org.springframework.stereotype.Service;
23 22 import org.thingsboard.rule.engine.api.SmsService;
... ... @@ -26,12 +25,15 @@ import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
26 25 import org.thingsboard.rule.engine.api.sms.config.SmsProviderConfiguration;
27 26 import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest;
28 27 import org.thingsboard.server.common.data.AdminSettings;
  28 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
29 29 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
30 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
31 31 import org.thingsboard.server.common.data.id.EntityId;
32 32 import org.thingsboard.server.common.data.id.TenantId;
33 33 import org.thingsboard.server.dao.settings.AdminSettingsService;
34 34 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  35 +import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
  36 +import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
35 37
36 38 import javax.annotation.PostConstruct;
37 39 import javax.annotation.PreDestroy;
... ... @@ -40,14 +42,20 @@ import javax.annotation.PreDestroy;
40 42 @Slf4j
41 43 public class DefaultSmsService implements SmsService {
42 44
43   - @Autowired
44   - private SmsSenderFactory smsSenderFactory;
45   -
46   - @Autowired
47   - private AdminSettingsService adminSettingsService;
  45 + private final SmsSenderFactory smsSenderFactory;
  46 + private final AdminSettingsService adminSettingsService;
  47 + private final TbApiUsageStateService apiUsageStateService;
  48 + private final TbApiUsageClient apiUsageClient;
48 49
49 50 private SmsSender smsSender;
50 51
  52 + public DefaultSmsService(SmsSenderFactory smsSenderFactory, AdminSettingsService adminSettingsService, TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) {
  53 + this.smsSenderFactory = smsSenderFactory;
  54 + this.adminSettingsService = adminSettingsService;
  55 + this.apiUsageStateService = apiUsageStateService;
  56 + this.apiUsageClient = apiUsageClient;
  57 + }
  58 +
51 59 @PostConstruct
52 60 private void init() {
53 61 updateSmsConfiguration();
... ... @@ -78,18 +86,26 @@ public class DefaultSmsService implements SmsService {
78 86 }
79 87 }
80 88
81   - @Override
82   - public void sendSms(String numberTo, String message) throws ThingsboardException {
  89 + private int sendSms(String numberTo, String message) throws ThingsboardException {
83 90 if (this.smsSender == null) {
84 91 throw new ThingsboardException("Unable to send SMS: no SMS provider configured!", ThingsboardErrorCode.GENERAL);
85 92 }
86   - this.sendSms(this.smsSender, numberTo, message);
  93 + return this.sendSms(this.smsSender, numberTo, message);
87 94 }
88 95
89 96 @Override
90   - public void sendSms(String[] numbersTo, String message) throws ThingsboardException {
91   - for (String numberTo : numbersTo) {
92   - this.sendSms(numberTo, message);
  97 + public void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException {
  98 + if (apiUsageStateService.getApiUsageState(tenantId).isSmsSendEnabled()) {
  99 + int smsCount = 0;
  100 + try {
  101 + for (String numberTo : numbersTo) {
  102 + smsCount += this.sendSms(numberTo, message);
  103 + }
  104 + } finally {
  105 + if (smsCount > 0) {
  106 + apiUsageClient.report(tenantId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount);
  107 + }
  108 + }
93 109 }
94 110 }
95 111
... ...
... ... @@ -21,7 +21,9 @@ public enum ApiFeature {
21 21 TRANSPORT("transportApiState", "Device API"),
22 22 DB("dbApiState", "Telemetry persistence"),
23 23 RE("ruleEngineApiState", "Rule Engine execution"),
24   - JS("jsExecutionApiState", "JavaScript functions execution");
  24 + JS("jsExecutionApiState", "JavaScript functions execution"),
  25 + EMAIL("emailApiState", "Email messages"),
  26 + SMS("smsApiState", "SMS messages");
25 27
26 28 @Getter
27 29 private final String apiStateKey;
... ...
... ... @@ -23,11 +23,15 @@ public enum ApiUsageRecordKey {
23 23 TRANSPORT_DP_COUNT(ApiFeature.TRANSPORT, "transportDataPointsCount", "transportDataPointsLimit"),
24 24 STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit"),
25 25 RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit"),
26   - JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit");
  26 + JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit"),
  27 + EMAIL_EXEC_COUNT(ApiFeature.EMAIL, "emailCount", "emailLimit"),
  28 + SMS_EXEC_COUNT(ApiFeature.SMS, "smsCount", "smsLimit");
27 29 private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT};
28 30 private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT};
29 31 private static final ApiUsageRecordKey[] DB_RECORD_KEYS = {STORAGE_DP_COUNT};
30 32 private static final ApiUsageRecordKey[] TRANSPORT_RECORD_KEYS = {TRANSPORT_MSG_COUNT, TRANSPORT_DP_COUNT};
  33 + private static final ApiUsageRecordKey[] EMAIL_RECORD_KEYS = {EMAIL_EXEC_COUNT};
  34 + private static final ApiUsageRecordKey[] SMS_RECORD_KEYS = {SMS_EXEC_COUNT};
31 35
32 36 @Getter
33 37 private final ApiFeature apiFeature;
... ... @@ -52,6 +56,10 @@ public enum ApiUsageRecordKey {
52 56 return RE_RECORD_KEYS;
53 57 case JS:
54 58 return JS_RECORD_KEYS;
  59 + case EMAIL:
  60 + return EMAIL_RECORD_KEYS;
  61 + case SMS:
  62 + return SMS_RECORD_KEYS;
55 63 default:
56 64 return new ApiUsageRecordKey[]{};
57 65 }
... ...
... ... @@ -47,6 +47,12 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
47 47 @Getter
48 48 @Setter
49 49 private ApiUsageStateValue jsExecState;
  50 + @Getter
  51 + @Setter
  52 + private ApiUsageStateValue emailExecState;
  53 + @Getter
  54 + @Setter
  55 + private ApiUsageStateValue smsExecState;
50 56
51 57 public ApiUsageState() {
52 58 super();
... ... @@ -64,6 +70,8 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
64 70 this.dbStorageState = ur.getDbStorageState();
65 71 this.reExecState = ur.getReExecState();
66 72 this.jsExecState = ur.getJsExecState();
  73 + this.emailExecState = ur.getEmailExecState();
  74 + this.smsExecState = ur.getSmsExecState();
67 75 }
68 76
69 77 public boolean isTransportEnabled() {
... ... @@ -81,4 +89,12 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
81 89 public boolean isJsExecEnabled() {
82 90 return !ApiUsageStateValue.DISABLED.equals(jsExecState);
83 91 }
  92 +
  93 + public boolean isEmailSendEnabled(){
  94 + return !ApiUsageStateValue.DISABLED.equals(emailExecState);
  95 + }
  96 +
  97 + public boolean isSmsSendEnabled(){
  98 + return !ApiUsageStateValue.DISABLED.equals(smsExecState);
  99 + }
84 100 }
... ...
... ... @@ -42,6 +42,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
42 42 private long maxJSExecutions;
43 43 private long maxDPStorageDays;
44 44 private int maxRuleNodeExecutionsPerMessage;
  45 + private long maxEmails;
  46 + private long maxSms;
45 47
46 48 private double warnThreshold;
47 49
... ... @@ -58,6 +60,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
58 60 return maxREExecutions;
59 61 case STORAGE_DP_COUNT:
60 62 return maxDPStorageDays;
  63 + case EMAIL_EXEC_COUNT:
  64 + return maxEmails;
  65 + case SMS_EXEC_COUNT:
  66 + return maxSms;
61 67 }
62 68 return 0L;
63 69 }
... ...
... ... @@ -450,6 +450,8 @@ public class ModelConstants {
450 450 public static final String API_USAGE_STATE_DB_STORAGE_COLUMN = "db_storage";
451 451 public static final String API_USAGE_STATE_RE_EXEC_COLUMN = "re_exec";
452 452 public static final String API_USAGE_STATE_JS_EXEC_COLUMN = "js_exec";
  453 + public static final String API_USAGE_STATE_EMAIL_EXEC_COLUMN = "email_exec";
  454 + public static final String API_USAGE_STATE_SMS_EXEC_COLUMN = "sms_exec";
453 455
454 456 /**
455 457 * Cassandra attributes and timeseries constants.
... ...
... ... @@ -63,6 +63,12 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
63 63 @Enumerated(EnumType.STRING)
64 64 @Column(name = ModelConstants.API_USAGE_STATE_JS_EXEC_COLUMN)
65 65 private ApiUsageStateValue jsExecState = ApiUsageStateValue.ENABLED;
  66 + @Enumerated(EnumType.STRING)
  67 + @Column(name = ModelConstants.API_USAGE_STATE_EMAIL_EXEC_COLUMN)
  68 + private ApiUsageStateValue emailExecState = ApiUsageStateValue.ENABLED;
  69 + @Enumerated(EnumType.STRING)
  70 + @Column(name = ModelConstants.API_USAGE_STATE_SMS_EXEC_COLUMN)
  71 + private ApiUsageStateValue smsExecState = ApiUsageStateValue.ENABLED;
66 72
67 73 public ApiUsageStateEntity() {
68 74 }
... ... @@ -83,6 +89,8 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
83 89 this.dbStorageState = ur.getDbStorageState();
84 90 this.reExecState = ur.getReExecState();
85 91 this.jsExecState = ur.getJsExecState();
  92 + this.emailExecState = ur.getEmailExecState();
  93 + this.smsExecState = ur.getSmsExecState();
86 94 }
87 95
88 96 @Override
... ... @@ -99,6 +107,8 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
99 107 ur.setDbStorageState(dbStorageState);
100 108 ur.setReExecState(reExecState);
101 109 ur.setJsExecState(jsExecState);
  110 + ur.setEmailExecState(emailExecState);
  111 + ur.setSmsExecState(smsExecState);
102 112 return ur;
103 113 }
104 114
... ...
... ... @@ -78,6 +78,8 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
78 78 apiUsageState.setReExecState(ApiUsageStateValue.ENABLED);
79 79 apiUsageState.setJsExecState(ApiUsageStateValue.ENABLED);
80 80 apiUsageState.setDbStorageState(ApiUsageStateValue.ENABLED);
  81 + apiUsageState.setSmsExecState(ApiUsageStateValue.ENABLED);
  82 + apiUsageState.setEmailExecState(ApiUsageStateValue.ENABLED);
81 83 apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId);
82 84
83 85 ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
... ...
... ... @@ -416,5 +416,7 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
416 416 db_storage varchar(32),
417 417 re_exec varchar(32),
418 418 js_exec varchar(32),
  419 + email_exec varchar(32),
  420 + sms_exec varchar(32),
419 421 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
420 422 );
... ...
... ... @@ -442,6 +442,8 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
442 442 db_storage varchar(32),
443 443 re_exec varchar(32),
444 444 js_exec varchar(32),
  445 + email_exec varchar(32),
  446 + sms_exec varchar(32),
445 447 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
446 448 );
447 449
... ...
... ... @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.ApiFeature;
20 20 import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
21 21 import org.thingsboard.server.common.data.ApiUsageStateValue;
22 22 import org.thingsboard.server.common.data.exception.ThingsboardException;
  23 +import org.thingsboard.server.common.data.id.TenantId;
23 24
24 25 import javax.mail.MessagingException;
25 26
... ... @@ -27,7 +28,7 @@ public interface MailService {
27 28
28 29 void updateMailConfiguration();
29 30
30   - void sendEmail(String email, String subject, String message) throws ThingsboardException;
  31 + void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException;
31 32
32 33 void sendTestMail(JsonNode config, String email) throws ThingsboardException;
33 34
... ... @@ -39,7 +40,7 @@ public interface MailService {
39 40
40 41 void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException;
41 42
42   - void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException;
  43 + void send(TenantId tenantId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException;
43 44
44 45 void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException;
45 46
... ...
... ... @@ -17,14 +17,13 @@ package org.thingsboard.rule.engine.api;
17 17
18 18 import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest;
19 19 import org.thingsboard.server.common.data.exception.ThingsboardException;
  20 +import org.thingsboard.server.common.data.id.TenantId;
20 21
21 22 public interface SmsService {
22 23
23 24 void updateSmsConfiguration();
24 25
25   - void sendSms(String numberTo, String message) throws ThingsboardException;
26   -
27   - void sendSms(String[] numbersTo, String message) throws ThingsboardException;;
  26 + void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException;;
28 27
29 28 void sendTestSms(TestSmsRequest testSmsRequest) throws ThingsboardException;
30 29
... ...
... ... @@ -19,6 +19,7 @@ import io.netty.channel.EventLoopGroup;
19 19 import org.springframework.data.redis.core.RedisTemplate;
20 20 import org.thingsboard.common.util.ListeningExecutor;
21 21 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
  22 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
22 23 import org.thingsboard.server.common.data.Customer;
23 24 import org.thingsboard.server.common.data.Device;
24 25 import org.thingsboard.server.common.data.DeviceProfile;
... ...
... ... @@ -26,6 +26,7 @@ import org.thingsboard.rule.engine.api.TbNode;
26 26 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
27 27 import org.thingsboard.rule.engine.api.TbNodeException;
28 28 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  29 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
29 30 import org.thingsboard.server.common.data.plugin.ComponentType;
30 31 import org.thingsboard.server.common.msg.TbMsg;
31 32
... ... @@ -87,7 +88,7 @@ public class TbSendEmailNode implements TbNode {
87 88
88 89 private void sendEmail(TbContext ctx, EmailPojo email) throws Exception {
89 90 if (this.config.isUseSystemSmtpSettings()) {
90   - ctx.getMailService().send(email.getFrom(), email.getTo(), email.getCc(),
  91 + ctx.getMailService().send(ctx.getTenantId(), email.getFrom(), email.getTo(), email.getCc(),
91 92 email.getBcc(), email.getSubject(), email.getBody());
92 93 } else {
93 94 MimeMessage mailMsg = mailSender.createMimeMessage();
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
23 23 import org.thingsboard.rule.engine.api.TbNodeException;
24 24 import org.thingsboard.rule.engine.api.sms.SmsSender;
25 25 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  26 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
26 27 import org.thingsboard.server.common.data.plugin.ComponentType;
27 28 import org.thingsboard.server.common.msg.TbMsg;
28 29
... ... @@ -75,7 +76,7 @@ public class TbSendSmsNode implements TbNode {
75 76 String message = TbNodeUtils.processPattern(this.config.getSmsMessageTemplate(), msg.getMetaData());
76 77 String[] numbersToList = numbersTo.split(",");
77 78 if (this.config.isUseSystemSmsSettings()) {
78   - ctx.getSmsService().sendSms(numbersToList, message);
  79 + ctx.getSmsService().sendSms(ctx.getTenantId(), numbersToList, message);
79 80 } else {
80 81 for (String numberTo : numbersToList) {
81 82 this.smsSender.sendSms(numberTo, message);
... ...
... ... @@ -161,6 +161,30 @@
161 161 </mat-error>
162 162 </mat-form-field>
163 163 <mat-form-field class="mat-block">
  164 + <mat-label translate>tenant-profile.max-emails</mat-label>
  165 + <input matInput required min="0" step="1"
  166 + formControlName="maxEmails"
  167 + type="number">
  168 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxEmails').hasError('required')">
  169 + {{ 'tenant-profile.max-emails-required' | translate}}
  170 + </mat-error>
  171 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxEmails').hasError('min')">
  172 + {{ 'tenant-profile.max-emails-range' | translate}}
  173 + </mat-error>
  174 + </mat-form-field>
  175 + <mat-form-field class="mat-block">
  176 + <mat-label translate>tenant-profile.max-sms</mat-label>
  177 + <input matInput required min="0" step="1"
  178 + formControlName="maxSms"
  179 + type="number">
  180 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxSms').hasError('required')">
  181 + {{ 'tenant-profile.max-sms-required' | translate}}
  182 + </mat-error>
  183 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxSms').hasError('min')">
  184 + {{ 'tenant-profile.max-sms-range' | translate}}
  185 + </mat-error>
  186 + </mat-form-field>
  187 + <mat-form-field class="mat-block">
164 188 <mat-label translate>tenant-profile.transport-tenant-msg-rate-limit</mat-label>
165 189 <input matInput formControlName="transportTenantMsgRateLimit">
166 190 </mat-form-field>
... ...
... ... @@ -70,7 +70,9 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
70 70 maxREExecutions: [null, [Validators.required, Validators.min(0)]],
71 71 maxJSExecutions: [null, [Validators.required, Validators.min(0)]],
72 72 maxDPStorageDays: [null, [Validators.required, Validators.min(0)]],
73   - maxRuleNodeExecutionsPerMessage: [null, [Validators.required, Validators.min(0)]]
  73 + maxRuleNodeExecutionsPerMessage: [null, [Validators.required, Validators.min(0)]],
  74 + maxEmails: [null, [Validators.required, Validators.min(0)]],
  75 + maxSms: [null, [Validators.required, Validators.min(0)]]
74 76 });
75 77 this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => {
76 78 this.updateModel();
... ...
... ... @@ -44,6 +44,8 @@ export interface DefaultTenantProfileConfiguration {
44 44 maxJSExecutions: number;
45 45 maxDPStorageDays: number;
46 46 maxRuleNodeExecutionsPerMessage: number;
  47 + maxEmails: number;
  48 + maxSms: number;
47 49 }
48 50
49 51 export type TenantProfileConfigurations = DefaultTenantProfileConfiguration;
... ... @@ -69,7 +71,9 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
69 71 maxREExecutions: 0,
70 72 maxJSExecutions: 0,
71 73 maxDPStorageDays: 0,
72   - maxRuleNodeExecutionsPerMessage: 0
  74 + maxRuleNodeExecutionsPerMessage: 0,
  75 + maxEmails: 0,
  76 + maxSms: 0
73 77 };
74 78 configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT};
75 79 break;
... ...
... ... @@ -2017,7 +2017,13 @@
2017 2017 "max-d-p-storage-days-range": "Minimum number of data points storage days can't be negative",
2018 2018 "max-rule-node-executions-per-message": "Maximum number of rule node executions per message (0 - unlimited)",
2019 2019 "max-rule-node-executions-per-message-required": "Maximum number of rule node executions per message is required.",
2020   - "max-rule-node-executions-per-message-range": "Minimum number of rule node executions per message can't be negative"
  2020 + "max-rule-node-executions-per-message-range": "Minimum number of rule node executions per message can't be negative",
  2021 + "max-emails": "Maximum number of emails sent (0 - unlimited)",
  2022 + "max-emails-required": "Maximum number of emails sent is required.",
  2023 + "max-emails-range": "Maximum number of emails sent can't be negative",
  2024 + "max-sms": "Maximum number of SMS sent (0 - unlimited)",
  2025 + "max-sms-required": "Maximum number of SMS sent is required.",
  2026 + "max-sms-range": "Maximum number of SMS sent can't be negative"
2021 2027 },
2022 2028 "timeinterval": {
2023 2029 "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }",
... ...