diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index b3cdb6daa2..bfe28e2148 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -441,6 +441,42 @@ + + {{ 'notification.api-usage-trigger-settings' | translate }} +
+
+ + api-usage.api-features + + + {{ apiFeatureTranslationMap.get(apiFeature) | translate }} + + + {{ 'notification.api-feature-hint' | translate }} + + + notification.notify-on + + + {{ apiUsageStateValueTranslationMap.get(apiUsageStateValue) | translate }} + + + + {{ 'notification.notify-on-required' | translate }} + + +
+
+
+
+ + notification.description + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts index 4658ab62ef..2a3af20859 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts @@ -20,7 +20,9 @@ import { AlarmAssignmentAction, AlarmAssignmentActionTranslationMap, ComponentLifecycleEvent, - ComponentLifecycleEventTranslationMap, DeviceEvent, DeviceEventTranslationMap, + ComponentLifecycleEventTranslationMap, + DeviceEvent, + DeviceEventTranslationMap, NotificationRule, NotificationTarget, TriggerType, @@ -58,6 +60,12 @@ import { AuthState } from '@core/auth/auth.models'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { AuthUser } from '@shared/models/user.model'; import { Authority } from '@shared/models/authority.enum'; +import { + ApiFeature, + ApiFeatureTranslationMap, + ApiUsageStateValue, + ApiUsageStateValueTranslationMap +} from '@shared/models/api-usage.models'; export interface RuleNotificationDialogData { rule?: NotificationRule; @@ -85,6 +93,7 @@ export class RuleNotificationDialogComponent extends alarmAssignmentTemplateForm: FormGroup; ruleEngineEventsTemplateForm: FormGroup; entitiesLimitTemplateForm: FormGroup; + apiUsageLimitTemplateForm: FormGroup; triggerType = TriggerType; triggerTypes: TriggerType[]; @@ -113,6 +122,12 @@ export class RuleNotificationDialogComponent extends deviceEvents: DeviceEvent[] = Object.values(DeviceEvent); deviceEventTranslationMap = DeviceEventTranslationMap; + apiUsageStateValues: ApiUsageStateValue[] = Object.values(ApiUsageStateValue); + apiUsageStateValueTranslationMap = ApiUsageStateValueTranslationMap; + + apiFeatures: ApiFeature[] = Object.values(ApiFeature); + apiFeatureTranslationMap = ApiFeatureTranslationMap; + entityType = EntityType; entityTypes = Array.from(entityTypeTranslations.keys()).filter(type => !!this.entityType[type]); isAdd = true; @@ -263,6 +278,13 @@ export class RuleNotificationDialogComponent extends }) }); + this.apiUsageLimitTemplateForm = this.fb.group({ + triggerConfig: this.fb.group({ + apiFeatures: [[]], + notifyOn: [[ApiUsageStateValue.WARNING], Validators.required] + }) + }); + this.triggerTypeFormsMap = new Map([ [TriggerType.ALARM, this.alarmTemplateForm], [TriggerType.ALARM_COMMENT, this.alarmCommentTemplateForm], @@ -270,7 +292,8 @@ export class RuleNotificationDialogComponent extends [TriggerType.ENTITY_ACTION, this.entityActionTemplateForm], [TriggerType.ALARM_ASSIGNMENT, this.alarmAssignmentTemplateForm], [TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, this.ruleEngineEventsTemplateForm], - [TriggerType.ENTITIES_LIMIT, this.entitiesLimitTemplateForm] + [TriggerType.ENTITIES_LIMIT, this.entitiesLimitTemplateForm], + [TriggerType.API_USAGE_LIMIT, this.apiUsageLimitTemplateForm] ]); if (data.isAdd || data.isCopy) { @@ -401,8 +424,8 @@ export class RuleNotificationDialogComponent extends private allowTriggerTypes(): TriggerType[] { if (this.isSysAdmin()) { - return [TriggerType.ENTITIES_LIMIT]; + return [TriggerType.ENTITIES_LIMIT, TriggerType.API_USAGE_LIMIT]; } - return Object.values(TriggerType).filter(type => type !== TriggerType.ENTITIES_LIMIT); + return Object.values(TriggerType).filter(type => type !== TriggerType.ENTITIES_LIMIT && type !== TriggerType.API_USAGE_LIMIT); } } diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html index af7e481e28..6d21dc4a39 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html @@ -342,14 +342,14 @@ -
-
- {{ user.firstName }} {{ user.lastName }} ({{ user.email }}) + + + {{ user.firstName }} {{ user.lastName }} {{ user.email }} -
-
+ + diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts index f7f7a41add..122169ed07 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts @@ -176,8 +176,9 @@ export class TemplateNotificationDialogComponent private allowNotificationType(): NotificationType[] { if (this.isSysAdmin()) { - return [NotificationType.GENERAL, NotificationType.ENTITIES_LIMIT]; + return [NotificationType.GENERAL, NotificationType.ENTITIES_LIMIT, NotificationType.API_USAGE_LIMIT]; } - return Object.values(NotificationType).filter(type => type !== NotificationType.ENTITIES_LIMIT); + return Object.values(NotificationType) + .filter(type => type !== NotificationType.ENTITIES_LIMIT && type !== NotificationType.API_USAGE_LIMIT); } } diff --git a/ui-ngx/src/app/shared/models/api-usage.models.ts b/ui-ngx/src/app/shared/models/api-usage.models.ts new file mode 100644 index 0000000000..bbc0ff5597 --- /dev/null +++ b/ui-ngx/src/app/shared/models/api-usage.models.ts @@ -0,0 +1,47 @@ +/// +/// Copyright © 2016-2023 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. +/// + +export enum ApiUsageStateValue { + ENABLED = 'ENABLED', + WARNING = 'WARNING', + DISABLED = 'DISABLED' +} + +export const ApiUsageStateValueTranslationMap = new Map([ + [ApiUsageStateValue.ENABLED, 'notification.enabled'], + [ApiUsageStateValue.WARNING, 'notification.warning'], + [ApiUsageStateValue.DISABLED, 'notification.disabled'], +]); + +export enum ApiFeature { + TRANSPORT = 'TRANSPORT', + DB = 'DB', + RE = 'RE', + JS = 'JS', + EMAIL = 'EMAIL', + SMS = 'SMS', + ALARM = 'ALARM' +} + +export const ApiFeatureTranslationMap = new Map([ + [ApiFeature.TRANSPORT, 'api-usage.device-api'], + [ApiFeature.DB, 'api-usage.telemetry-persistence'], + [ApiFeature.RE, 'api-usage.rule-engine-executions'], + [ApiFeature.JS, 'api-usage.javascript-executions'], + [ApiFeature.EMAIL, 'api-usage.email-messages'], + [ApiFeature.SMS, 'api-usage.sms-messages'], + [ApiFeature.ALARM, 'api-usage.alarm'], +]); diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index 31a3c03cc1..8a8d8d6683 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -26,6 +26,7 @@ import { NotificationRuleId } from '@shared/models/id/notification-rule-id'; import { AlarmSearchStatus, AlarmSeverity, AlarmStatus } from '@shared/models/alarm.models'; import { EntityType } from '@shared/models/entity-type.models'; import { User } from '@shared/models/user.model'; +import { ApiFeature, ApiUsageStateValue } from '@shared/models/api-usage.models'; export interface Notification { readonly id: NotificationId; @@ -113,7 +114,8 @@ export interface NotificationRule extends Omit, 'la export type NotificationRuleTriggerConfig = Partial; + RuleEngineLifecycleEventNotificationRuleTriggerConfig & EntitiesLimitNotificationRuleTriggerConfig & + ApiUsageLimitNotificationRuleTriggerConfig>; export interface AlarmNotificationRuleTriggerConfig { alarmTypes?: Array; @@ -132,7 +134,7 @@ export interface DeviceInactivityNotificationRuleTriggerConfig { } export interface EntityActionNotificationRuleTriggerConfig { - entityType: EntityType; + entityTypes: EntityType[]; created: boolean; updated: boolean; deleted: boolean; @@ -167,6 +169,11 @@ export interface EntitiesLimitNotificationRuleTriggerConfig { threshold: number; } +export interface ApiUsageLimitNotificationRuleTriggerConfig { + apiFeatures: ApiFeature[]; + notifyOn: ApiUsageStateValue[]; +} + export enum ComponentLifecycleEvent { STARTED = 'STARTED', UPDATED = 'UPDATED', @@ -430,7 +437,8 @@ export enum NotificationType { ALARM_COMMENT = 'ALARM_COMMENT', ALARM_ASSIGNMENT = 'ALARM_ASSIGNMENT', RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT = 'RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT', - ENTITIES_LIMIT = 'ENTITIES_LIMIT' + ENTITIES_LIMIT = 'ENTITIES_LIMIT', + API_USAGE_LIMIT = 'API_USAGE_LIMIT' } export const NotificationTypeIcons = new Map([ @@ -441,6 +449,7 @@ export const NotificationTypeIcons = new Map([ [NotificationType.ALARM_ASSIGNMENT, 'assignment_turned_in'], [NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'], [NotificationType.ENTITIES_LIMIT, 'data_thresholding'], + [NotificationType.API_USAGE_LIMIT, 'insert_chart'], ]); export const AlarmSeverityNotificationColors = new Map( @@ -515,6 +524,11 @@ export const NotificationTemplateTypeTranslateMap = new Map([ @@ -536,4 +551,5 @@ export const TriggerTypeTranslationMap = new Map([ [TriggerType.ALARM_ASSIGNMENT, 'notification.trigger.alarm-assignment'], [TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'notification.trigger.rule-engine-lifecycle-event'], [TriggerType.ENTITIES_LIMIT, 'notification.trigger.entities-limit'], + [TriggerType.API_USAGE_LIMIT, 'notification.trigger.api-usage-limit'], ]); diff --git a/ui-ngx/src/app/shared/models/public-api.ts b/ui-ngx/src/app/shared/models/public-api.ts index bfc430db09..1629c656da 100644 --- a/ui-ngx/src/app/shared/models/public-api.ts +++ b/ui-ngx/src/app/shared/models/public-api.ts @@ -20,6 +20,7 @@ export * from './telemetry/telemetry.models'; export * from './time/time.models'; export * from './alarm.models'; export * from './alias.models'; +export * from './api-usage.models'; export * from './asset.models'; export * from './audit-log.models'; export * from './authority.enum'; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a0f95cf0e6..6e5306358e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -673,6 +673,7 @@ "no-telemetry-text": "No telemetry found" }, "api-usage": { + "api-features": "API features", "api-usage": "Api Usage", "alarm": "Alarm", "alarms-created": "Alarms created", @@ -681,6 +682,7 @@ "alarms-created-monthly-activity": "Alarms created monthly activity", "data-points": "Data points", "data-points-storage-days": "Data points storage days", + "device-api": "Device API", "email": "Email", "email-messages": "Email messages", "email-messages-daily-activity": "Email messages daily activity", @@ -2742,6 +2744,8 @@ "alarm-comment-trigger-settings": "Alarm comment trigger settings", "alarm-trigger-settings": "Alarm trigger settings", "all": "All", + "api-feature-hint": "If the field is empty, the trigger will be applied to all api features", + "api-usage-trigger-settings": "API usage trigger settings", "at-least-one-should-be-selected": "At least one should be selected", "basic-settings": "Basic settings", "button-text": "Button text", @@ -2795,10 +2799,12 @@ "device-activity-trigger-settings": "Device active trigger settings", "device-list-rule-hint": "If the field is empty, the trigger will be applied to all devices", "device-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all device profiles", + "disabled": "Disabled", "edit-notification-recipients-group": "Edit notification recipients group", "edit-notification-template": "Edit notification template", "edit-rule": "Edit rule", "edit-template": "Edit template", + "enabled": "Enabled", "entities-limit-trigger-settings": "Entities limit trigger settings", "entity-action-trigger-settings": "Entity action trigger settings", "entity-type": "Entity type", @@ -2921,6 +2927,7 @@ "alarm": "Alarm", "alarm-assignment": "Alarm assignment", "alarm-comment": "Alarm comment", + "api-usage-limit": "API usage limit", "device-activity": "Device activity", "entities-limit": "Entities limit", "entity-action": "Entity action", @@ -2937,6 +2944,7 @@ "alarm": "Alarm", "alarm-assignment": "Alarm assignment", "alarm-comment": "Alarm comment", + "api-usage-limit": "API usage limit", "device-activity": "Device activity", "entities-limit": "Entities limit", "entity-action": "Entity action", @@ -2948,7 +2956,8 @@ "unread": "Unread", "updated": "Updated", "use-template": "Use template", - "view-all": "View all" + "view-all": "View all", + "warning": "Warning" }, "ota-update": { "add": "Add package",