Commit 1c79480b332c20151c9d99acef82f3059522617a

Authored by YevhenBondarenko
Committed by Andrew Shvayka
1 parent 449cb594

created sendApiFeatureStateEmail

@@ -17,21 +17,21 @@ package org.thingsboard.server.service.apiusage; @@ -17,21 +17,21 @@ package org.thingsboard.server.service.apiusage;
17 17
18 import com.google.common.util.concurrent.FutureCallback; 18 import com.google.common.util.concurrent.FutureCallback;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.apache.commons.lang3.StringUtils;
20 import org.checkerframework.checker.nullness.qual.Nullable; 21 import org.checkerframework.checker.nullness.qual.Nullable;
21 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.beans.factory.annotation.Value; 23 import org.springframework.beans.factory.annotation.Value;
23 -import org.springframework.boot.context.event.ApplicationReadyEvent;  
24 import org.springframework.context.annotation.Lazy; 24 import org.springframework.context.annotation.Lazy;
25 -import org.springframework.context.event.EventListener;  
26 -import org.springframework.core.annotation.Order;  
27 -import org.springframework.data.util.Pair;  
28 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.rule.engine.api.MailService;
29 import org.thingsboard.server.common.data.ApiFeature; 27 import org.thingsboard.server.common.data.ApiFeature;
30 import org.thingsboard.server.common.data.ApiUsageRecordKey; 28 import org.thingsboard.server.common.data.ApiUsageRecordKey;
31 import org.thingsboard.server.common.data.ApiUsageState; 29 import org.thingsboard.server.common.data.ApiUsageState;
  30 +import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
32 import org.thingsboard.server.common.data.ApiUsageStateValue; 31 import org.thingsboard.server.common.data.ApiUsageStateValue;
33 import org.thingsboard.server.common.data.Tenant; 32 import org.thingsboard.server.common.data.Tenant;
34 import org.thingsboard.server.common.data.TenantProfile; 33 import org.thingsboard.server.common.data.TenantProfile;
  34 +import org.thingsboard.server.common.data.exception.ThingsboardException;
35 import org.thingsboard.server.common.data.id.ApiUsageStateId; 35 import org.thingsboard.server.common.data.id.ApiUsageStateId;
36 import org.thingsboard.server.common.data.id.TenantId; 36 import org.thingsboard.server.common.data.id.TenantId;
37 import org.thingsboard.server.common.data.id.TenantProfileId; 37 import org.thingsboard.server.common.data.id.TenantProfileId;
@@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; @@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
45 import org.thingsboard.server.common.msg.queue.ServiceType; 45 import org.thingsboard.server.common.msg.queue.ServiceType;
46 import org.thingsboard.server.common.msg.queue.TbCallback; 46 import org.thingsboard.server.common.msg.queue.TbCallback;
47 import org.thingsboard.server.common.msg.tools.SchedulerUtils; 47 import org.thingsboard.server.common.msg.tools.SchedulerUtils;
  48 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
48 import org.thingsboard.server.dao.tenant.TenantService; 49 import org.thingsboard.server.dao.tenant.TenantService;
49 import org.thingsboard.server.dao.timeseries.TimeseriesService; 50 import org.thingsboard.server.dao.timeseries.TimeseriesService;
50 import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; 51 import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
@@ -54,14 +55,11 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; @@ -54,14 +55,11 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg;
54 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 55 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
55 import org.thingsboard.server.queue.discovery.PartitionService; 56 import org.thingsboard.server.queue.discovery.PartitionService;
56 import org.thingsboard.server.queue.scheduler.SchedulerComponent; 57 import org.thingsboard.server.queue.scheduler.SchedulerComponent;
57 -import org.thingsboard.server.queue.util.TbCoreComponent;  
58 -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;  
59 import org.thingsboard.server.service.queue.TbClusterService; 58 import org.thingsboard.server.service.queue.TbClusterService;
60 import org.thingsboard.server.service.telemetry.InternalTelemetryService; 59 import org.thingsboard.server.service.telemetry.InternalTelemetryService;
61 60
62 import javax.annotation.PostConstruct; 61 import javax.annotation.PostConstruct;
63 import java.util.ArrayList; 62 import java.util.ArrayList;
64 -import java.util.HashMap;  
65 import java.util.HashSet; 63 import java.util.HashSet;
66 import java.util.List; 64 import java.util.List;
67 import java.util.Map; 65 import java.util.Map;
@@ -94,6 +92,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -94,6 +92,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
94 private final ApiUsageStateService apiUsageStateService; 92 private final ApiUsageStateService apiUsageStateService;
95 private final SchedulerComponent scheduler; 93 private final SchedulerComponent scheduler;
96 private final TbTenantProfileCache tenantProfileCache; 94 private final TbTenantProfileCache tenantProfileCache;
  95 + private final MailService mailService;
97 96
98 @Lazy 97 @Lazy
99 @Autowired 98 @Autowired
@@ -118,7 +117,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -118,7 +117,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
118 TimeseriesService tsService, 117 TimeseriesService tsService,
119 ApiUsageStateService apiUsageStateService, 118 ApiUsageStateService apiUsageStateService,
120 SchedulerComponent scheduler, 119 SchedulerComponent scheduler,
121 - TbTenantProfileCache tenantProfileCache) { 120 + TbTenantProfileCache tenantProfileCache, MailService mailService) {
122 this.clusterService = clusterService; 121 this.clusterService = clusterService;
123 this.partitionService = partitionService; 122 this.partitionService = partitionService;
124 this.tenantService = tenantService; 123 this.tenantService = tenantService;
@@ -126,6 +125,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -126,6 +125,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
126 this.apiUsageStateService = apiUsageStateService; 125 this.apiUsageStateService = apiUsageStateService;
127 this.scheduler = scheduler; 126 this.scheduler = scheduler;
128 this.tenantProfileCache = tenantProfileCache; 127 this.tenantProfileCache = tenantProfileCache;
  128 + this.mailService = mailService;
129 } 129 }
130 130
131 @PostConstruct 131 @PostConstruct
@@ -286,7 +286,26 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { @@ -286,7 +286,26 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService {
286 List<TsKvEntry> stateTelemetry = new ArrayList<>(); 286 List<TsKvEntry> stateTelemetry = new ArrayList<>();
287 result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))))); 287 result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name())))));
288 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); 288 tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK);
289 - //TODO: notify tenant admin via email! 289 +
  290 + String email = tenantService.findTenantById(state.getTenantId()).getEmail();
  291 +
  292 + if (StringUtils.isNotEmpty(email)) {
  293 + result.forEach((apiFeature, stateValue) -> {
  294 + ApiUsageRecordKey[] keys = ApiUsageRecordKey.getKeys(apiFeature);
  295 + ApiUsageStateMailMessage[] msgs = new ApiUsageStateMailMessage[keys.length];
  296 + for (int i = 0; i < keys.length; i++) {
  297 + ApiUsageRecordKey key = keys[i];
  298 + msgs[i] = new ApiUsageStateMailMessage(key, state.getProfileThreshold(key), state.get(key));
  299 + }
  300 + try {
  301 + mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, msgs);
  302 + } catch (ThingsboardException e) {
  303 + log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e);
  304 + }
  305 + });
  306 + } else {
  307 + log.warn("[{}] Can't send update of the API state to tenant with empty email!", state.getTenantId());
  308 + }
290 } 309 }
291 310
292 private void checkStartOfNextCycle() { 311 private void checkStartOfNextCycle() {
@@ -29,6 +29,9 @@ import org.springframework.stereotype.Service; @@ -29,6 +29,9 @@ import org.springframework.stereotype.Service;
29 import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; 29 import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
30 import org.thingsboard.rule.engine.api.MailService; 30 import org.thingsboard.rule.engine.api.MailService;
31 import org.thingsboard.server.common.data.AdminSettings; 31 import org.thingsboard.server.common.data.AdminSettings;
  32 +import org.thingsboard.server.common.data.ApiFeature;
  33 +import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
  34 +import org.thingsboard.server.common.data.ApiUsageStateValue;
32 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 35 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
33 import org.thingsboard.server.common.data.exception.ThingsboardException; 36 import org.thingsboard.server.common.data.exception.ThingsboardException;
34 import org.thingsboard.server.common.data.id.EntityId; 37 import org.thingsboard.server.common.data.id.EntityId;
@@ -246,6 +249,32 @@ public class DefaultMailService implements MailService { @@ -246,6 +249,32 @@ public class DefaultMailService implements MailService {
246 sendMail(mailSender, mailFrom, email, subject, message); 249 sendMail(mailSender, mailFrom, email, subject, message);
247 } 250 }
248 251
  252 + @Override
  253 + public void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage[] stateMailMessages) throws ThingsboardException {
  254 + String subject = messages.getMessage("api.usage.state", null, Locale.US);
  255 +
  256 + Map<String, Object> model = new HashMap<>();
  257 + model.put("apiFeature", apiFeature.getApiStateKey());
  258 + model.put("apiUsageStateMailMessages", stateMailMessages);
  259 +
  260 + model.put(TARGET_EMAIL, email);
  261 +
  262 + String message = null;
  263 +
  264 + switch (stateValue) {
  265 + case ENABLED:
  266 + message = mergeTemplateIntoString("state.enabled.ftl", model);
  267 + break;
  268 + case WARNING:
  269 + message = mergeTemplateIntoString("state.warning.ftl", model);
  270 + break;
  271 + case DISABLED:
  272 + message = mergeTemplateIntoString("state.disabled.ftl", model);
  273 + break;
  274 + }
  275 + sendMail(mailSender, mailFrom, email, subject, message);
  276 + }
  277 +
249 private void sendMail(JavaMailSenderImpl mailSender, 278 private void sendMail(JavaMailSenderImpl mailSender,
250 String mailFrom, String email, 279 String mailFrom, String email,
251 String subject, String message) throws ThingsboardException { 280 String subject, String message) throws ThingsboardException {
@@ -3,4 +3,5 @@ activation.subject=Your account activation on Thingsboard @@ -3,4 +3,5 @@ activation.subject=Your account activation on Thingsboard
3 account.activated.subject=Thingsboard - your account has been activated 3 account.activated.subject=Thingsboard - your account has been activated
4 reset.password.subject=Thingsboard - Password reset has been requested 4 reset.password.subject=Thingsboard - Password reset has been requested
5 password.was.reset.subject=Thingsboard - your account password has been reset 5 password.was.reset.subject=Thingsboard - your account password has been reset
6 -account.lockout.subject=Thingsboard - User account has been lockout  
  6 +account.lockout.subject=Thingsboard - User account has been lockout
  7 +api.usage.state=Thingsboard - Api Usage State for tenant has been updated
  1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  19 +<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  20 +<head>
  21 +<meta name="viewport" content="width=device-width" />
  22 +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  23 +<title>Thingsboard - Api Usage State</title>
  24 +
  25 +
  26 +<style type="text/css">
  27 +img {
  28 +max-width: 100%;
  29 +}
  30 +body {
  31 +-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em;
  32 +}
  33 +body {
  34 +background-color: #f6f6f6;
  35 +}
  36 +@media only screen and (max-width: 640px) {
  37 + body {
  38 + padding: 0 !important;
  39 + }
  40 + h1 {
  41 + font-weight: 800 !important; margin: 20px 0 5px !important;
  42 + }
  43 + h2 {
  44 + font-weight: 800 !important; margin: 20px 0 5px !important;
  45 + }
  46 + h3 {
  47 + font-weight: 800 !important; margin: 20px 0 5px !important;
  48 + }
  49 + h4 {
  50 + font-weight: 800 !important; margin: 20px 0 5px !important;
  51 + }
  52 + h1 {
  53 + font-size: 22px !important;
  54 + }
  55 + h2 {
  56 + font-size: 18px !important;
  57 + }
  58 + h3 {
  59 + font-size: 16px !important;
  60 + }
  61 + .container {
  62 + padding: 0 !important; width: 100% !important;
  63 + }
  64 + .content {
  65 + padding: 0 !important;
  66 + }
  67 + .content-wrap {
  68 + padding: 10px !important;
  69 + }
  70 + .invoice {
  71 + width: 100% !important;
  72 + }
  73 +}
  74 +</style>
  75 +</head>
  76 +
  77 +<body itemscope itemtype="http://schema.org/EmailMessage" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6">
  78 +
  79 +<table class="body-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td>
  80 + <td class="container" width="600" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" valign="top">
  81 + <div class="content" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
  82 + <table class="main" width="100%" cellpadding="0" cellspacing="0" itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;" bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
  83 + <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  84 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  85 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  86 + <h2>Thingsboard Api Usage State for tenant has been updated</h2>
  87 + </td>
  88 + </tr>
  89 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  90 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  91 + Thingsboard Usage state ${apiFeature} was updated to status DISABLED.
  92 + </td>
  93 + </tr>
  94 + <#list apiUsageStateMailMessages as msg>
  95 + <tr>
  96 + <td>
  97 + ${msg.key.apiLimitKey} = ${msg.threshold}
  98 + </td>
  99 + </tr>
  100 + <tr>
  101 + <td>
  102 + ${msg.key.apiCountKey} = ${msg.value}
  103 + </td>
  104 + </tr>
  105 + </#list>
  106 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  107 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  108 + &mdash; The Thingsboard
  109 + </td>
  110 + </tr></table></td>
  111 + </tr>
  112 + </table>
  113 + <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
  114 + <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  115 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  116 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
  117 + </tr>
  118 + </table>
  119 + </div>
  120 + </div>
  121 + </td>
  122 + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td>
  123 + </tr>
  124 +</table>
  125 +</body>
  126 +</html>
  1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  19 +<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  20 +<head>
  21 +<meta name="viewport" content="width=device-width" />
  22 +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  23 +<title>Thingsboard - Api Usage State</title>
  24 +
  25 +
  26 +<style type="text/css">
  27 +img {
  28 +max-width: 100%;
  29 +}
  30 +body {
  31 +-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em;
  32 +}
  33 +body {
  34 +background-color: #f6f6f6;
  35 +}
  36 +@media only screen and (max-width: 640px) {
  37 + body {
  38 + padding: 0 !important;
  39 + }
  40 + h1 {
  41 + font-weight: 800 !important; margin: 20px 0 5px !important;
  42 + }
  43 + h2 {
  44 + font-weight: 800 !important; margin: 20px 0 5px !important;
  45 + }
  46 + h3 {
  47 + font-weight: 800 !important; margin: 20px 0 5px !important;
  48 + }
  49 + h4 {
  50 + font-weight: 800 !important; margin: 20px 0 5px !important;
  51 + }
  52 + h1 {
  53 + font-size: 22px !important;
  54 + }
  55 + h2 {
  56 + font-size: 18px !important;
  57 + }
  58 + h3 {
  59 + font-size: 16px !important;
  60 + }
  61 + .container {
  62 + padding: 0 !important; width: 100% !important;
  63 + }
  64 + .content {
  65 + padding: 0 !important;
  66 + }
  67 + .content-wrap {
  68 + padding: 10px !important;
  69 + }
  70 + .invoice {
  71 + width: 100% !important;
  72 + }
  73 +}
  74 +</style>
  75 +</head>
  76 +
  77 +<body itemscope itemtype="http://schema.org/EmailMessage" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6">
  78 +
  79 +<table class="body-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td>
  80 + <td class="container" width="600" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" valign="top">
  81 + <div class="content" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
  82 + <table class="main" width="100%" cellpadding="0" cellspacing="0" itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;" bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
  83 + <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  84 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  85 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  86 + <h2>Thingsboard Api Usage State for tenant has been updated</h2>
  87 + </td>
  88 + </tr>
  89 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0 ;">
  90 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  91 + Thingsboard Usage state ${apiFeature} was updated to status ENABLED.
  92 + </td>
  93 + </tr>
  94 + <#list apiUsageStateMailMessages as msg>
  95 + <tr>
  96 + <td>
  97 + ${msg.key.apiLimitKey} = ${msg.threshold}
  98 + </td>
  99 + </tr>
  100 + <tr>
  101 + <td>
  102 + ${msg.key.apiCountKey} = ${msg.value}
  103 + </td>
  104 + </tr>
  105 + </#list>
  106 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  107 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  108 + &mdash; The Thingsboard
  109 + </td>
  110 + </tr></table></td>
  111 + </tr>
  112 + </table>
  113 + <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
  114 + <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  115 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  116 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
  117 + </tr>
  118 + </table>
  119 + </div>
  120 + </div>
  121 + </td>
  122 + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td>
  123 + </tr>
  124 +</table>
  125 +</body>
  126 +</html>
  1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  19 +<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  20 +<head>
  21 +<meta name="viewport" content="width=device-width" />
  22 +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  23 +<title>Thingsboard - Api Usage State</title>
  24 +
  25 +
  26 +<style type="text/css">
  27 +img {
  28 +max-width: 100%;
  29 +}
  30 +body {
  31 +-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em;
  32 +}
  33 +body {
  34 +background-color: #f6f6f6;
  35 +}
  36 +@media only screen and (max-width: 640px) {
  37 + body {
  38 + padding: 0 !important;
  39 + }
  40 + h1 {
  41 + font-weight: 800 !important; margin: 20px 0 5px !important;
  42 + }
  43 + h2 {
  44 + font-weight: 800 !important; margin: 20px 0 5px !important;
  45 + }
  46 + h3 {
  47 + font-weight: 800 !important; margin: 20px 0 5px !important;
  48 + }
  49 + h4 {
  50 + font-weight: 800 !important; margin: 20px 0 5px !important;
  51 + }
  52 + h1 {
  53 + font-size: 22px !important;
  54 + }
  55 + h2 {
  56 + font-size: 18px !important;
  57 + }
  58 + h3 {
  59 + font-size: 16px !important;
  60 + }
  61 + .container {
  62 + padding: 0 !important; width: 100% !important;
  63 + }
  64 + .content {
  65 + padding: 0 !important;
  66 + }
  67 + .content-wrap {
  68 + padding: 10px !important;
  69 + }
  70 + .invoice {
  71 + width: 100% !important;
  72 + }
  73 +}
  74 +</style>
  75 +</head>
  76 +
  77 +<body itemscope itemtype="http://schema.org/EmailMessage" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6">
  78 +
  79 +<table class="body-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td>
  80 + <td class="container" width="600" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" valign="top">
  81 + <div class="content" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
  82 + <table class="main" width="100%" cellpadding="0" cellspacing="0" itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;" bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
  83 + <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  84 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  85 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  86 + <h2>Thingsboard Api Usage State for tenant has been updated</h2>
  87 + </td>
  88 + </tr>
  89 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  90 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  91 + Thingsboard Usage state ${apiFeature} was updated to status WARNING.
  92 + </td>
  93 + </tr>
  94 + <#list apiUsageStateMailMessages as msg>
  95 + <tr>
  96 + <td>
  97 + ${msg.key.apiLimitKey} = ${msg.threshold}
  98 + </td>
  99 + </tr>
  100 + <tr>
  101 + <td>
  102 + ${msg.key.apiCountKey} = ${msg.value}
  103 + </td>
  104 + </tr>
  105 + </#list>
  106 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  107 + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
  108 + &mdash; The Thingsboard
  109 + </td>
  110 + </tr></table></td>
  111 + </tr>
  112 + </table>
  113 + <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
  114 + <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  115 + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
  116 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
  117 + </tr>
  118 + </table>
  119 + </div>
  120 + </div>
  121 + </td>
  122 + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td>
  123 + </tr>
  124 +</table>
  125 +</body>
  126 +</html>
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class ApiUsageStateMailMessage {
  22 + private final ApiUsageRecordKey key;
  23 + private final long threshold;
  24 + private final long value;
  25 +}
@@ -16,6 +16,9 @@ @@ -16,6 +16,9 @@
16 package org.thingsboard.rule.engine.api; 16 package org.thingsboard.rule.engine.api;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.thingsboard.server.common.data.ApiFeature;
  20 +import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
  21 +import org.thingsboard.server.common.data.ApiUsageStateValue;
19 import org.thingsboard.server.common.data.exception.ThingsboardException; 22 import org.thingsboard.server.common.data.exception.ThingsboardException;
20 23
21 import javax.mail.MessagingException; 24 import javax.mail.MessagingException;
@@ -39,4 +42,6 @@ public interface MailService { @@ -39,4 +42,6 @@ public interface MailService {
39 void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException; 42 void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException;
40 43
41 void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException; 44 void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException;
  45 +
  46 + void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage[] stateMailMessages) throws ThingsboardException;
42 } 47 }