diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 4548551937..cd7e89cd6e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -155,6 +155,8 @@ public abstract class BaseController { /*Swagger UI description*/ + protected static final String NEW_LINE = "\n\n"; + public static final String CUSTOMER_ID = "customerId"; public static final String TENANT_ID = "tenantId"; public static final String DEVICE_ID = "deviceId"; @@ -263,7 +265,7 @@ public abstract class BaseController { protected static final String EVENT_END_TIME_DESCRIPTION = "Timestamp. Events with creation time after it won't be queried."; protected static final String EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION = "Unassignment works in async way - first, 'unassign' notification event pushed to edge queue on platform. "; - protected static final String EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION = "(Edge will receive this instantly, if it's currently connected, or once it's going to be connected to platform)" ; + protected static final String EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION = "(Edge will receive this instantly, if it's currently connected, or once it's going to be connected to platform)"; protected static final String EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION = "Assignment works in async way - first, notification event pushed to edge service queue on platform. "; protected static final String EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION = "(Edge will receive this instantly, if it's currently connected, or once it's going to be connected to platform)"; @@ -281,6 +283,114 @@ public abstract class BaseController { protected static final String EVENT_DEBUG_RULE_NODE_FILTER_OBJ = MARKDOWN_CODE_BLOCK_START + "{ \"eventType\": \"DEBUG_RULE_NODE\"," + DEBUG_FILTER_OBJ + MARKDOWN_CODE_BLOCK_END; protected static final String EVENT_DEBUG_RULE_CHAIN_FILTER_OBJ = MARKDOWN_CODE_BLOCK_START + "{ \"eventType\": \"DEBUG_RULE_CHAIN\"," + DEBUG_FILTER_OBJ + MARKDOWN_CODE_BLOCK_END; + protected static final String FILTER_VALUE_TYPE = NEW_LINE + "## Value Type and Operations" + NEW_LINE + + "Provides a hint about the data type of the entity field that is defined in the filter key. " + + "The value type impacts the list of possible operations that you may use in the corresponding predicate. For example, you may use 'STARTS_WITH' or 'END_WITH', but you can't use 'GREATER_OR_EQUAL' for string values." + + "The following filter value types and corresponding predicate operations are supported: " + NEW_LINE + + " * 'STRING' - used to filter any 'String' or 'JSON' values. Operations: EQUAL, NOT_EQUAL, STARTS_WITH, ENDS_WITH, CONTAINS, NOT_CONTAINS; \n" + + " * 'NUMERIC' - used for 'Long' and 'Double' values. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n" + + " * 'BOOLEAN' - used for boolean values. Operations: EQUAL, NOT_EQUAL;\n" + + " * 'DATE_TIME' - similar to numeric, transforms value to milliseconds since epoch. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n"; + + protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_SPECIFIC_TIME_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"schedule\":{\n" + + " \"type\":\"SPECIFIC_TIME\",\n" + + " \"endsOn\":64800000,\n" + + " \"startsOn\":43200000,\n" + + " \"timezone\":\"Europe/Kiev\",\n" + + " \"daysOfWeek\":[\n" + + " 1,\n" + + " 3,\n" + + " 5\n" + + " ]\n" + + " }\n" + + "}" + + MARKDOWN_CODE_BLOCK_END; + protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_CUSTOM_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"schedule\":{\n" + + " \"type\":\"CUSTOM\",\n" + + " \"items\":[\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":1\n" + + " },\n" + + " {\n" + + " \"endsOn\":64800000,\n" + + " \"enabled\":true,\n" + + " \"startsOn\":43200000,\n" + + " \"dayOfWeek\":2\n" + + " },\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":3\n" + + " },\n" + + " {\n" + + " \"endsOn\":57600000,\n" + + " \"enabled\":true,\n" + + " \"startsOn\":36000000,\n" + + " \"dayOfWeek\":4\n" + + " },\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":5\n" + + " },\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":6\n" + + " },\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":7\n" + + " }\n" + + " ],\n" + + " \"timezone\":\"Europe/Kiev\"\n" + + " }\n" + + "}" + + MARKDOWN_CODE_BLOCK_END; + protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_ALWAYS_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "\"schedule\": null" + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_PROFILE_ALARM_CONDITION_REPEATING_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"spec\":{\n" + + " \"type\":\"REPEATING\",\n" + + " \"predicate\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":5,\n" + + " \"dynamicValue\":{\n" + + " \"inherit\":true,\n" + + " \"sourceType\":\"CURRENT_DEVICE\",\n" + + " \"sourceAttribute\":\"tempAttr\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}" + + MARKDOWN_CODE_BLOCK_END; + protected static final String DEVICE_PROFILE_ALARM_CONDITION_DURATION_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"spec\":{\n" + + " \"type\":\"DURATION\",\n" + + " \"unit\":\"MINUTES\",\n" + + " \"predicate\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":30,\n" + + " \"dynamicValue\":null\n" + + " }\n" + + " }\n" + + "}" + + MARKDOWN_CODE_BLOCK_END; + protected static final String RELATION_TYPE_PARAM_DESCRIPTION = "A string value representing relation type between entities. For example, 'Contains', 'Manages'. It can be any string value."; protected static final String RELATION_TYPE_GROUP_PARAM_DESCRIPTION = "A string value representing relation type group. For example, 'COMMON'"; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 71719531d5..9eb9ed4f84 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -55,6 +55,453 @@ import java.util.UUID; @Slf4j public class DeviceProfileController extends BaseController { + private static final String COAP_TRANSPORT_CONFIGURATION_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"type\":\"COAP\",\n" + + " \"clientSettings\":{\n" + + " \"edrxCycle\":null,\n" + + " \"powerMode\":\"DRX\",\n" + + " \"psmActivityTimer\":null,\n" + + " \"pagingTransmissionWindow\":null\n" + + " },\n" + + " \"coapDeviceTypeConfiguration\":{\n" + + " \"coapDeviceType\":\"DEFAULT\",\n" + + " \"transportPayloadTypeConfiguration\":{\n" + + " \"transportPayloadType\":\"JSON\"\n" + + " }\n" + + " }\n" + + "}" + + MARKDOWN_CODE_BLOCK_END; + + private static final String TRANSPORT_CONFIGURATION = "# Transport Configuration" + NEW_LINE + + "5 transport configuration types are available:\n" + + " * 'DEFAULT';\n" + + " * 'MQTT';\n" + + " * 'LWM2M';\n" + + " * 'COAP';\n" + + " * 'SNMP'." + NEW_LINE + "Default type supports basic MQTT, HTTP, CoAP and LwM2M transports. " + + "Please refer to the [docs](https://thingsboard.io/docs/user-guide/device-profiles/#transport-configuration) for more details about other types.\n" + + "\nSee another example of COAP transport configuration below:" + NEW_LINE + COAP_TRANSPORT_CONFIGURATION_EXAMPLE; + + private static final String ALARM_FILTER_KEY = "## Alarm Filter Key" + NEW_LINE + + "Filter Key defines either entity field, attribute, telemetry or constant. It is a JSON object that consists the key name and type. The following filter key types are supported:\n" + + " * 'ATTRIBUTE' - used for attributes values;\n" + + " * 'TIME_SERIES' - used for time-series values;\n" + + " * 'ENTITY_FIELD' - used for accessing entity fields like 'name', 'label', etc. The list of available fields depends on the entity type;\n" + + " * 'CONSTANT' - constant value specified." + NEW_LINE + "Let's review the example:" + NEW_LINE + + MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"type\": \"TIME_SERIES\",\n" + + " \"key\": \"temperature\"\n" + + "}" + + MARKDOWN_CODE_BLOCK_END; + + private static final String FILTER_PREDICATE = NEW_LINE + "## Filter Predicate" + NEW_LINE + + "Filter Predicate defines the logical expression to evaluate. The list of available operations depends on the filter value type, see above. " + + "Platform supports 4 predicate types: 'STRING', 'NUMERIC', 'BOOLEAN' and 'COMPLEX'. The last one allows to combine multiple operations over one filter key." + NEW_LINE + + "Simple predicate example to check 'value < 100': " + NEW_LINE + + MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"operation\": \"LESS\",\n" + + " \"value\": {\n" + + " \"userValue\": null,\n" + + " \"defaultValue\": 100,\n" + + " \"dynamicValue\": null\n" + + " },\n" + + " \"type\": \"NUMERIC\"\n" + + "}" + + MARKDOWN_CODE_BLOCK_END + NEW_LINE + + "Complex predicate example, to check 'value < 10 or value > 20': " + NEW_LINE + + MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"type\": \"COMPLEX\",\n" + + " \"operation\": \"OR\",\n" + + " \"predicates\": [\n" + + " {\n" + + " \"operation\": \"LESS\",\n" + + " \"value\": {\n" + + " \"userValue\": null,\n" + + " \"defaultValue\": 10,\n" + + " \"dynamicValue\": null\n" + + " },\n" + + " \"type\": \"NUMERIC\"\n" + + " },\n" + + " {\n" + + " \"operation\": \"GREATER\",\n" + + " \"value\": {\n" + + " \"userValue\": null,\n" + + " \"defaultValue\": 20,\n" + + " \"dynamicValue\": null\n" + + " },\n" + + " \"type\": \"NUMERIC\"\n" + + " }\n" + + " ]\n" + + "}" + + MARKDOWN_CODE_BLOCK_END + NEW_LINE + + "More complex predicate example, to check 'value < 10 or (value > 50 && value < 60)': " + NEW_LINE + + MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"type\": \"COMPLEX\",\n" + + " \"operation\": \"OR\",\n" + + " \"predicates\": [\n" + + " {\n" + + " \"operation\": \"LESS\",\n" + + " \"value\": {\n" + + " \"userValue\": null,\n" + + " \"defaultValue\": 10,\n" + + " \"dynamicValue\": null\n" + + " },\n" + + " \"type\": \"NUMERIC\"\n" + + " },\n" + + " {\n" + + " \"type\": \"COMPLEX\",\n" + + " \"operation\": \"AND\",\n" + + " \"predicates\": [\n" + + " {\n" + + " \"operation\": \"GREATER\",\n" + + " \"value\": {\n" + + " \"userValue\": null,\n" + + " \"defaultValue\": 50,\n" + + " \"dynamicValue\": null\n" + + " },\n" + + " \"type\": \"NUMERIC\"\n" + + " },\n" + + " {\n" + + " \"operation\": \"LESS\",\n" + + " \"value\": {\n" + + " \"userValue\": null,\n" + + " \"defaultValue\": 60,\n" + + " \"dynamicValue\": null\n" + + " },\n" + + " \"type\": \"NUMERIC\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}" + + MARKDOWN_CODE_BLOCK_END + NEW_LINE + + "You may also want to replace hardcoded values (for example, temperature > 20) with the more dynamic " + + "expression (for example, temperature > value of the tenant attribute with key 'temperatureThreshold'). " + + "It is possible to use 'dynamicValue' to define attribute of the tenant, customer or device. " + + "See example below:" + NEW_LINE + + MARKDOWN_CODE_BLOCK_START + + "{\n" + + " \"operation\": \"GREATER\",\n" + + " \"value\": {\n" + + " \"userValue\": null,\n" + + " \"defaultValue\": 0,\n" + + " \"dynamicValue\": {\n" + + " \"inherit\": false,\n" + + " \"sourceType\": \"CURRENT_TENANT\",\n" + + " \"sourceAttribute\": \"temperatureThreshold\"\n" + + " }\n" + + " },\n" + + " \"type\": \"NUMERIC\"\n" + + "}" + + MARKDOWN_CODE_BLOCK_END + NEW_LINE + + "Note that you may use 'CURRENT_DEVICE', 'CURRENT_CUSTOMER' and 'CURRENT_TENANT' as a 'sourceType'. The 'defaultValue' is used when the attribute with such a name is not defined for the chosen source. " + + "The 'sourceAttribute' can be inherited from the owner of the specified 'sourceType' if 'inherit' is set to true."; + + private static final String KEY_FILTERS_DESCRIPTION = "# Key Filters" + NEW_LINE + + "Key filter objects are created under the **'condition'** array. They allow you to define complex logical expressions over entity field, " + + "attribute, latest time-series value or constant. The filter is defined using 'key', 'valueType', " + + "'value' (refers to the value of the 'CONSTANT' alarm filter key type) and 'predicate' objects. Let's review each object:" + NEW_LINE + + ALARM_FILTER_KEY + FILTER_VALUE_TYPE + NEW_LINE + FILTER_PREDICATE + NEW_LINE; + + private static final String DEFAULT_DEVICE_PROFILE_DATA_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "{\n" + + " \"alarms\":[\n" + + " ],\n" + + " \"configuration\":{\n" + + " \"type\":\"DEFAULT\"\n" + + " },\n" + + " \"provisionConfiguration\":{\n" + + " \"type\":\"DISABLED\",\n" + + " \"provisionDeviceSecret\":null\n" + + " },\n" + + " \"transportConfiguration\":{\n" + + " \"type\":\"DEFAULT\"\n" + + " }\n" + + "}" + MARKDOWN_CODE_BLOCK_END; + + private static final String CUSTOM_DEVICE_PROFILE_DATA_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "{\n" + + " \"alarms\":[\n" + + " {\n" + + " \"id\":\"2492b935-1226-59e9-8615-17d8978a4f93\",\n" + + " \"alarmType\":\"Temperature Alarm\",\n" + + " \"clearRule\":{\n" + + " \"schedule\":null,\n" + + " \"condition\":{\n" + + " \"spec\":{\n" + + " \"type\":\"SIMPLE\"\n" + + " },\n" + + " \"condition\":[\n" + + " {\n" + + " \"key\":{\n" + + " \"key\":\"temperature\",\n" + + " \"type\":\"TIME_SERIES\"\n" + + " },\n" + + " \"value\":null,\n" + + " \"predicate\":{\n" + + " \"type\":\"NUMERIC\",\n" + + " \"value\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":30.0,\n" + + " \"dynamicValue\":null\n" + + " },\n" + + " \"operation\":\"LESS\"\n" + + " },\n" + + " \"valueType\":\"NUMERIC\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"dashboardId\":null,\n" + + " \"alarmDetails\":null\n" + + " },\n" + + " \"propagate\":false,\n" + + " \"createRules\":{\n" + + " \"MAJOR\":{\n" + + " \"schedule\":{\n" + + " \"type\":\"SPECIFIC_TIME\",\n" + + " \"endsOn\":64800000,\n" + + " \"startsOn\":43200000,\n" + + " \"timezone\":\"Europe/Kiev\",\n" + + " \"daysOfWeek\":[\n" + + " 1,\n" + + " 3,\n" + + " 5\n" + + " ]\n" + + " },\n" + + " \"condition\":{\n" + + " \"spec\":{\n" + + " \"type\":\"DURATION\",\n" + + " \"unit\":\"MINUTES\",\n" + + " \"predicate\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":30,\n" + + " \"dynamicValue\":null\n" + + " }\n" + + " },\n" + + " \"condition\":[\n" + + " {\n" + + " \"key\":{\n" + + " \"key\":\"temperature\",\n" + + " \"type\":\"TIME_SERIES\"\n" + + " },\n" + + " \"value\":null,\n" + + " \"predicate\":{\n" + + " \"type\":\"COMPLEX\",\n" + + " \"operation\":\"OR\",\n" + + " \"predicates\":[\n" + + " {\n" + + " \"type\":\"NUMERIC\",\n" + + " \"value\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":50.0,\n" + + " \"dynamicValue\":null\n" + + " },\n" + + " \"operation\":\"LESS_OR_EQUAL\"\n" + + " },\n" + + " {\n" + + " \"type\":\"NUMERIC\",\n" + + " \"value\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":30.0,\n" + + " \"dynamicValue\":null\n" + + " },\n" + + " \"operation\":\"GREATER\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"valueType\":\"NUMERIC\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"dashboardId\":null,\n" + + " \"alarmDetails\":null\n" + + " },\n" + + " \"WARNING\":{\n" + + " \"schedule\":{\n" + + " \"type\":\"CUSTOM\",\n" + + " \"items\":[\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":1\n" + + " },\n" + + " {\n" + + " \"endsOn\":64800000,\n" + + " \"enabled\":true,\n" + + " \"startsOn\":43200000,\n" + + " \"dayOfWeek\":2\n" + + " },\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":3\n" + + " },\n" + + " {\n" + + " \"endsOn\":57600000,\n" + + " \"enabled\":true,\n" + + " \"startsOn\":36000000,\n" + + " \"dayOfWeek\":4\n" + + " },\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":5\n" + + " },\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":6\n" + + " },\n" + + " {\n" + + " \"endsOn\":0,\n" + + " \"enabled\":false,\n" + + " \"startsOn\":0,\n" + + " \"dayOfWeek\":7\n" + + " }\n" + + " ],\n" + + " \"timezone\":\"Europe/Kiev\"\n" + + " },\n" + + " \"condition\":{\n" + + " \"spec\":{\n" + + " \"type\":\"REPEATING\",\n" + + " \"predicate\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":5,\n" + + " \"dynamicValue\":null\n" + + " }\n" + + " },\n" + + " \"condition\":[\n" + + " {\n" + + " \"key\":{\n" + + " \"key\":\"tempConstant\",\n" + + " \"type\":\"CONSTANT\"\n" + + " },\n" + + " \"value\":30,\n" + + " \"predicate\":{\n" + + " \"type\":\"NUMERIC\",\n" + + " \"value\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":0.0,\n" + + " \"dynamicValue\":{\n" + + " \"inherit\":false,\n" + + " \"sourceType\":\"CURRENT_DEVICE\",\n" + + " \"sourceAttribute\":\"tempThreshold\"\n" + + " }\n" + + " },\n" + + " \"operation\":\"EQUAL\"\n" + + " },\n" + + " \"valueType\":\"NUMERIC\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"dashboardId\":null,\n" + + " \"alarmDetails\":null\n" + + " },\n" + + " \"CRITICAL\":{\n" + + " \"schedule\":null,\n" + + " \"condition\":{\n" + + " \"spec\":{\n" + + " \"type\":\"SIMPLE\"\n" + + " },\n" + + " \"condition\":[\n" + + " {\n" + + " \"key\":{\n" + + " \"key\":\"temperature\",\n" + + " \"type\":\"TIME_SERIES\"\n" + + " },\n" + + " \"value\":null,\n" + + " \"predicate\":{\n" + + " \"type\":\"NUMERIC\",\n" + + " \"value\":{\n" + + " \"userValue\":null,\n" + + " \"defaultValue\":50.0,\n" + + " \"dynamicValue\":null\n" + + " },\n" + + " \"operation\":\"GREATER\"\n" + + " },\n" + + " \"valueType\":\"NUMERIC\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"dashboardId\":null,\n" + + " \"alarmDetails\":null\n" + + " }\n" + + " },\n" + + " \"propagateRelationTypes\":null\n" + + " }\n" + + " ],\n" + + " \"configuration\":{\n" + + " \"type\":\"DEFAULT\"\n" + + " },\n" + + " \"provisionConfiguration\":{\n" + + " \"type\":\"ALLOW_CREATE_NEW_DEVICES\",\n" + + " \"provisionDeviceSecret\":\"vaxb9hzqdbz3oqukvomg\"\n" + + " },\n" + + " \"transportConfiguration\":{\n" + + " \"type\":\"MQTT\",\n" + + " \"deviceTelemetryTopic\":\"v1/devices/me/telemetry\",\n" + + " \"deviceAttributesTopic\":\"v1/devices/me/attributes\",\n" + + " \"transportPayloadTypeConfiguration\":{\n" + + " \"transportPayloadType\":\"PROTOBUF\",\n" + + " \"deviceTelemetryProtoSchema\":\"syntax =\\\"proto3\\\";\\npackage telemetry;\\n\\nmessage SensorDataReading {\\n\\n optional double temperature = 1;\\n optional double humidity = 2;\\n InnerObject innerObject = 3;\\n\\n message InnerObject {\\n optional string key1 = 1;\\n optional bool key2 = 2;\\n optional double key3 = 3;\\n optional int32 key4 = 4;\\n optional string key5 = 5;\\n }\\n}\",\n" + + " \"deviceAttributesProtoSchema\":\"syntax =\\\"proto3\\\";\\npackage attributes;\\n\\nmessage SensorConfiguration {\\n optional string firmwareVersion = 1;\\n optional string serialNumber = 2;\\n}\",\n" + + " \"deviceRpcRequestProtoSchema\":\"syntax =\\\"proto3\\\";\\npackage rpc;\\n\\nmessage RpcRequestMsg {\\n optional string method = 1;\\n optional int32 requestId = 2;\\n optional string params = 3;\\n}\",\n" + + " \"deviceRpcResponseProtoSchema\":\"syntax =\\\"proto3\\\";\\npackage rpc;\\n\\nmessage RpcResponseMsg {\\n optional string payload = 1;\\n}\"\n" + + " }\n" + + " }\n" + + "}" + MARKDOWN_CODE_BLOCK_END; + private static final String DEVICE_PROFILE_DATA_DEFINITION = NEW_LINE + "# Device profile data definition" + NEW_LINE + + "Device profile data object contains alarm rules configuration, device provision strategy and transport type configuration for device connectivity. Let's review some examples. " + + "First one is the default device profile data configuration and second one - the custom one. " + + NEW_LINE + DEFAULT_DEVICE_PROFILE_DATA_EXAMPLE + NEW_LINE + CUSTOM_DEVICE_PROFILE_DATA_EXAMPLE + + NEW_LINE + "Let's review some specific objects examples related to the device profile configuration:"; + + private static final String ALARM_SCHEDULE = NEW_LINE + "# Alarm Schedule" + NEW_LINE + + "Alarm Schedule JSON object represents the time interval during which the alarm rule is active. Note, " + + NEW_LINE + DEVICE_PROFILE_ALARM_SCHEDULE_ALWAYS_EXAMPLE + NEW_LINE + "means alarm rule is active all the time. " + + "**'daysOfWeek'** field represents Monday as 1, Tuesday as 2 and so on. **'startsOn'** and **'endsOn'** fields represent hours in millis (e.g. 64800000 = 18:00 or 6pm). " + + "**'enabled'** flag specifies if item in a custom rule is active for specific day of the week:" + NEW_LINE + + "## Specific Time Schedule" + NEW_LINE + + DEVICE_PROFILE_ALARM_SCHEDULE_SPECIFIC_TIME_EXAMPLE + NEW_LINE + + "## Custom Schedule" + + NEW_LINE + DEVICE_PROFILE_ALARM_SCHEDULE_CUSTOM_EXAMPLE + NEW_LINE; + + private static final String ALARM_CONDITION_TYPE = "# Alarm condition type (**'spec'**)" + NEW_LINE + + "Alarm condition type can be either simple, duration, or repeating. For example, 5 times in a row or during 5 minutes." + NEW_LINE + + "Note, **'userValue'** field is not used and reserved for future usage, **'dynamicValue'** is used for condition appliance by using the value of the **'sourceAttribute'** " + + "or else **'defaultValue'** is used (if **'sourceAttribute'** is absent).\n" + + "\n**'sourceType'** of the **'sourceAttribute'** can be: \n" + + " * 'CURRENT_DEVICE';\n" + + " * 'CURRENT_CUSTOMER';\n" + + " * 'CURRENT_TENANT'." + NEW_LINE + + "**'sourceAttribute'** can be inherited from the owner if **'inherit'** is set to true (for CURRENT_DEVICE and CURRENT_CUSTOMER)." + NEW_LINE + + "## Repeating alarm condition" + NEW_LINE + + DEVICE_PROFILE_ALARM_CONDITION_REPEATING_EXAMPLE + NEW_LINE + + "## Duration alarm condition" + NEW_LINE + + DEVICE_PROFILE_ALARM_CONDITION_DURATION_EXAMPLE + NEW_LINE + + "**'unit'** can be: \n" + + " * 'SECONDS';\n" + + " * 'MINUTES';\n" + + " * 'HOURS';\n" + + " * 'DAYS'." + NEW_LINE; + + private static final String PROVISION_CONFIGURATION = "# Provision Configuration" + NEW_LINE + + "There are 3 types of device provision configuration for the device profile: \n" + + " * 'DISABLED';\n" + + " * 'ALLOW_CREATE_NEW_DEVICES';\n" + + " * 'CHECK_PRE_PROVISIONED_DEVICES'." + NEW_LINE + + "Please refer to the [docs](https://thingsboard.io/docs/user-guide/device-provisioning/) for more details." + NEW_LINE; + + private static final String DEVICE_PROFILE_DATA = DEVICE_PROFILE_DATA_DEFINITION + ALARM_SCHEDULE + ALARM_CONDITION_TYPE + + KEY_FILTERS_DESCRIPTION + PROVISION_CONFIGURATION + TRANSPORT_CONFIGURATION; + private static final String DEVICE_PROFILE_ID = "deviceProfileId"; @Autowired @@ -169,12 +616,13 @@ public class DeviceProfileController extends BaseController { } } - @ApiOperation(value = "Create Or Update Device Profile (saveDevice)", + @ApiOperation(value = "Create Or Update Device Profile (saveDeviceProfile)", notes = "Create or update the Device Profile. When creating device profile, platform generates device profile id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address). " + "The newly created device profile id will be present in the response. " + "Specify existing device profile id to update the device profile. " + - "Referencing non-existing device profile Id will cause 'Not Found' error. " + - "\n\nDevice profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + TENANT_AUTHORITY_PARAGRAPH, + "Referencing non-existing device profile Id will cause 'Not Found' error. " + NEW_LINE + + "Device profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + DEVICE_PROFILE_DATA + + TENANT_AUTHORITY_PARAGRAPH, produces = "application/json", consumes = "application/json") @PreAuthorize("hasAuthority('TENANT_ADMIN')") diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java index 509da8bc73..46bf285178 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java @@ -45,7 +45,7 @@ import org.thingsboard.server.service.query.EntityQueryService; public class EntityQueryController extends BaseController { private static final String SINGLE_ENTITY = "\n\n## Single Entity\n\n" + - "Allows to filter only one entity based on the id. For example, this entity filter selects certain device:\n\n"+ + "Allows to filter only one entity based on the id. For example, this entity filter selects certain device:\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"singleEntity\",\n" + @@ -53,12 +53,12 @@ public class EntityQueryController extends BaseController { " \"id\": \"d521edb0-2a7a-11ec-94eb-213c95f54092\",\n" + " \"entityType\": \"DEVICE\"\n" + " }\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String ENTITY_LIST = "\n\n## Entity List Filter\n\n" + - "Allows to filter entities of the same type using their ids. For example, this entity filter selects two devices:\n\n"+ + "Allows to filter entities of the same type using their ids. For example, this entity filter selects two devices:\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"entityList\",\n" + @@ -67,84 +67,84 @@ public class EntityQueryController extends BaseController { " \"e6501f30-2a7a-11ec-94eb-213c95f54092\",\n" + " \"e6657bf0-2a7a-11ec-94eb-213c95f54092\"\n" + " ]\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String ENTITY_NAME = "\n\n## Entity Name Filter\n\n" + "Allows to filter entities of the same type using the **'starts with'** expression over entity name. " + - "For example, this entity filter selects all devices which name starts with 'Air Quality':\n\n"+ + "For example, this entity filter selects all devices which name starts with 'Air Quality':\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"entityName\",\n" + " \"entityType\": \"DEVICE\",\n" + " \"entityNameFilter\": \"Air Quality\"\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String ENTITY_TYPE = "\n\n## Entity Type Filter\n\n" + "Allows to filter entities based on their type (CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, etc)" + - "For example, this entity filter selects all tenant customers:\n\n"+ + "For example, this entity filter selects all tenant customers:\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"entityType\",\n" + " \"entityType\": \"CUSTOMER\"\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String ASSET_TYPE = "\n\n## Asset Type Filter\n\n" + "Allows to filter assets based on their type and the **'starts with'** expression over their name. " + - "For example, this entity filter selects all 'charging station' assets which name starts with 'Tesla':\n\n"+ + "For example, this entity filter selects all 'charging station' assets which name starts with 'Tesla':\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"assetType\",\n" + " \"assetType\": \"charging station\",\n" + " \"assetNameFilter\": \"Tesla\"\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String DEVICE_TYPE = "\n\n## Device Type Filter\n\n" + "Allows to filter devices based on their type and the **'starts with'** expression over their name. " + - "For example, this entity filter selects all 'Temperature Sensor' devices which name starts with 'ABC':\n\n"+ + "For example, this entity filter selects all 'Temperature Sensor' devices which name starts with 'ABC':\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"deviceType\",\n" + " \"deviceType\": \"Temperature Sensor\",\n" + " \"deviceNameFilter\": \"ABC\"\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String EDGE_TYPE = "\n\n## Edge Type Filter\n\n" + "Allows to filter edge instances based on their type and the **'starts with'** expression over their name. " + - "For example, this entity filter selects all 'Factory' edge instances which name starts with 'Nevada':\n\n"+ + "For example, this entity filter selects all 'Factory' edge instances which name starts with 'Nevada':\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"edgeType\",\n" + " \"edgeType\": \"Factory\",\n" + " \"edgeNameFilter\": \"Nevada\"\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String ENTITY_VIEW_TYPE = "\n\n## Entity View Filter\n\n" + "Allows to filter entity views based on their type and the **'starts with'** expression over their name. " + - "For example, this entity filter selects all 'Concrete Mixer' entity views which name starts with 'CAT':\n\n"+ + "For example, this entity filter selects all 'Concrete Mixer' entity views which name starts with 'CAT':\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"entityViewType\",\n" + " \"entityViewType\": \"Concrete Mixer\",\n" + " \"entityViewNameFilter\": \"CAT\"\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String API_USAGE = "\n\n## Api Usage Filter\n\n" + "Allows to query for Api Usage based on optional customer id. If the customer id is not set, returns current tenant API usage." + - "For example, this entity filter selects the 'Api Usage' entity for customer with id 'e6501f30-2a7a-11ec-94eb-213c95f54092':\n\n"+ + "For example, this entity filter selects the 'Api Usage' entity for customer with id 'e6501f30-2a7a-11ec-94eb-213c95f54092':\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"apiUsageState\",\n" + @@ -152,7 +152,7 @@ public class EntityQueryController extends BaseController { " \"id\": \"d521edb0-2a7a-11ec-94eb-213c95f54092\",\n" + " \"entityType\": \"CUSTOMER\"\n" + " }\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; @@ -165,7 +165,7 @@ public class EntityQueryController extends BaseController { FETCH_LAST_LEVEL_ONLY_DESCRIPTION + "The 'filter' object allows you to define the relation type and set of acceptable entity types to search for. " + "The relation query calculates all related entities, even if they are filtered using different relation types, and then extracts only those who match the 'filters'.\n\n" + - "For example, this entity filter selects all devices and assets which are related to the asset with id 'e51de0c0-2a7a-11ec-94eb-213c95f54092':\n\n"+ + "For example, this entity filter selects all devices and assets which are related to the asset with id 'e51de0c0-2a7a-11ec-94eb-213c95f54092':\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"relationsQuery\",\n" + @@ -185,7 +185,7 @@ public class EntityQueryController extends BaseController { " ]\n" + " }\n" + " ]\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; @@ -197,7 +197,7 @@ public class EntityQueryController extends BaseController { "The 'relationType' defines the type of the relation to search for. " + "The 'assetTypes' defines the type of the asset to search for. " + "The relation query calculates all related entities, even if they are filtered using different relation types, and then extracts only assets that match 'relationType' and 'assetTypes' conditions.\n\n" + - "For example, this entity filter selects 'charging station' assets which are related to the asset with id 'e51de0c0-2a7a-11ec-94eb-213c95f54092' using 'Contains' relation:\n\n"+ + "For example, this entity filter selects 'charging station' assets which are related to the asset with id 'e51de0c0-2a7a-11ec-94eb-213c95f54092' using 'Contains' relation:\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"assetSearchQuery\",\n" + @@ -212,7 +212,7 @@ public class EntityQueryController extends BaseController { " \"assetTypes\": [\n" + " \"charging station\"\n" + " ]\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; @@ -223,7 +223,7 @@ public class EntityQueryController extends BaseController { "The 'relationType' defines the type of the relation to search for. " + "The 'deviceTypes' defines the type of the device to search for. " + "The relation query calculates all related entities, even if they are filtered using different relation types, and then extracts only devices that match 'relationType' and 'deviceTypes' conditions.\n\n" + - "For example, this entity filter selects 'Charging port' and 'Air Quality Sensor' devices which are related to the asset with id 'e52b0020-2a7a-11ec-94eb-213c95f54092' using 'Contains' relation:\n\n"+ + "For example, this entity filter selects 'Charging port' and 'Air Quality Sensor' devices which are related to the asset with id 'e52b0020-2a7a-11ec-94eb-213c95f54092' using 'Contains' relation:\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"deviceSearchQuery\",\n" + @@ -239,7 +239,7 @@ public class EntityQueryController extends BaseController { " \"Air Quality Sensor\",\n" + " \"Charging port\"\n" + " ]\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; @@ -250,7 +250,7 @@ public class EntityQueryController extends BaseController { "The 'relationType' defines the type of the relation to search for. " + "The 'entityViewTypes' defines the type of the entity view to search for. " + "The relation query calculates all related entities, even if they are filtered using different relation types, and then extracts only devices that match 'relationType' and 'deviceTypes' conditions.\n\n" + - "For example, this entity filter selects 'Concrete mixer' entity views which are related to the asset with id 'e52b0020-2a7a-11ec-94eb-213c95f54092' using 'Contains' relation:\n\n"+ + "For example, this entity filter selects 'Concrete mixer' entity views which are related to the asset with id 'e52b0020-2a7a-11ec-94eb-213c95f54092' using 'Contains' relation:\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"entityViewSearchQuery\",\n" + @@ -265,7 +265,7 @@ public class EntityQueryController extends BaseController { " \"entityViewTypes\": [\n" + " \"Concrete mixer\"\n" + " ]\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; @@ -276,7 +276,7 @@ public class EntityQueryController extends BaseController { "The 'relationType' defines the type of the relation to search for. " + "The 'deviceTypes' defines the type of the device to search for. " + "The relation query calculates all related entities, even if they are filtered using different relation types, and then extracts only devices that match 'relationType' and 'deviceTypes' conditions.\n\n" + - "For example, this entity filter selects 'Factory' edge instances which are related to the asset with id 'e52b0020-2a7a-11ec-94eb-213c95f54092' using 'Contains' relation:\n\n"+ + "For example, this entity filter selects 'Factory' edge instances which are related to the asset with id 'e52b0020-2a7a-11ec-94eb-213c95f54092' using 'Contains' relation:\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"deviceSearchQuery\",\n" + @@ -291,27 +291,27 @@ public class EntityQueryController extends BaseController { " \"edgeTypes\": [\n" + " \"Factory\"\n" + " ]\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; private static final String EMPTY = "\n\n## Entity Type Filter\n\n" + "Allows to filter multiple entities of the same type using the **'starts with'** expression over entity name. " + - "For example, this entity filter selects all devices which name starts with 'Air Quality':\n\n"+ + "For example, this entity filter selects all devices which name starts with 'Air Quality':\n\n" + MARKDOWN_CODE_BLOCK_START + - ""+ + "" + MARKDOWN_CODE_BLOCK_END + ""; private static final String ENTITY_FILTERS = "\n\n # Entity Filters" + - "\nEntity Filter body depends on the 'type' parameter. Let's review available entity filter types. In fact, they do correspond to available dashboard aliases." + - SINGLE_ENTITY + ENTITY_LIST + ENTITY_NAME + ENTITY_TYPE + ASSET_TYPE + DEVICE_TYPE + EDGE_TYPE + ENTITY_VIEW_TYPE + API_USAGE + RELATIONS_QUERY_FILTER - + ASSET_QUERY_FILTER + DEVICE_QUERY_FILTER + EV_QUERY_FILTER + EDGE_QUERY_FILTER; + "\nEntity Filter body depends on the 'type' parameter. Let's review available entity filter types. In fact, they do correspond to available dashboard aliases." + + SINGLE_ENTITY + ENTITY_LIST + ENTITY_NAME + ENTITY_TYPE + ASSET_TYPE + DEVICE_TYPE + EDGE_TYPE + ENTITY_VIEW_TYPE + API_USAGE + RELATIONS_QUERY_FILTER + + ASSET_QUERY_FILTER + DEVICE_QUERY_FILTER + EV_QUERY_FILTER + EDGE_QUERY_FILTER; private static final String FILTER_KEY = "\n\n## Filter Key\n\n" + "Filter Key defines either entity field, attribute or telemetry. It is a JSON object that consists the key name and type. " + - "The following filter key types are supported: \n\n"+ + "The following filter key types are supported: \n\n" + " * 'CLIENT_ATTRIBUTE' - used for client attributes; \n" + " * 'SHARED_ATTRIBUTE' - used for shared attributes; \n" + " * 'SERVER_ATTRIBUTE' - used for server attributes; \n" + @@ -328,19 +328,10 @@ public class EntityQueryController extends BaseController { MARKDOWN_CODE_BLOCK_END + ""; - private static final String FILTER_VALUE_TYPE = "\n\n## Value Type and Operations\n\n" + - "Provides a hint about the data type of the entity field that is defined in the filter key. " + - "The value type impacts the list of possible operations that you may use in the corresponding predicate. For example, you may use 'STARTS_WITH' or 'END_WITH', but you can't use 'GREATER_OR_EQUAL' for string values." + - "The following filter value types and corresponding predicate operations are supported: \n\n"+ - " * 'STRING' - used to filter any 'String' or 'JSON' values. Operations: EQUAL, NOT_EQUAL, STARTS_WITH, ENDS_WITH, CONTAINS, NOT_CONTAINS; \n" + - " * 'NUMERIC' - used for 'Long' and 'Double' values. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n" + - " * 'BOOLEAN' - used for boolean values; Operations: EQUAL, NOT_EQUAL \n" + - " * 'DATE_TIME' - similar to numeric, transforms value to milliseconds since epoch. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n"; - private static final String FILTER_PREDICATE = "\n\n## Filter Predicate\n\n" + "Filter Predicate defines the logical expression to evaluate. The list of available operations depends on the filter value type, see above. " + "Platform supports 4 predicate types: 'STRING', 'NUMERIC', 'BOOLEAN' and 'COMPLEX'. The last one allows to combine multiple operations over one filter key." + - "\n\nSimple predicate example to check 'value < 100': \n\n"+ + "\n\nSimple predicate example to check 'value < 100': \n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"operation\": \"LESS\",\n" + @@ -351,7 +342,7 @@ public class EntityQueryController extends BaseController { " \"type\": \"NUMERIC\"\n" + "}" + MARKDOWN_CODE_BLOCK_END + - "\n\nComplex predicate example, to check 'value < 10 or value > 20': \n\n"+ + "\n\nComplex predicate example, to check 'value < 10 or value > 20': \n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"COMPLEX\",\n" + @@ -376,7 +367,7 @@ public class EntityQueryController extends BaseController { " ]\n" + "}" + MARKDOWN_CODE_BLOCK_END + - "\n\nMore complex predicate example, to check 'value < 10 or (value > 50 && value < 60)': \n\n"+ + "\n\nMore complex predicate example, to check 'value < 10 or (value > 50 && value < 60)': \n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"type\": \"COMPLEX\",\n" + @@ -461,16 +452,16 @@ public class EntityQueryController extends BaseController { private static final String ENTITY_COUNT_QUERY_DESCRIPTION = "Allows to run complex queries to search the count of platform entities (devices, assets, customers, etc) " + - "based on the combination of main entity filter and multiple key filters. Returns the number of entities that match the query definition.\n\n" + - "# Query Definition\n\n" + - "\n\nMain **entity filter** is mandatory and defines generic search criteria. " + + "based on the combination of main entity filter and multiple key filters. Returns the number of entities that match the query definition.\n\n" + + "# Query Definition\n\n" + + "\n\nMain **entity filter** is mandatory and defines generic search criteria. " + "For example, \"find all devices with profile 'Moisture Sensor'\" or \"Find all devices related to asset 'Building A'\"" + - "\n\nOptional **key filters** allow to filter results of the entity filter by complex criteria against " + - "main entity fields (name, label, type, etc), attributes and telemetry. " + - "For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"."+ - "\n\nLet's review the example:" + - "\n\n" + MARKDOWN_CODE_BLOCK_START + - "{\n" + + "\n\nOptional **key filters** allow to filter results of the entity filter by complex criteria against " + + "main entity fields (name, label, type, etc), attributes and telemetry. " + + "For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"." + + "\n\nLet's review the example:" + + "\n\n" + MARKDOWN_CODE_BLOCK_START + + "{\n" + " \"entityFilter\": {\n" + " \"type\": \"entityType\",\n" + " \"entityType\": \"DEVICE\"\n" + @@ -492,12 +483,12 @@ public class EntityQueryController extends BaseController { " }\n" + " }\n" + " ]\n" + - "}"+ - MARKDOWN_CODE_BLOCK_END + - "\n\n Example mentioned above search all devices which have attribute 'active' set to 'true'. Now let's review available entity filters and key filters syntax:" + - ENTITY_FILTERS + - KEY_FILTERS + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;; + "}" + + MARKDOWN_CODE_BLOCK_END + + "\n\n Example mentioned above search all devices which have attribute 'active' set to 'true'. Now let's review available entity filters and key filters syntax:" + + ENTITY_FILTERS + + KEY_FILTERS + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; private static final String ENTITY_DATA_QUERY_DESCRIPTION = "Allows to run complex queries over platform entities (devices, assets, customers, etc) " + @@ -508,7 +499,7 @@ public class EntityQueryController extends BaseController { "For example, \"find all devices with profile 'Moisture Sensor'\" or \"Find all devices related to asset 'Building A'\"" + "\n\nOptional **key filters** allow to filter results of the **entity filter** by complex criteria against " + "main entity fields (name, label, type, etc), attributes and telemetry. " + - "For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"."+ + "For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"." + "\n\nThe **entity fields** and **latest values** contains list of entity fields and latest attribute/telemetry fields to fetch for each entity." + "\n\nThe **page link** contains information about the page to fetch and the sort ordering." + "\n\nLet's review the example:" + @@ -575,7 +566,7 @@ public class EntityQueryController extends BaseController { " \"direction\": \"ASC\"\n" + " }\n" + " }\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + "\n\n Example mentioned above search all devices which have attribute 'active' set to 'true'. Now let's review available entity filters and key filters syntax:" + ENTITY_FILTERS + @@ -672,7 +663,7 @@ public class EntityQueryController extends BaseController { " \"key\": \"temperature\"\n" + " }\n" + " ]\n" + - "}"+ + "}" + MARKDOWN_CODE_BLOCK_END + ""; diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index 32911a6d14..68969add27 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -45,8 +45,6 @@ import org.thingsboard.server.service.security.permission.Operation; @RequestMapping("/api") public class EventController extends BaseController { - private static final String NEW_LINE = "\n\n"; - @Autowired private EventService eventService; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index cb73aeb5e0..b59d62bda6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -69,8 +69,8 @@ public class Alarm extends BaseData implements HasName, HasTenantId, Ha @ApiModelProperty(position = 15, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true") private boolean propagate; @ApiModelProperty(position = 16, value = "JSON array of relation types that should be used for propagation. " + - "By default, 'propagateRelationTypes' array is empty which means that the alarm will propagate based on any relation type to parent entities. " + - "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will ignoned.") + "By default, 'propagateRelationTypes' array is empty which means that the alarm will be propagated based on any relation type to parent entities. " + + "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.") private List propagateRelationTypes; public Alarm() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceData.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceData.java index 06e52d47a0..26266b2b18 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceData.java @@ -16,13 +16,16 @@ package org.thingsboard.server.common.data.device.data; import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; @ApiModel @Data public class DeviceData { + @ApiModelProperty(position = 1, value = "Device configuration for device profile type. DEFAULT is only supported value for now") private DeviceConfiguration configuration; + @ApiModelProperty(position = 2, value = "Device transport configuration used to connect the device") private DeviceTransportConfiguration transportConfiguration; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java index 90de622fb0..1898d201ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java @@ -16,18 +16,23 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import javax.validation.Valid; import java.util.List; +@ApiModel @Data @JsonIgnoreProperties(ignoreUnknown = true) public class AlarmCondition implements Serializable { @Valid + @ApiModelProperty(position = 1, value = "JSON array of alarm condition filters") private List condition; + @ApiModelProperty(position = 2, value = "JSON object representing alarm condition type") private AlarmConditionSpec spec; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java index 96fbd3465f..5a45fa6d68 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.thingsboard.server.common.data.query.EntityKeyValueType; import org.thingsboard.server.common.data.query.KeyFilterPredicate; @@ -24,15 +26,20 @@ import javax.validation.Valid; import java.io.Serializable; +@ApiModel @Data public class AlarmConditionFilter implements Serializable { @Valid + @ApiModelProperty(position = 1, value = "JSON object for specifying alarm condition by specific key") private AlarmConditionFilterKey key; + @ApiModelProperty(position = 2, value = "String representation of the type of the value", example = "NUMERIC") private EntityKeyValueType valueType; @NoXss + @ApiModelProperty(position = 3, value = "Value used in Constant comparison. For other types, such as TIME_SERIES or ATTRIBUTE, the predicate condition is used") private Object value; @Valid + @ApiModelProperty(position = 4, value = "JSON object representing filter condition") private KeyFilterPredicate predicate; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java index 258e50a6a0..737db08c0b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilterKey.java @@ -15,16 +15,21 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.thingsboard.server.common.data.validation.NoXss; import java.io.Serializable; +@ApiModel @Data public class AlarmConditionFilterKey implements Serializable { + @ApiModelProperty(position = 1, value = "The key type", example = "TIME_SERIES") private final AlarmConditionKeyType type; @NoXss + @ApiModelProperty(position = 2, value = "String value representing the key", example = "temp") private final String key; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java index f578cd15c3..8304a1fc6a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.validation.NoXss; @@ -23,15 +25,20 @@ import javax.validation.Valid; import java.io.Serializable; +@ApiModel @Data public class AlarmRule implements Serializable { @Valid + @ApiModelProperty(position = 1, value = "JSON object representing the alarm rule condition") private AlarmCondition condition; + @ApiModelProperty(position = 2, value = "JSON object representing time interval during which the rule is active") private AlarmSchedule schedule; // Advanced @NoXss + @ApiModelProperty(position = 3, value = "String value representing the additional details for an alarm rule") private String alarmDetails; + @ApiModelProperty(position = 4, value = "JSON object with the dashboard Id representing the reference to alarm details dashboard used by mobile application") private DashboardId dashboardId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java index 7a99ccb41e..806925f244 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java @@ -15,28 +15,40 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.validation.NoXss; -import java.io.Serializable; import javax.validation.Valid; +import java.io.Serializable; import java.util.List; import java.util.TreeMap; +@ApiModel @Data public class DeviceProfileAlarm implements Serializable { + @ApiModelProperty(position = 1, value = "String value representing the alarm rule id", example = "highTemperatureAlarmID") private String id; @NoXss + @ApiModelProperty(position = 2, value = "String value representing type of the alarm", example = "High Temperature Alarm") private String alarmType; @Valid + @ApiModelProperty(position = 3, value = "Complex JSON object representing create alarm rules. The unique create alarm rule can be created for each alarm severity type. " + + "There can be 5 create alarm rules configured per a single alarm type. See method implementation notes and AlarmRule model for more details") private TreeMap createRules; @Valid + @ApiModelProperty(position = 4, value = "JSON object representing clear alarm rule") private AlarmRule clearRule; // Hidden in advanced settings + @ApiModelProperty(position = 5, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true") private boolean propagate; + @ApiModelProperty(position = 6, value = "JSON array of relation types that should be used for propagation. " + + "By default, 'propagateRelationTypes' array is empty which means that the alarm will be propagated based on any relation type to parent entities. " + + "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.") private List propagateRelationTypes; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java index ce019212eb..2d0a5fcf10 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileConfiguration.java @@ -29,7 +29,7 @@ import java.io.Serializable; include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ - @JsonSubTypes.Type(value = DefaultDeviceProfileConfiguration.class, name = "DEFAULT")}) + @JsonSubTypes.Type(value = DefaultDeviceProfileConfiguration.class, name = "DEFAULT")}) public interface DeviceProfileConfiguration extends Serializable { @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java index f77e6388a5..d56edcd323 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java @@ -15,20 +15,27 @@ */ package org.thingsboard.server.common.data.device.profile; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import java.io.Serializable; import javax.validation.Valid; +import java.io.Serializable; import java.util.List; +@ApiModel @Data public class DeviceProfileData implements Serializable { + @ApiModelProperty(position = 1, value = "JSON object of device profile configuration") private DeviceProfileConfiguration configuration; @Valid + @ApiModelProperty(position = 2, value = "JSON object of device profile transport configuration") private DeviceProfileTransportConfiguration transportConfiguration; + @ApiModelProperty(position = 3, value = "JSON object of provisioning strategy type per device profile") private DeviceProfileProvisionConfiguration provisionConfiguration; @Valid + @ApiModelProperty(position = 4, value = "JSON array of alarm rules configuration per device profile") private List alarms; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileProvisionConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileProvisionConfiguration.java index 2b0f1b8da1..f4582786d1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileProvisionConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileProvisionConfiguration.java @@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.DeviceProfileProvisionType; import java.io.Serializable; - @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME,