diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java index 59b63d7200..330ecc23f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java @@ -297,15 +297,9 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { if (StringUtils.isNotEmpty(email)) { result.forEach((apiFeature, stateValue) -> { - ApiUsageRecordKey[] keys = ApiUsageRecordKey.getKeys(apiFeature); - ApiUsageStateMailMessage[] msgs = new ApiUsageStateMailMessage[keys.length]; - for (int i = 0; i < keys.length; i++) { - ApiUsageRecordKey key = keys[i]; - msgs[i] = new ApiUsageStateMailMessage(key, state.getProfileThreshold(key), state.get(key)); - } mailExecutor.submit(() -> { try { - mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, msgs[0]); + mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, createStateMailMessage(state, apiFeature, stateValue)); } catch (ThingsboardException e) { log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e); } @@ -316,6 +310,33 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { } } + private ApiUsageStateMailMessage createStateMailMessage(TenantApiUsageState state, ApiFeature apiFeature, ApiUsageStateValue stateValue) { + StateChecker checker = getStateChecker(stateValue); + for (ApiUsageRecordKey apiUsageRecordKey : ApiUsageRecordKey.getKeys(apiFeature)) { + long threshold = state.getProfileThreshold(apiUsageRecordKey); + long warnThreshold = state.getProfileWarnThreshold(apiUsageRecordKey); + long value = state.get(apiUsageRecordKey); + if (checker.check(threshold, warnThreshold, value)) { + return new ApiUsageStateMailMessage(apiUsageRecordKey, threshold, value); + } + } + return null; + } + + private StateChecker getStateChecker(ApiUsageStateValue stateValue) { + if (ApiUsageStateValue.ENABLED.equals(stateValue)) { + return (t, wt, v) -> true; + } else if (ApiUsageStateValue.WARNING.equals(stateValue)) { + return (t, wt, v) -> v < t && v >= wt; + } else { + return (t, wt, v) -> v >= t; + } + } + + private interface StateChecker { + boolean check(long threshold, long warnThreshold, long value); + } + private void checkStartOfNextCycle() { updateLock.lock(); try { diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 19509243ef..3045fe0cce 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -30,6 +30,7 @@ import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.ApiFeature; +import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageStateMailMessage; import org.thingsboard.server.common.data.ApiUsageStateValue; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -261,19 +262,36 @@ public class DefaultMailService implements MailService { switch (stateValue) { case ENABLED: + model.put("apiLabel", toEnabledValueLabel(apiFeature)); message = mergeTemplateIntoString("state.enabled.ftl", model); break; case WARNING: + model.put("apiLimitValueLabel", toDisabledValueLabel(msg.getKey(), msg.getThreshold())); + model.put("apiValueLabel", toDisabledValueLabel(apiFeature) + " " + toDisabledValueLabel(msg.getKey(), msg.getValue())); message = mergeTemplateIntoString("state.warning.ftl", model); break; case DISABLED: - model.put("apiLimitValueLabel", toDisabledValueLabel(apiFeature) + " " + toDisabledValueLabel(msg)); + model.put("apiLimitValueLabel", toDisabledValueLabel(apiFeature) + " " + toDisabledValueLabel(msg.getKey(), msg.getThreshold())); message = mergeTemplateIntoString("state.disabled.ftl", model); break; } sendMail(mailSender, mailFrom, email, subject, message); } + private String toEnabledValueLabel(ApiFeature apiFeature) { + switch (apiFeature) { + case DB: + return "save"; + case TRANSPORT: + return "receive"; + case JS: + case RE: + return "invoke"; + default: + throw new RuntimeException("Not implemented!"); + } + } + private String toDisabledValueLabel(ApiFeature apiFeature) { switch (apiFeature) { case DB: @@ -288,17 +306,17 @@ public class DefaultMailService implements MailService { } } - private String toDisabledValueLabel(ApiUsageStateMailMessage msg) { - switch (msg.getKey()) { + private String toDisabledValueLabel(ApiUsageRecordKey key, long value) { + switch (key) { case STORAGE_DP_COUNT: case TRANSPORT_DP_COUNT: - return (msg.getThreshold() / 1000000) + "M data points"; + return (value / 1000000) + "M data points"; case TRANSPORT_MSG_COUNT: - return (msg.getThreshold() / 1000000) + "M messages"; + return (value / 1000000) + "M messages"; case JS_EXEC_COUNT: - return (msg.getThreshold() / 1000000) + "M JavaScript functions"; + return (value / 1000000) + "M JavaScript functions"; case RE_EXEC_COUNT: - return (msg.getThreshold() / 1000000) + "M Rule Engine nodes"; + return (value / 1000000) + "M Rule Engine nodes"; default: throw new RuntimeException("Not implemented!"); } diff --git a/application/src/main/resources/templates/state.enabled.ftl b/application/src/main/resources/templates/state.enabled.ftl index 497b3cd780..52db9683b7 100644 --- a/application/src/main/resources/templates/state.enabled.ftl +++ b/application/src/main/resources/templates/state.enabled.ftl @@ -15,112 +15,141 @@ limitations under the License. --> - - + + - - -Thingsboard - Api Usage State + + + Thingsboard - Api Usage State - + - + - -
-
- - -
- - - - - - - - <#list apiUsageStateMailMessages as msg> - - - - - - - - - -
-

Thingsboard Api Usage State for tenant has been updated

-
- Thingsboard Usage state ${apiFeature} was updated to status ENABLED. -
- ${msg.key.apiLimitKey} = ${msg.threshold} -
- ${msg.key.apiCountKey} = ${msg.value} -
- — The Thingsboard -
- -
+ + + + - + +
+ + + + + + + + + + + + +
+

Your ThingsBoard account feature was enabled

+
We have enabled the ${apiFeature} for your account and ThingsBoard already able to ${apiLabel} messages. +
— The ThingsBoard +
+ + + + + +
This email was sent to ${targetEmail} by ThingsBoard. +
diff --git a/application/src/main/resources/templates/state.warning.ftl b/application/src/main/resources/templates/state.warning.ftl index bd4424a0b9..1a2787a9f5 100644 --- a/application/src/main/resources/templates/state.warning.ftl +++ b/application/src/main/resources/templates/state.warning.ftl @@ -15,112 +15,148 @@ limitations under the License. --> - - + + - - -Thingsboard - Api Usage State + + + Thingsboard - Api Usage State - + - + - -
-
- - -
- - - - - - - - <#list apiUsageStateMailMessages as msg> - - - - - - - - - -
-

Thingsboard Api Usage State for tenant has been updated

-
- Thingsboard Usage state ${apiFeature} was updated to status WARNING. -
- ${msg.key.apiLimitKey} = ${msg.threshold} -
- ${msg.key.apiCountKey} = ${msg.value} -
- — The Thingsboard -
- -
+ + + + - + +
+ + + + + + + + + + + + + + + +
+

Your ThingsBoard account feature may be disabled

+
+ Your ${apiFeature} limit (${apiLimitValueLabel}) is almost exhausted.
ThingsBoard has already ${apiValueLabel}.
${apiFeature} will be disabled for your account when limit will be reached. +
Please contact your system administrator to resolve the issue. +
— The ThingsBoard +
+ + + + + +
This email was sent to ${targetEmail} by ThingsBoard. +