Commit 3bc534ecb5791b9cb0c81156f69c4b1b70222920

Authored by Igor Kulikov
2 parents 106ba882 8de34703

Merge branch 'master' of github.com:thingsboard/thingsboard

Showing 82 changed files with 2817 additions and 334 deletions
@@ -15,8 +15,11 @@ @@ -15,8 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import org.apache.commons.lang3.StringUtils; 20 import org.apache.commons.lang3.StringUtils;
19 import org.springframework.http.HttpStatus; 21 import org.springframework.http.HttpStatus;
  22 +import org.springframework.http.MediaType;
20 import org.springframework.security.access.prepost.PreAuthorize; 23 import org.springframework.security.access.prepost.PreAuthorize;
21 import org.springframework.web.bind.annotation.PathVariable; 24 import org.springframework.web.bind.annotation.PathVariable;
22 import org.springframework.web.bind.annotation.RequestBody; 25 import org.springframework.web.bind.annotation.RequestBody;
@@ -55,11 +58,25 @@ import java.util.List; @@ -55,11 +58,25 @@ import java.util.List;
55 public class AlarmController extends BaseController { 58 public class AlarmController extends BaseController {
56 59
57 public static final String ALARM_ID = "alarmId"; 60 public static final String ALARM_ID = "alarmId";
  61 + private static final String ALARM_SECURITY_CHECK = "If the user has the authority of 'Tenant Administrator', the server checks that the originator of alarm is owned by the same tenant. " +
  62 + "If the user has the authority of 'Customer User', the server checks that the originator of alarm belongs to the customer. ";
  63 + private static final String ALARM_QUERY_SEARCH_STATUS_DESCRIPTION = "A string value representing one of the AlarmSearchStatus enumeration value";
  64 + private static final String ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES = "ANY, ACTIVE, CLEARED, ACK, UNACK";
  65 + private static final String ALARM_QUERY_STATUS_DESCRIPTION = "A string value representing one of the AlarmStatus enumeration value";
  66 + private static final String ALARM_QUERY_STATUS_ALLOWABLE_VALUES = "ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK";
  67 + private static final String ALARM_QUERY_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on of next alarm fields: type, severity or status";
  68 + private static final String ALARM_QUERY_START_TIME_DESCRIPTION = "The start timestamp in milliseconds of the search time range over the Alarm class field: 'createdTime'.";
  69 + private static final String ALARM_QUERY_END_TIME_DESCRIPTION = "The end timestamp in milliseconds of the search time range over the Alarm class field: 'createdTime'.";
  70 + private static final String ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION = "A boolean value to specify if the alarm originator name will be " +
  71 + "filled in the AlarmInfo object field: 'originatorName' or will returns as null.";
58 72
59 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 73 + @ApiOperation(value = "Get Alarm (getAlarmById)",
  74 + notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK, produces = MediaType.APPLICATION_JSON_VALUE)
  75 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
60 @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET) 76 @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET)
61 @ResponseBody 77 @ResponseBody
62 - public Alarm getAlarmById(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { 78 + public Alarm getAlarmById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
  79 + @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
63 checkParameter(ALARM_ID, strAlarmId); 80 checkParameter(ALARM_ID, strAlarmId);
64 try { 81 try {
65 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 82 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
@@ -69,10 +86,14 @@ public class AlarmController extends BaseController { @@ -69,10 +86,14 @@ public class AlarmController extends BaseController {
69 } 86 }
70 } 87 }
71 88
72 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 89 + @ApiOperation(value = "Get Alarm Info (getAlarmInfoById)",
  90 + notes = "Fetch the Alarm Info object based on the provided Alarm Id. " +
  91 + ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION, produces = MediaType.APPLICATION_JSON_VALUE)
  92 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
73 @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET) 93 @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET)
74 @ResponseBody 94 @ResponseBody
75 - public AlarmInfo getAlarmInfoById(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { 95 + public AlarmInfo getAlarmInfoById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
  96 + @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
76 checkParameter(ALARM_ID, strAlarmId); 97 checkParameter(ALARM_ID, strAlarmId);
77 try { 98 try {
78 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 99 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
@@ -82,10 +103,20 @@ public class AlarmController extends BaseController { @@ -82,10 +103,20 @@ public class AlarmController extends BaseController {
82 } 103 }
83 } 104 }
84 105
85 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 106 + @ApiOperation(value = "Create or update Alarm (saveAlarm)",
  107 + notes = "Creates or Updates the Alarm. " +
  108 + "When creating alarm, platform generates Alarm Id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address). " +
  109 + "The newly created Alarm id will be present in the response. Specify existing Alarm id to update the alarm. " +
  110 + "Referencing non-existing Alarm Id will cause 'Not Found' error. " +
  111 + "\n\nPlatform also deduplicate the alarms based on the entity id of originator and alarm 'type'. " +
  112 + "For example, if the user or system component create the alarm with the type 'HighTemperature' for device 'Device A' the new active alarm is created. " +
  113 + "If the user tries to create 'HighTemperature' alarm for the same device again, the previous alarm will be updated (the 'end_ts' will be set to current timestamp). " +
  114 + "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. "
  115 + , produces = MediaType.APPLICATION_JSON_VALUE)
  116 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
86 @RequestMapping(value = "/alarm", method = RequestMethod.POST) 117 @RequestMapping(value = "/alarm", method = RequestMethod.POST)
87 @ResponseBody 118 @ResponseBody
88 - public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException { 119 + public Alarm saveAlarm(@ApiParam(value = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException {
89 try { 120 try {
90 alarm.setTenantId(getCurrentUser().getTenantId()); 121 alarm.setTenantId(getCurrentUser().getTenantId());
91 122
@@ -106,10 +137,12 @@ public class AlarmController extends BaseController { @@ -106,10 +137,12 @@ public class AlarmController extends BaseController {
106 } 137 }
107 } 138 }
108 139
109 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 140 + @ApiOperation(value = "Delete Alarm (deleteAlarm)",
  141 + notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error.", produces = MediaType.APPLICATION_JSON_VALUE)
  142 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
110 @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE) 143 @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE)
111 @ResponseBody 144 @ResponseBody
112 - public Boolean deleteAlarm(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { 145 + public Boolean deleteAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
113 checkParameter(ALARM_ID, strAlarmId); 146 checkParameter(ALARM_ID, strAlarmId);
114 try { 147 try {
115 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 148 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
@@ -124,15 +157,19 @@ public class AlarmController extends BaseController { @@ -124,15 +157,19 @@ public class AlarmController extends BaseController {
124 sendAlarmDeleteNotificationMsg(getTenantId(), alarmId, relatedEdgeIds, alarm); 157 sendAlarmDeleteNotificationMsg(getTenantId(), alarmId, relatedEdgeIds, alarm);
125 158
126 return alarmService.deleteAlarm(getTenantId(), alarmId); 159 return alarmService.deleteAlarm(getTenantId(), alarmId);
127 - } catch (Exception e) { 160 + } catch (Exception e) {
128 throw handleException(e); 161 throw handleException(e);
129 } 162 }
130 } 163 }
131 164
132 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 165 + @ApiOperation(value = "Acknowledge Alarm (ackAlarm)",
  166 + notes = "Acknowledge the Alarm. " +
  167 + "Once acknowledged, the 'ack_ts' field will be set to current timestamp and special rule chain event 'ALARM_ACK' will be generated. " +
  168 + "Referencing non-existing Alarm Id will cause an error.", produces = MediaType.APPLICATION_JSON_VALUE)
  169 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
133 @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST) 170 @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST)
134 @ResponseStatus(value = HttpStatus.OK) 171 @ResponseStatus(value = HttpStatus.OK)
135 - public void ackAlarm(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { 172 + public void ackAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
136 checkParameter(ALARM_ID, strAlarmId); 173 checkParameter(ALARM_ID, strAlarmId);
137 try { 174 try {
138 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 175 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
@@ -149,10 +186,14 @@ public class AlarmController extends BaseController { @@ -149,10 +186,14 @@ public class AlarmController extends BaseController {
149 } 186 }
150 } 187 }
151 188
152 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 189 + @ApiOperation(value = "Clear Alarm (clearAlarm)",
  190 + notes = "Clear the Alarm. " +
  191 + "Once cleared, the 'clear_ts' field will be set to current timestamp and special rule chain event 'ALARM_CLEAR' will be generated. " +
  192 + "Referencing non-existing Alarm Id will cause an error.", produces = MediaType.APPLICATION_JSON_VALUE)
  193 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
153 @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST) 194 @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST)
154 @ResponseStatus(value = HttpStatus.OK) 195 @ResponseStatus(value = HttpStatus.OK)
155 - public void clearAlarm(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { 196 + public void clearAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
156 checkParameter(ALARM_ID, strAlarmId); 197 checkParameter(ALARM_ID, strAlarmId);
157 try { 198 try {
158 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 199 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
@@ -169,21 +210,36 @@ public class AlarmController extends BaseController { @@ -169,21 +210,36 @@ public class AlarmController extends BaseController {
169 } 210 }
170 } 211 }
171 212
  213 + @ApiOperation(value = "Get Alarms (getAlarms)",
  214 + notes = "Returns a page of alarms for the selected entity. Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " +
  215 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
172 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 216 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
173 @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET) 217 @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
174 @ResponseBody 218 @ResponseBody
175 public PageData<AlarmInfo> getAlarms( 219 public PageData<AlarmInfo> getAlarms(
176 - @PathVariable("entityType") String strEntityType,  
177 - @PathVariable("entityId") String strEntityId, 220 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION)
  221 + @PathVariable(ENTITY_TYPE) String strEntityType,
  222 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION)
  223 + @PathVariable(ENTITY_ID) String strEntityId,
  224 + @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
178 @RequestParam(required = false) String searchStatus, 225 @RequestParam(required = false) String searchStatus,
  226 + @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
179 @RequestParam(required = false) String status, 227 @RequestParam(required = false) String status,
  228 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
180 @RequestParam int pageSize, 229 @RequestParam int pageSize,
  230 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
181 @RequestParam int page, 231 @RequestParam int page,
  232 + @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
182 @RequestParam(required = false) String textSearch, 233 @RequestParam(required = false) String textSearch,
  234 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ALARM_SORT_PROPERTY_ALLOWABLE_VALUES)
183 @RequestParam(required = false) String sortProperty, 235 @RequestParam(required = false) String sortProperty,
  236 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
184 @RequestParam(required = false) String sortOrder, 237 @RequestParam(required = false) String sortOrder,
  238 + @ApiParam(value = ALARM_QUERY_START_TIME_DESCRIPTION)
185 @RequestParam(required = false) Long startTime, 239 @RequestParam(required = false) Long startTime,
  240 + @ApiParam(value = ALARM_QUERY_END_TIME_DESCRIPTION)
186 @RequestParam(required = false) Long endTime, 241 @RequestParam(required = false) Long endTime,
  242 + @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION)
187 @RequestParam(required = false) Boolean fetchOriginator 243 @RequestParam(required = false) Boolean fetchOriginator
188 ) throws ThingsboardException { 244 ) throws ThingsboardException {
189 checkParameter("EntityId", strEntityId); 245 checkParameter("EntityId", strEntityId);
@@ -204,20 +260,35 @@ public class AlarmController extends BaseController { @@ -204,20 +260,35 @@ public class AlarmController extends BaseController {
204 throw handleException(e); 260 throw handleException(e);
205 } 261 }
206 } 262 }
207 -  
208 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 263 + @ApiOperation(value = "Get All Alarms (getAllAlarms)",
  264 + notes = "Returns a page of alarms that belongs to the current user owner. " +
  265 + "If the user has the authority of 'Tenant Administrator', the server returns alarms that belongs to the tenant of current user. " +
  266 + "If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " +
  267 + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " +
  268 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
  269 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
209 @RequestMapping(value = "/alarms", method = RequestMethod.GET) 270 @RequestMapping(value = "/alarms", method = RequestMethod.GET)
210 @ResponseBody 271 @ResponseBody
211 public PageData<AlarmInfo> getAllAlarms( 272 public PageData<AlarmInfo> getAllAlarms(
  273 + @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
212 @RequestParam(required = false) String searchStatus, 274 @RequestParam(required = false) String searchStatus,
  275 + @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
213 @RequestParam(required = false) String status, 276 @RequestParam(required = false) String status,
  277 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
214 @RequestParam int pageSize, 278 @RequestParam int pageSize,
  279 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
215 @RequestParam int page, 280 @RequestParam int page,
  281 + @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
216 @RequestParam(required = false) String textSearch, 282 @RequestParam(required = false) String textSearch,
  283 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ALARM_SORT_PROPERTY_ALLOWABLE_VALUES)
217 @RequestParam(required = false) String sortProperty, 284 @RequestParam(required = false) String sortProperty,
  285 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
218 @RequestParam(required = false) String sortOrder, 286 @RequestParam(required = false) String sortOrder,
  287 + @ApiParam(value = ALARM_QUERY_START_TIME_DESCRIPTION)
219 @RequestParam(required = false) Long startTime, 288 @RequestParam(required = false) Long startTime,
  289 + @ApiParam(value = ALARM_QUERY_END_TIME_DESCRIPTION)
220 @RequestParam(required = false) Long endTime, 290 @RequestParam(required = false) Long endTime,
  291 + @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION)
221 @RequestParam(required = false) Boolean fetchOriginator 292 @RequestParam(required = false) Boolean fetchOriginator
222 ) throws ThingsboardException { 293 ) throws ThingsboardException {
223 AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus); 294 AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
@@ -239,13 +310,21 @@ public class AlarmController extends BaseController { @@ -239,13 +310,21 @@ public class AlarmController extends BaseController {
239 } 310 }
240 } 311 }
241 312
242 - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 313 + @ApiOperation(value = "Get Highest Alarm Severity (getHighestAlarmSeverity)",
  314 + notes = "Search the alarms by originator ('entityType' and entityId') and optional 'status' or 'searchStatus' filters and returns the highest AlarmSeverity(CRITICAL, MAJOR, MINOR, WARNING or INDETERMINATE). " +
  315 + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error."
  316 + , produces = MediaType.APPLICATION_JSON_VALUE)
  317 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
243 @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET) 318 @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET)
244 @ResponseBody 319 @ResponseBody
245 public AlarmSeverity getHighestAlarmSeverity( 320 public AlarmSeverity getHighestAlarmSeverity(
246 - @PathVariable("entityType") String strEntityType,  
247 - @PathVariable("entityId") String strEntityId, 321 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
  322 + @PathVariable(ENTITY_TYPE) String strEntityType,
  323 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
  324 + @PathVariable(ENTITY_ID) String strEntityId,
  325 + @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
248 @RequestParam(required = false) String searchStatus, 326 @RequestParam(required = false) String searchStatus,
  327 + @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
249 @RequestParam(required = false) String status 328 @RequestParam(required = false) String status
250 ) throws ThingsboardException { 329 ) throws ThingsboardException {
251 checkParameter("EntityId", strEntityId); 330 checkParameter("EntityId", strEntityId);
@@ -518,8 +518,11 @@ public class AssetController extends BaseController { @@ -518,8 +518,11 @@ public class AssetController extends BaseController {
518 518
519 @ApiOperation(value = "Assign asset to edge (assignAssetToEdge)", 519 @ApiOperation(value = "Assign asset to edge (assignAssetToEdge)",
520 notes = "Creates assignment of an existing asset to an instance of The Edge. " + 520 notes = "Creates assignment of an existing asset to an instance of The Edge. " +
521 - "The Edge is a software product for edge computing. " +  
522 - "It allows bringing data analysis and management to the edge, while seamlessly synchronizing with the platform server (cloud). ", produces = MediaType.APPLICATION_JSON_VALUE) 521 + EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  522 + "Second, remote edge service will receive a copy of assignment asset " +
  523 + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  524 + "Third, once asset will be delivered to edge service, it's going to be available for usage on remote edge instance.",
  525 + produces = MediaType.APPLICATION_JSON_VALUE)
523 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 526 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
524 @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST) 527 @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST)
525 @ResponseBody 528 @ResponseBody
@@ -554,7 +557,12 @@ public class AssetController extends BaseController { @@ -554,7 +557,12 @@ public class AssetController extends BaseController {
554 } 557 }
555 558
556 @ApiOperation(value = "Unassign asset from edge (unassignAssetFromEdge)", 559 @ApiOperation(value = "Unassign asset from edge (unassignAssetFromEdge)",
557 - notes = "Clears assignment of the asset to the edge", produces = MediaType.APPLICATION_JSON_VALUE) 560 + notes = "Clears assignment of the asset to the edge. " +
  561 + EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  562 + "Second, remote edge service will receive an 'unassign' command to remove asset " +
  563 + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  564 + "Third, once 'unassign' command will be delivered to edge service, it's going to remove asset locally.",
  565 + produces = MediaType.APPLICATION_JSON_VALUE)
558 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 566 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
559 @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE) 567 @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE)
560 @ResponseBody 568 @ResponseBody
@@ -15,7 +15,10 @@ @@ -15,7 +15,10 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import org.apache.commons.lang3.StringUtils; 20 import org.apache.commons.lang3.StringUtils;
  21 +import org.springframework.http.MediaType;
19 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.web.bind.annotation.PathVariable; 23 import org.springframework.web.bind.annotation.PathVariable;
21 import org.springframework.web.bind.annotation.RequestMapping; 24 import org.springframework.web.bind.annotation.RequestMapping;
@@ -44,18 +47,42 @@ import java.util.stream.Collectors; @@ -44,18 +47,42 @@ import java.util.stream.Collectors;
44 @RequestMapping("/api") 47 @RequestMapping("/api")
45 public class AuditLogController extends BaseController { 48 public class AuditLogController extends BaseController {
46 49
  50 + private static final String AUDIT_LOG_QUERY_START_TIME_DESCRIPTION = "The start timestamp in milliseconds of the search time range over the AuditLog class field: 'createdTime'.";
  51 + private static final String AUDIT_LOG_QUERY_END_TIME_DESCRIPTION = "The end timestamp in milliseconds of the search time range over the AuditLog class field: 'createdTime'.";
  52 + private static final String AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION = "A String value representing comma-separated list of action types. " +
  53 + "This parameter is optional, but it can be used to filter results to fetch only audit logs of specific action types. " +
  54 + "For example, 'LOGIN', 'LOGOUT'. See the 'Model' tab of the Response Class for more details.";
  55 + private static final String AUDIT_LOG_SORT_PROPERTY_DESCRIPTION = "Property of audit log to sort by. " +
  56 + "See the 'Model' tab of the Response Class for more details. " +
  57 + "Note: entityType sort property is not defined in the AuditLog class, however, it can be used to sort audit logs by types of entities that were logged.";
  58 +
  59 +
  60 + @ApiOperation(value = "Get audit logs by customer id (getAuditLogsByCustomerId)",
  61 + notes = "Returns a page of audit logs related to the targeted customer entities (devices, assets, etc.), " +
  62 + "and users actions (login, logout, etc.) that belong to this customer. " +
  63 + PAGE_DATA_PARAMETERS + ADMINISTRATOR_AUTHORITY_ONLY,
  64 + produces = MediaType.APPLICATION_JSON_VALUE)
47 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 65 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
48 @RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"pageSize", "page"}, method = RequestMethod.GET) 66 @RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
49 @ResponseBody 67 @ResponseBody
50 public PageData<AuditLog> getAuditLogsByCustomerId( 68 public PageData<AuditLog> getAuditLogsByCustomerId(
  69 + @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
51 @PathVariable("customerId") String strCustomerId, 70 @PathVariable("customerId") String strCustomerId,
  71 + @ApiParam(value = PAGE_SIZE_DESCRIPTION)
52 @RequestParam int pageSize, 72 @RequestParam int pageSize,
  73 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
53 @RequestParam int page, 74 @RequestParam int page,
  75 + @ApiParam(value = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
54 @RequestParam(required = false) String textSearch, 76 @RequestParam(required = false) String textSearch,
  77 + @ApiParam(value = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, allowableValues = AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES)
55 @RequestParam(required = false) String sortProperty, 78 @RequestParam(required = false) String sortProperty,
  79 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
56 @RequestParam(required = false) String sortOrder, 80 @RequestParam(required = false) String sortOrder,
  81 + @ApiParam(value = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
57 @RequestParam(required = false) Long startTime, 82 @RequestParam(required = false) Long startTime,
  83 + @ApiParam(value = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
58 @RequestParam(required = false) Long endTime, 84 @RequestParam(required = false) Long endTime,
  85 + @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
59 @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { 86 @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
60 try { 87 try {
61 checkParameter("CustomerId", strCustomerId); 88 checkParameter("CustomerId", strCustomerId);
@@ -68,18 +95,32 @@ public class AuditLogController extends BaseController { @@ -68,18 +95,32 @@ public class AuditLogController extends BaseController {
68 } 95 }
69 } 96 }
70 97
  98 + @ApiOperation(value = "Get audit logs by user id (getAuditLogsByUserId)",
  99 + notes = "Returns a page of audit logs related to the actions of targeted user. " +
  100 + "For example, RPC call to a particular device, or alarm acknowledgment for a specific device, etc. " +
  101 + PAGE_DATA_PARAMETERS + ADMINISTRATOR_AUTHORITY_ONLY,
  102 + produces = MediaType.APPLICATION_JSON_VALUE)
71 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 103 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
72 @RequestMapping(value = "/audit/logs/user/{userId}", params = {"pageSize", "page"}, method = RequestMethod.GET) 104 @RequestMapping(value = "/audit/logs/user/{userId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
73 @ResponseBody 105 @ResponseBody
74 public PageData<AuditLog> getAuditLogsByUserId( 106 public PageData<AuditLog> getAuditLogsByUserId(
  107 + @ApiParam(value = USER_ID_PARAM_DESCRIPTION)
75 @PathVariable("userId") String strUserId, 108 @PathVariable("userId") String strUserId,
  109 + @ApiParam(value = PAGE_SIZE_DESCRIPTION)
76 @RequestParam int pageSize, 110 @RequestParam int pageSize,
  111 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
77 @RequestParam int page, 112 @RequestParam int page,
  113 + @ApiParam(value = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
78 @RequestParam(required = false) String textSearch, 114 @RequestParam(required = false) String textSearch,
  115 + @ApiParam(value = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, allowableValues = AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES)
79 @RequestParam(required = false) String sortProperty, 116 @RequestParam(required = false) String sortProperty,
  117 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
80 @RequestParam(required = false) String sortOrder, 118 @RequestParam(required = false) String sortOrder,
  119 + @ApiParam(value = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
81 @RequestParam(required = false) Long startTime, 120 @RequestParam(required = false) Long startTime,
  121 + @ApiParam(value = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
82 @RequestParam(required = false) Long endTime, 122 @RequestParam(required = false) Long endTime,
  123 + @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
83 @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { 124 @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
84 try { 125 try {
85 checkParameter("UserId", strUserId); 126 checkParameter("UserId", strUserId);
@@ -92,19 +133,35 @@ public class AuditLogController extends BaseController { @@ -92,19 +133,35 @@ public class AuditLogController extends BaseController {
92 } 133 }
93 } 134 }
94 135
  136 + @ApiOperation(value = "Get audit logs by entity id (getAuditLogsByEntityId)",
  137 + notes = "Returns a page of audit logs related to the actions on the targeted entity. " +
  138 + "Basically, this API call is used to get the full lifecycle of some specific entity. " +
  139 + "For example to see when a device was created, updated, assigned to some customer, or even deleted from the system. " +
  140 + PAGE_DATA_PARAMETERS + ADMINISTRATOR_AUTHORITY_ONLY,
  141 + produces = MediaType.APPLICATION_JSON_VALUE)
95 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 142 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
96 @RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET) 143 @RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
97 @ResponseBody 144 @ResponseBody
98 public PageData<AuditLog> getAuditLogsByEntityId( 145 public PageData<AuditLog> getAuditLogsByEntityId(
  146 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION)
99 @PathVariable("entityType") String strEntityType, 147 @PathVariable("entityType") String strEntityType,
  148 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION)
100 @PathVariable("entityId") String strEntityId, 149 @PathVariable("entityId") String strEntityId,
  150 + @ApiParam(value = PAGE_SIZE_DESCRIPTION)
101 @RequestParam int pageSize, 151 @RequestParam int pageSize,
  152 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
102 @RequestParam int page, 153 @RequestParam int page,
  154 + @ApiParam(value = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
103 @RequestParam(required = false) String textSearch, 155 @RequestParam(required = false) String textSearch,
  156 + @ApiParam(value = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, allowableValues = AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES)
104 @RequestParam(required = false) String sortProperty, 157 @RequestParam(required = false) String sortProperty,
  158 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
105 @RequestParam(required = false) String sortOrder, 159 @RequestParam(required = false) String sortOrder,
  160 + @ApiParam(value = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
106 @RequestParam(required = false) Long startTime, 161 @RequestParam(required = false) Long startTime,
  162 + @ApiParam(value = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
107 @RequestParam(required = false) Long endTime, 163 @RequestParam(required = false) Long endTime,
  164 + @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
108 @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { 165 @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
109 try { 166 try {
110 checkParameter("EntityId", strEntityId); 167 checkParameter("EntityId", strEntityId);
@@ -118,17 +175,29 @@ public class AuditLogController extends BaseController { @@ -118,17 +175,29 @@ public class AuditLogController extends BaseController {
118 } 175 }
119 } 176 }
120 177
  178 + @ApiOperation(value = "Get all audit logs (getAuditLogs)",
  179 + notes = "Returns a page of audit logs related to all entities in the scope of the current user's Tenant. " +
  180 + PAGE_DATA_PARAMETERS + ADMINISTRATOR_AUTHORITY_ONLY,
  181 + produces = MediaType.APPLICATION_JSON_VALUE)
121 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 182 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
122 @RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET) 183 @RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET)
123 @ResponseBody 184 @ResponseBody
124 public PageData<AuditLog> getAuditLogs( 185 public PageData<AuditLog> getAuditLogs(
  186 + @ApiParam(value = PAGE_SIZE_DESCRIPTION)
125 @RequestParam int pageSize, 187 @RequestParam int pageSize,
  188 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
126 @RequestParam int page, 189 @RequestParam int page,
  190 + @ApiParam(value = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
127 @RequestParam(required = false) String textSearch, 191 @RequestParam(required = false) String textSearch,
  192 + @ApiParam(value = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, allowableValues = AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES)
128 @RequestParam(required = false) String sortProperty, 193 @RequestParam(required = false) String sortProperty,
  194 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
129 @RequestParam(required = false) String sortOrder, 195 @RequestParam(required = false) String sortOrder,
  196 + @ApiParam(value = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
130 @RequestParam(required = false) Long startTime, 197 @RequestParam(required = false) Long startTime,
  198 + @ApiParam(value = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
131 @RequestParam(required = false) Long endTime, 199 @RequestParam(required = false) Long endTime,
  200 + @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
132 @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { 201 @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
133 try { 202 try {
134 TenantId tenantId = getCurrentUser().getTenantId(); 203 TenantId tenantId = getCurrentUser().getTenantId();
@@ -157,41 +157,112 @@ public abstract class BaseController { @@ -157,41 +157,112 @@ public abstract class BaseController {
157 157
158 public static final String CUSTOMER_ID = "customerId"; 158 public static final String CUSTOMER_ID = "customerId";
159 public static final String TENANT_ID = "tenantId"; 159 public static final String TENANT_ID = "tenantId";
  160 + public static final String DEVICE_ID = "deviceId";
  161 + public static final String RPC_ID = "rpcId";
  162 + public static final String ENTITY_ID = "entityId";
  163 + public static final String ENTITY_TYPE = "entityType";
160 164
161 public static final String PAGE_DATA_PARAMETERS = "You can specify parameters to filter the results. " + 165 public static final String PAGE_DATA_PARAMETERS = "You can specify parameters to filter the results. " +
162 "The result is wrapped with PageData object that allows you to iterate over result set using pagination. " + 166 "The result is wrapped with PageData object that allows you to iterate over result set using pagination. " +
163 "See the 'Model' tab of the Response Class for more details. "; 167 "See the 'Model' tab of the Response Class for more details. ";
164 public static final String DASHBOARD_ID_PARAM_DESCRIPTION = "A string value representing the device id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; 168 public static final String DASHBOARD_ID_PARAM_DESCRIPTION = "A string value representing the device id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
  169 + public static final String RPC_ID_PARAM_DESCRIPTION = "A string value representing the rpc id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
165 public static final String DEVICE_ID_PARAM_DESCRIPTION = "A string value representing the device id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; 170 public static final String DEVICE_ID_PARAM_DESCRIPTION = "A string value representing the device id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
166 - public static final String DEVICE_PROFILE_ID_DESCRIPTION = "A string value representing the device profile id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; 171 + public static final String DEVICE_PROFILE_ID_PARAM_DESCRIPTION = "A string value representing the device profile id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
  172 + public static final String TENANT_PROFILE_ID_PARAM_DESCRIPTION = "A string value representing the tenant profile id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
167 public static final String TENANT_ID_PARAM_DESCRIPTION = "A string value representing the tenant id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; 173 public static final String TENANT_ID_PARAM_DESCRIPTION = "A string value representing the tenant id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
168 public static final String EDGE_ID_PARAM_DESCRIPTION = "A string value representing the edge id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; 174 public static final String EDGE_ID_PARAM_DESCRIPTION = "A string value representing the edge id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
169 public static final String CUSTOMER_ID_PARAM_DESCRIPTION = "A string value representing the customer id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; 175 public static final String CUSTOMER_ID_PARAM_DESCRIPTION = "A string value representing the customer id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
  176 + public static final String USER_ID_PARAM_DESCRIPTION = "A string value representing the user id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
170 public static final String ASSET_ID_PARAM_DESCRIPTION = "A string value representing the asset id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; 177 public static final String ASSET_ID_PARAM_DESCRIPTION = "A string value representing the asset id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
171 -  
172 -  
173 - protected final String PAGE_SIZE_DESCRIPTION = "Maximum amount of entities in a one page";  
174 - protected final String PAGE_NUMBER_DESCRIPTION = "Sequence number of page starting from 0";  
175 - protected final String DEVICE_TYPE_DESCRIPTION = "Device type as the name of the device profile";  
176 - protected final String ASSET_TYPE_DESCRIPTION = "Asset type";  
177 -  
178 - protected final String ASSET_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the asset name.";  
179 - protected final String DASHBOARD_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the dashboard title.";  
180 - protected final String DEVICE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the device name.";  
181 - protected final String CUSTOMER_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the customer title.";  
182 - protected final String SORT_PROPERTY_DESCRIPTION = "Property of entity to sort by";  
183 - protected final String DASHBOARD_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title";  
184 - protected final String CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, email, country, city";  
185 - protected final String DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, deviceProfileName, label, customerTitle";  
186 - protected final String ASSET_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";  
187 - protected final String SORT_ORDER_DESCRIPTION = "Sort order. ASC (ASCENDING) or DESC (DESCENDING)";  
188 - protected final String SORT_ORDER_ALLOWABLE_VALUES = "ASC, DESC";  
189 - protected final String DEVICE_INFO_DESCRIPTION = "Device Info is an extension of the default Device object that contains information about the assigned customer name and device profile name. ";  
190 - protected final String ASSET_INFO_DESCRIPTION = "Asset Info is an extension of the default Asset object that contains information about the assigned customer name. ";  
191 -  
192 -  
193 - protected final String DEVICE_NAME_DESCRIPTION = "A string value representing the Device name.";  
194 - protected final String ASSET_NAME_DESCRIPTION = "A string value representing the Asset name."; 178 + public static final String ALARM_ID_PARAM_DESCRIPTION = "A string value representing the alarm id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
  179 + public static final String ENTITY_ID_PARAM_DESCRIPTION = "A string value representing the entity id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
  180 + public static final String ENTITY_TYPE_PARAM_DESCRIPTION = "A string value representing the entity type. For example, 'DEVICE'";
  181 + public static final String RULE_CHAIN_ID_PARAM_DESCRIPTION = "A string value representing the rule chain id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
  182 +
  183 + protected static final String SYSTEM_AUTHORITY_PARAGRAPH = "\n\nAvailable for users with 'SYS_ADMIN' authority.";
  184 + protected static final String SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH = "\n\nAvailable for users with 'SYS_ADMIN' or 'TENANT_ADMIN' authority.";
  185 + protected static final String TENANT_AUTHORITY_PARAGRAPH = "\n\nAvailable for users with 'TENANT_ADMIN' authority.";
  186 + protected static final String TENANT_OR_USER_AUTHORITY_PARAGRAPH = "\n\nAvailable for users with 'TENANT_ADMIN' or 'CUSTOMER_USER' authority.";
  187 +
  188 + protected static final String PAGE_SIZE_DESCRIPTION = "Maximum amount of entities in a one page";
  189 + protected static final String PAGE_NUMBER_DESCRIPTION = "Sequence number of page starting from 0";
  190 + protected static final String DEVICE_TYPE_DESCRIPTION = "Device type as the name of the device profile";
  191 + protected static final String ASSET_TYPE_DESCRIPTION = "Asset type";
  192 + protected static final String EDGE_TYPE_DESCRIPTION = "A string value representing the edge type. For example, 'default'";
  193 + protected static final String RULE_CHAIN_TYPE_DESCRIPTION = "Rule chain type (CORE or EDGE)";
  194 +
  195 + protected static final String ASSET_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the asset name.";
  196 + protected static final String DASHBOARD_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the dashboard title.";
  197 + protected static final String RPC_TEXT_SEARCH_DESCRIPTION = "Not implemented. Leave empty.";
  198 + protected static final String DEVICE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the device name.";
  199 + protected static final String USER_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the user email.";
  200 + protected static final String TENANT_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the tenant name.";
  201 + protected static final String TENANT_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the tenant profile name.";
  202 + protected static final String RULE_CHAIN_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the rule chain name.";
  203 + protected static final String DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the device profile name.";
  204 + protected static final String CUSTOMER_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the customer title.";
  205 + protected static final String EDGE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on the edge name.";
  206 + protected static final String EVENT_TEXT_SEARCH_DESCRIPTION = "The value is not used in searching.";
  207 + protected static final String AUDIT_LOG_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'startsWith' filter based on one of the next properties: entityType, entityName, userName, actionType, actionStatus.";
  208 + protected static final String SORT_PROPERTY_DESCRIPTION = "Property of entity to sort by";
  209 + protected static final String DASHBOARD_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title";
  210 + protected static final String CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, email, country, city";
  211 + protected static final String RPC_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, expirationTime, request, response";
  212 + protected static final String DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, deviceProfileName, label, customerTitle";
  213 + protected static final String USER_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, firstName, lastName, email";
  214 + protected static final String TENANT_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, email, country, state, city, address, address2, zip, phone, email";
  215 + protected static final String TENANT_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, description, isDefault";
  216 + protected static final String TENANT_PROFILE_INFO_SORT_PROPERTY_ALLOWABLE_VALUES = "id, name";
  217 + protected static final String TENANT_INFO_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, tenantProfileName, title, email, country, state, city, address, address2, zip, phone, email";
  218 + protected static final String DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, transportType, description, isDefault";
  219 + protected static final String ASSET_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
  220 + protected static final String ALARM_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, startTs, endTs, type, ackTs, clearTs, severity, status";
  221 + protected static final String EVENT_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, id";
  222 + protected static final String EDGE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
  223 + protected static final String RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, root";
  224 + protected static final String AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, entityType, entityName, userName, actionType, actionStatus";
  225 + protected static final String SORT_ORDER_DESCRIPTION = "Sort order. ASC (ASCENDING) or DESC (DESCENDING)";
  226 + protected static final String SORT_ORDER_ALLOWABLE_VALUES = "ASC, DESC";
  227 + protected static final String RPC_STATUS_ALLOWABLE_VALUES = "QUEUED, SENT, DELIVERED, SUCCESSFUL, TIMEOUT, EXPIRED, FAILED";
  228 + protected static final String RULE_CHAIN_TYPES_ALLOWABLE_VALUES = "CORE, EDGE";
  229 + protected static final String TRANSPORT_TYPE_ALLOWABLE_VALUES = "DEFAULT, MQTT, COAP, LWM2M, SNMP";
  230 + protected static final String DEVICE_INFO_DESCRIPTION = "Device Info is an extension of the default Device object that contains information about the assigned customer name and device profile name. ";
  231 + protected static final String ASSET_INFO_DESCRIPTION = "Asset Info is an extension of the default Asset object that contains information about the assigned customer name. ";
  232 + protected static final String ALARM_INFO_DESCRIPTION = "Alarm Info is an extension of the default Alarm object that also contains name of the alarm originator.";
  233 + protected static final String RELATION_INFO_DESCRIPTION = "Relation Info is an extension of the default Relation object that contains information about the 'from' and 'to' entity names. ";
  234 + protected static final String EDGE_INFO_DESCRIPTION = "Edge Info is an extension of the default Edge object that contains information about the assigned customer name. ";
  235 + protected static final String DEVICE_PROFILE_INFO_DESCRIPTION = "Device Profile Info is a lightweight object that includes main information about Device Profile excluding the heavyweight configuration object. ";
  236 +
  237 + protected static final String DEVICE_NAME_DESCRIPTION = "A string value representing the Device name.";
  238 + protected static final String ASSET_NAME_DESCRIPTION = "A string value representing the Asset name.";
  239 +
  240 + protected static final String EVENT_START_TIME_DESCRIPTION = "Timestamp. Events with creation time before it won't be queried.";
  241 + protected static final String EVENT_END_TIME_DESCRIPTION = "Timestamp. Events with creation time after it won't be queried.";
  242 +
  243 + 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. ";
  244 + 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)" ;
  245 + 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. ";
  246 + 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)";
  247 +
  248 + protected static final String MARKDOWN_CODE_BLOCK_START = "```json\n";
  249 + protected static final String MARKDOWN_CODE_BLOCK_END = "\n```";
  250 + protected static final String EVENT_ERROR_FILTER_OBJ = MARKDOWN_CODE_BLOCK_START + "{ \"eventType\": \"ERROR\", \"server\": \"ip-172-31-24-152\", " +
  251 + "\"method\": \"onClusterEventMsg\", \"error\": \"Error Message\" }" + MARKDOWN_CODE_BLOCK_END;
  252 + protected static final String EVENT_LC_EVENT_FILTER_OBJ = MARKDOWN_CODE_BLOCK_START + "{ \"eventType\": \"LC_EVENT\", \"server\": \"ip-172-31-24-152\", \"event\":" +
  253 + " \"STARTED\", \"status\": \"Success\", \"error\": \"Error Message\" }" + MARKDOWN_CODE_BLOCK_END;
  254 + protected static final String EVENT_STATS_FILTER_OBJ = MARKDOWN_CODE_BLOCK_START + "{ \"eventType\": \"STATS\", \"server\": \"ip-172-31-24-152\", \"messagesProcessed\": 10, \"errorsOccurred\": 5 }" + MARKDOWN_CODE_BLOCK_END;
  255 + protected static final String DEBUG_FILTER_OBJ = "\"msgDirectionType\": \"IN\", \"server\": \"ip-172-31-24-152\", \"dataSearch\": \"humidity\", " +
  256 + "\"metadataSearch\": \"deviceName\", \"entityName\": \"DEVICE\", \"relationType\": \"Success\"," +
  257 + " \"entityId\": \"de9d54a0-2b7a-11ec-a3cc-23386423d98f\", \"msgType\": \"POST_TELEMETRY_REQUEST\"," +
  258 + " \"isError\": \"false\", \"error\": \"Error Message\" }";
  259 + 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;
  260 + 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;
  261 +
  262 + 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.";
  263 + protected static final String RELATION_TYPE_GROUP_PARAM_DESCRIPTION = "A string value representing relation type group. For example, 'COMMON'";
  264 +
  265 + protected static final String ADMINISTRATOR_AUTHORITY_ONLY = "Available for users with 'Tenant Administrator' authority only.";
195 266
196 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; 267 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
197 protected static final String DEFAULT_DASHBOARD = "defaultDashboardId"; 268 protected static final String DEFAULT_DASHBOARD = "defaultDashboardId";
@@ -212,7 +212,7 @@ public class CustomerController extends BaseController { @@ -212,7 +212,7 @@ public class CustomerController extends BaseController {
212 } 212 }
213 213
214 @ApiOperation(value = "Get Tenant Customer by Customer title (getTenantCustomer)", 214 @ApiOperation(value = "Get Tenant Customer by Customer title (getTenantCustomer)",
215 - notes = "Get the Customer using Customer Title. Available for users with 'Tenant Administrator' authority only.") 215 + notes = "Get the Customer using Customer Title. " + ADMINISTRATOR_AUTHORITY_ONLY)
216 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 216 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
217 @RequestMapping(value = "/tenant/customers", params = {"customerTitle"}, method = RequestMethod.GET) 217 @RequestMapping(value = "/tenant/customers", params = {"customerTitle"}, method = RequestMethod.GET)
218 @ResponseBody 218 @ResponseBody
@@ -805,6 +805,13 @@ public class DashboardController extends BaseController { @@ -805,6 +805,13 @@ public class DashboardController extends BaseController {
805 return null; 805 return null;
806 } 806 }
807 807
  808 + @ApiOperation(value = "Assign dashboard to edge (assignDashboardToEdge)",
  809 + notes = "Creates assignment of an existing dashboard to an instance of The Edge. " +
  810 + EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  811 + "Second, remote edge service will receive a copy of assignment dashboard " +
  812 + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  813 + "Third, once dashboard will be delivered to edge service, it's going to be available for usage on remote edge instance.",
  814 + produces = MediaType.APPLICATION_JSON_VALUE)
808 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 815 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
809 @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST) 816 @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST)
810 @ResponseBody 817 @ResponseBody
@@ -838,6 +845,13 @@ public class DashboardController extends BaseController { @@ -838,6 +845,13 @@ public class DashboardController extends BaseController {
838 } 845 }
839 } 846 }
840 847
  848 + @ApiOperation(value = "Unassign dashboard from edge (unassignDashboardFromEdge)",
  849 + notes = "Clears assignment of the dashboard to the edge. " +
  850 + EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  851 + "Second, remote edge service will receive an 'unassign' command to remove dashboard " +
  852 + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  853 + "Third, once 'unassign' command will be delivered to edge service, it's going to remove dashboard locally.",
  854 + produces = MediaType.APPLICATION_JSON_VALUE)
841 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 855 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
842 @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) 856 @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE)
843 @ResponseBody 857 @ResponseBody
@@ -24,6 +24,7 @@ import io.swagger.annotations.ApiParam; @@ -24,6 +24,7 @@ import io.swagger.annotations.ApiParam;
24 import lombok.RequiredArgsConstructor; 24 import lombok.RequiredArgsConstructor;
25 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
26 import org.springframework.http.HttpStatus; 26 import org.springframework.http.HttpStatus;
  27 +import org.springframework.http.MediaType;
27 import org.springframework.http.ResponseEntity; 28 import org.springframework.http.ResponseEntity;
28 import org.springframework.security.access.prepost.PreAuthorize; 29 import org.springframework.security.access.prepost.PreAuthorize;
29 import org.springframework.web.bind.annotation.PathVariable; 30 import org.springframework.web.bind.annotation.PathVariable;
@@ -102,8 +103,8 @@ public class DeviceController extends BaseController { @@ -102,8 +103,8 @@ public class DeviceController extends BaseController {
102 103
103 @ApiOperation(value = "Get Device (getDeviceById)", 104 @ApiOperation(value = "Get Device (getDeviceById)",
104 notes = "Fetch the Device object based on the provided Device Id. " + 105 notes = "Fetch the Device object based on the provided Device Id. " +
105 - "If the user has the authority of 'Tenant Administrator', the server checks that the device is owned by the same tenant. " +  
106 - "If the user has the authority of 'Customer User', the server checks that the device is assigned to the same customer.") 106 + "If the user has the authority of 'TENANT_ADMIN', the server checks that the device is owned by the same tenant. " +
  107 + "If the user has the authority of 'CUSTOMER_USER', the server checks that the device is assigned to the same customer.")
107 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 108 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
108 @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET) 109 @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET)
109 @ResponseBody 110 @ResponseBody
@@ -141,7 +142,8 @@ public class DeviceController extends BaseController { @@ -141,7 +142,8 @@ public class DeviceController extends BaseController {
141 "Device credentials are also generated if not provided in the 'accessToken' request parameter. " + 142 "Device credentials are also generated if not provided in the 'accessToken' request parameter. " +
142 "The newly created device id will be present in the response. " + 143 "The newly created device id will be present in the response. " +
143 "Specify existing Device id to update the device. " + 144 "Specify existing Device id to update the device. " +
144 - "Referencing non-existing device Id will cause 'Not Found' error.") 145 + "Referencing non-existing device Id will cause 'Not Found' error." +
  146 + "\n\nDevice name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the device names and non-unique 'label' field for user-friendly visualization purposes.")
145 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 147 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
146 @RequestMapping(value = "/device", method = RequestMethod.POST) 148 @RequestMapping(value = "/device", method = RequestMethod.POST)
147 @ResponseBody 149 @ResponseBody
@@ -371,7 +373,7 @@ public class DeviceController extends BaseController { @@ -371,7 +373,7 @@ public class DeviceController extends BaseController {
371 373
372 @ApiOperation(value = "Get Tenant Devices (getTenantDevices)", 374 @ApiOperation(value = "Get Tenant Devices (getTenantDevices)",
373 notes = "Returns a page of devices owned by tenant. " + 375 notes = "Returns a page of devices owned by tenant. " +
374 - PAGE_DATA_PARAMETERS) 376 + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
375 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 377 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
376 @RequestMapping(value = "/tenant/devices", params = {"pageSize", "page"}, method = RequestMethod.GET) 378 @RequestMapping(value = "/tenant/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
377 @ResponseBody 379 @ResponseBody
@@ -414,7 +416,7 @@ public class DeviceController extends BaseController { @@ -414,7 +416,7 @@ public class DeviceController extends BaseController {
414 @RequestParam int page, 416 @RequestParam int page,
415 @ApiParam(value = DEVICE_TYPE_DESCRIPTION) 417 @ApiParam(value = DEVICE_TYPE_DESCRIPTION)
416 @RequestParam(required = false) String type, 418 @RequestParam(required = false) String type,
417 - @ApiParam(value = DEVICE_PROFILE_ID_DESCRIPTION) 419 + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
418 @RequestParam(required = false) String deviceProfileId, 420 @RequestParam(required = false) String deviceProfileId,
419 @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION) 421 @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION)
420 @RequestParam(required = false) String textSearch, 422 @RequestParam(required = false) String textSearch,
@@ -508,7 +510,7 @@ public class DeviceController extends BaseController { @@ -508,7 +510,7 @@ public class DeviceController extends BaseController {
508 @RequestParam int page, 510 @RequestParam int page,
509 @ApiParam(value = DEVICE_TYPE_DESCRIPTION) 511 @ApiParam(value = DEVICE_TYPE_DESCRIPTION)
510 @RequestParam(required = false) String type, 512 @RequestParam(required = false) String type,
511 - @ApiParam(value = DEVICE_PROFILE_ID_DESCRIPTION) 513 + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
512 @RequestParam(required = false) String deviceProfileId, 514 @RequestParam(required = false) String deviceProfileId,
513 @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION) 515 @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION)
514 @RequestParam(required = false) String textSearch, 516 @RequestParam(required = false) String textSearch,
@@ -571,7 +573,9 @@ public class DeviceController extends BaseController { @@ -571,7 +573,9 @@ public class DeviceController extends BaseController {
571 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 573 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
572 @RequestMapping(value = "/devices", method = RequestMethod.POST) 574 @RequestMapping(value = "/devices", method = RequestMethod.POST)
573 @ResponseBody 575 @ResponseBody
574 - public List<Device> findByQuery(@RequestBody DeviceSearchQuery query) throws ThingsboardException { 576 + public List<Device> findByQuery(
  577 + @ApiParam(value = "The device search query JSON")
  578 + @RequestBody DeviceSearchQuery query) throws ThingsboardException {
575 checkNotNull(query); 579 checkNotNull(query);
576 checkNotNull(query.getParameters()); 580 checkNotNull(query.getParameters());
577 checkNotNull(query.getDeviceTypes()); 581 checkNotNull(query.getDeviceTypes());
@@ -781,8 +785,11 @@ public class DeviceController extends BaseController { @@ -781,8 +785,11 @@ public class DeviceController extends BaseController {
781 785
782 @ApiOperation(value = "Assign device to edge (assignDeviceToEdge)", 786 @ApiOperation(value = "Assign device to edge (assignDeviceToEdge)",
783 notes = "Creates assignment of an existing device to an instance of The Edge. " + 787 notes = "Creates assignment of an existing device to an instance of The Edge. " +
784 - "The Edge is a software product for edge computing. " +  
785 - "It allows bringing data analysis and management to the edge, while seamlessly synchronizing with the platform server (cloud). ") 788 + EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  789 + "Second, remote edge service will receive a copy of assignment device " +
  790 + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  791 + "Third, once device will be delivered to edge service, it's going to be available for usage on remote edge instance.",
  792 + produces = MediaType.APPLICATION_JSON_VALUE)
786 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 793 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
787 @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST) 794 @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST)
788 @ResponseBody 795 @ResponseBody
@@ -820,7 +827,12 @@ public class DeviceController extends BaseController { @@ -820,7 +827,12 @@ public class DeviceController extends BaseController {
820 } 827 }
821 828
822 @ApiOperation(value = "Unassign device from edge (unassignDeviceFromEdge)", 829 @ApiOperation(value = "Unassign device from edge (unassignDeviceFromEdge)",
823 - notes = "Clears assignment of the device to the edge") 830 + notes = "Clears assignment of the device to the edge. " +
  831 + EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  832 + "Second, remote edge service will receive an 'unassign' command to remove device " +
  833 + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  834 + "Third, once 'unassign' command will be delivered to edge service, it's going to remove device locally.",
  835 + produces = MediaType.APPLICATION_JSON_VALUE)
824 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 836 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
825 @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE) 837 @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE)
826 @ResponseBody 838 @ResponseBody
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
19 import org.apache.commons.lang3.StringUtils; 21 import org.apache.commons.lang3.StringUtils;
20 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
@@ -58,10 +60,16 @@ public class DeviceProfileController extends BaseController { @@ -58,10 +60,16 @@ public class DeviceProfileController extends BaseController {
58 @Autowired 60 @Autowired
59 private TimeseriesService timeseriesService; 61 private TimeseriesService timeseriesService;
60 62
  63 + @ApiOperation(value = "Get Device Profile (getDeviceProfileById)",
  64 + notes = "Fetch the Device Profile object based on the provided Device Profile Id. " +
  65 + "The server checks that the device profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH,
  66 + produces = "application/json")
61 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 67 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
62 @RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.GET) 68 @RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.GET)
63 @ResponseBody 69 @ResponseBody
64 - public DeviceProfile getDeviceProfileById(@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { 70 + public DeviceProfile getDeviceProfileById(
  71 + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
  72 + @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
65 checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); 73 checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
66 try { 74 try {
67 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); 75 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
@@ -71,10 +79,16 @@ public class DeviceProfileController extends BaseController { @@ -71,10 +79,16 @@ public class DeviceProfileController extends BaseController {
71 } 79 }
72 } 80 }
73 81
  82 + @ApiOperation(value = "Get Device Profile Info (getDeviceProfileInfoById)",
  83 + notes = "Fetch the Device Profile Info object based on the provided Device Profile Id. "
  84 + + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_USER_AUTHORITY_PARAGRAPH,
  85 + produces = "application/json")
74 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 86 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
75 @RequestMapping(value = "/deviceProfileInfo/{deviceProfileId}", method = RequestMethod.GET) 87 @RequestMapping(value = "/deviceProfileInfo/{deviceProfileId}", method = RequestMethod.GET)
76 @ResponseBody 88 @ResponseBody
77 - public DeviceProfileInfo getDeviceProfileInfoById(@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { 89 + public DeviceProfileInfo getDeviceProfileInfoById(
  90 + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
  91 + @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
78 checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); 92 checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
79 try { 93 try {
80 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); 94 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
@@ -84,6 +98,10 @@ public class DeviceProfileController extends BaseController { @@ -84,6 +98,10 @@ public class DeviceProfileController extends BaseController {
84 } 98 }
85 } 99 }
86 100
  101 + @ApiOperation(value = "Get Default Device Profile (getDefaultDeviceProfileInfo)",
  102 + notes = "Fetch the Default Device Profile Info object. " +
  103 + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_USER_AUTHORITY_PARAGRAPH,
  104 + produces = "application/json")
87 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 105 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
88 @RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET) 106 @RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET)
89 @ResponseBody 107 @ResponseBody
@@ -95,10 +113,18 @@ public class DeviceProfileController extends BaseController { @@ -95,10 +113,18 @@ public class DeviceProfileController extends BaseController {
95 } 113 }
96 } 114 }
97 115
  116 + @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)",
  117 + notes = "Get a set of unique time-series keys used by devices that belong to specified profile. " +
  118 + "If profile is not set returns a list of unique keys among all profiles. " +
  119 + "The call is used for auto-complete in the UI forms. " +
  120 + "The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " +
  121 + TENANT_AUTHORITY_PARAGRAPH,
  122 + produces = "application/json")
98 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 123 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
99 @RequestMapping(value = "/deviceProfile/devices/keys/timeseries", method = RequestMethod.GET) 124 @RequestMapping(value = "/deviceProfile/devices/keys/timeseries", method = RequestMethod.GET)
100 @ResponseBody 125 @ResponseBody
101 public List<String> getTimeseriesKeys( 126 public List<String> getTimeseriesKeys(
  127 + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
102 @RequestParam(name = DEVICE_PROFILE_ID, required = false) String deviceProfileIdStr) throws ThingsboardException { 128 @RequestParam(name = DEVICE_PROFILE_ID, required = false) String deviceProfileIdStr) throws ThingsboardException {
103 DeviceProfileId deviceProfileId; 129 DeviceProfileId deviceProfileId;
104 if (StringUtils.isNotEmpty(deviceProfileIdStr)) { 130 if (StringUtils.isNotEmpty(deviceProfileIdStr)) {
@@ -115,10 +141,18 @@ public class DeviceProfileController extends BaseController { @@ -115,10 +141,18 @@ public class DeviceProfileController extends BaseController {
115 } 141 }
116 } 142 }
117 143
  144 + @ApiOperation(value = "Get attribute keys (getAttributesKeys)",
  145 + notes = "Get a set of unique attribute keys used by devices that belong to specified profile. " +
  146 + "If profile is not set returns a list of unique keys among all profiles. " +
  147 + "The call is used for auto-complete in the UI forms. " +
  148 + "The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " +
  149 + TENANT_AUTHORITY_PARAGRAPH,
  150 + produces = "application/json")
118 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 151 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
119 @RequestMapping(value = "/deviceProfile/devices/keys/attributes", method = RequestMethod.GET) 152 @RequestMapping(value = "/deviceProfile/devices/keys/attributes", method = RequestMethod.GET)
120 @ResponseBody 153 @ResponseBody
121 public List<String> getAttributesKeys( 154 public List<String> getAttributesKeys(
  155 + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
122 @RequestParam(name = DEVICE_PROFILE_ID, required = false) String deviceProfileIdStr) throws ThingsboardException { 156 @RequestParam(name = DEVICE_PROFILE_ID, required = false) String deviceProfileIdStr) throws ThingsboardException {
123 DeviceProfileId deviceProfileId; 157 DeviceProfileId deviceProfileId;
124 if (StringUtils.isNotEmpty(deviceProfileIdStr)) { 158 if (StringUtils.isNotEmpty(deviceProfileIdStr)) {
@@ -135,10 +169,20 @@ public class DeviceProfileController extends BaseController { @@ -135,10 +169,20 @@ public class DeviceProfileController extends BaseController {
135 } 169 }
136 } 170 }
137 171
  172 + @ApiOperation(value = "Create Or Update Device Profile (saveDevice)",
  173 + 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). " +
  174 + "The newly created device profile id will be present in the response. " +
  175 + "Specify existing device profile id to update the device profile. " +
  176 + "Referencing non-existing device profile Id will cause 'Not Found' error. " +
  177 + "\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,
  178 + produces = "application/json",
  179 + consumes = "application/json")
138 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 180 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
139 @RequestMapping(value = "/deviceProfile", method = RequestMethod.POST) 181 @RequestMapping(value = "/deviceProfile", method = RequestMethod.POST)
140 @ResponseBody 182 @ResponseBody
141 - public DeviceProfile saveDeviceProfile(@RequestBody DeviceProfile deviceProfile) throws ThingsboardException { 183 + public DeviceProfile saveDeviceProfile(
  184 + @ApiParam(value = "A JSON value representing the device profile.")
  185 + @RequestBody DeviceProfile deviceProfile) throws ThingsboardException {
142 try { 186 try {
143 boolean created = deviceProfile.getId() == null; 187 boolean created = deviceProfile.getId() == null;
144 deviceProfile.setTenantId(getTenantId()); 188 deviceProfile.setTenantId(getTenantId());
@@ -180,10 +224,16 @@ public class DeviceProfileController extends BaseController { @@ -180,10 +224,16 @@ public class DeviceProfileController extends BaseController {
180 } 224 }
181 } 225 }
182 226
  227 + @ApiOperation(value = "Delete device profile (deleteDeviceProfile)",
  228 + notes = "Deletes the device profile. Referencing non-existing device profile Id will cause an error. " +
  229 + "Can't delete the device profile if it is referenced by existing devices." + TENANT_AUTHORITY_PARAGRAPH,
  230 + produces = "application/json")
183 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 231 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
184 @RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.DELETE) 232 @RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.DELETE)
185 @ResponseStatus(value = HttpStatus.OK) 233 @ResponseStatus(value = HttpStatus.OK)
186 - public void deleteDeviceProfile(@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { 234 + public void deleteDeviceProfile(
  235 + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
  236 + @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
187 checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); 237 checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
188 try { 238 try {
189 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); 239 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
@@ -207,10 +257,15 @@ public class DeviceProfileController extends BaseController { @@ -207,10 +257,15 @@ public class DeviceProfileController extends BaseController {
207 } 257 }
208 } 258 }
209 259
  260 + @ApiOperation(value = "Make Device Profile Default (setDefaultDeviceProfile)",
  261 + notes = "Marks device profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH,
  262 + produces = "application/json")
210 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 263 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
211 @RequestMapping(value = "/deviceProfile/{deviceProfileId}/default", method = RequestMethod.POST) 264 @RequestMapping(value = "/deviceProfile/{deviceProfileId}/default", method = RequestMethod.POST)
212 @ResponseBody 265 @ResponseBody
213 - public DeviceProfile setDefaultDeviceProfile(@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { 266 + public DeviceProfile setDefaultDeviceProfile(
  267 + @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
  268 + @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
214 checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); 269 checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
215 try { 270 try {
216 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); 271 DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
@@ -238,14 +293,24 @@ public class DeviceProfileController extends BaseController { @@ -238,14 +293,24 @@ public class DeviceProfileController extends BaseController {
238 } 293 }
239 } 294 }
240 295
  296 + @ApiOperation(value = "Get Device Profiles (getDeviceProfiles)",
  297 + notes = "Returns a page of devices profile objects owned by tenant. " +
  298 + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH,
  299 + produces = "application/json")
241 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 300 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
242 @RequestMapping(value = "/deviceProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) 301 @RequestMapping(value = "/deviceProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET)
243 @ResponseBody 302 @ResponseBody
244 - public PageData<DeviceProfile> getDeviceProfiles(@RequestParam int pageSize,  
245 - @RequestParam int page,  
246 - @RequestParam(required = false) String textSearch,  
247 - @RequestParam(required = false) String sortProperty,  
248 - @RequestParam(required = false) String sortOrder) throws ThingsboardException { 303 + public PageData<DeviceProfile> getDeviceProfiles(
  304 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
  305 + @RequestParam int pageSize,
  306 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
  307 + @RequestParam int page,
  308 + @ApiParam(value = DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION)
  309 + @RequestParam(required = false) String textSearch,
  310 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES)
  311 + @RequestParam(required = false) String sortProperty,
  312 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
  313 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
249 try { 314 try {
250 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 315 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
251 return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink)); 316 return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink));
@@ -254,15 +319,26 @@ public class DeviceProfileController extends BaseController { @@ -254,15 +319,26 @@ public class DeviceProfileController extends BaseController {
254 } 319 }
255 } 320 }
256 321
  322 + @ApiOperation(value = "Get Device Profiles for transport type (getDeviceProfileInfos)",
  323 + notes = "Returns a page of devices profile info objects owned by tenant. " +
  324 + PAGE_DATA_PARAMETERS + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_USER_AUTHORITY_PARAGRAPH,
  325 + produces = "application/json")
257 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 326 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
258 @RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) 327 @RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
259 @ResponseBody 328 @ResponseBody
260 - public PageData<DeviceProfileInfo> getDeviceProfileInfos(@RequestParam int pageSize,  
261 - @RequestParam int page,  
262 - @RequestParam(required = false) String textSearch,  
263 - @RequestParam(required = false) String sortProperty,  
264 - @RequestParam(required = false) String sortOrder,  
265 - @RequestParam(required = false) String transportType) throws ThingsboardException { 329 + public PageData<DeviceProfileInfo> getDeviceProfileInfos(
  330 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
  331 + @RequestParam int pageSize,
  332 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
  333 + @RequestParam int page,
  334 + @ApiParam(value = DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION)
  335 + @RequestParam(required = false) String textSearch,
  336 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES)
  337 + @RequestParam(required = false) String sortProperty,
  338 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
  339 + @RequestParam(required = false) String sortOrder,
  340 + @ApiParam(value = "Type of the transport", allowableValues = TRANSPORT_TYPE_ALLOWABLE_VALUES)
  341 + @RequestParam(required = false) String transportType) throws ThingsboardException {
266 try { 342 try {
267 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 343 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
268 return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType)); 344 return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType));
@@ -17,9 +17,12 @@ package org.thingsboard.server.controller; @@ -17,9 +17,12 @@ package org.thingsboard.server.controller;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 import com.google.common.util.concurrent.ListenableFuture; 19 import com.google.common.util.concurrent.ListenableFuture;
  20 +import io.swagger.annotations.ApiOperation;
  21 +import io.swagger.annotations.ApiParam;
20 import lombok.RequiredArgsConstructor; 22 import lombok.RequiredArgsConstructor;
21 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.http.HttpStatus; 24 import org.springframework.http.HttpStatus;
  25 +import org.springframework.http.MediaType;
23 import org.springframework.http.ResponseEntity; 26 import org.springframework.http.ResponseEntity;
24 import org.springframework.security.access.prepost.PreAuthorize; 27 import org.springframework.security.access.prepost.PreAuthorize;
25 import org.springframework.web.bind.annotation.PathVariable; 28 import org.springframework.web.bind.annotation.PathVariable;
@@ -75,7 +78,11 @@ public class EdgeController extends BaseController { @@ -75,7 +78,11 @@ public class EdgeController extends BaseController {
75 private final EdgeBulkImportService edgeBulkImportService; 78 private final EdgeBulkImportService edgeBulkImportService;
76 79
77 public static final String EDGE_ID = "edgeId"; 80 public static final String EDGE_ID = "edgeId";
  81 + public static final String EDGE_SECURITY_CHECK = "If the user has the authority of 'Tenant Administrator', the server checks that the edge is owned by the same tenant. " +
  82 + "If the user has the authority of 'Customer User', the server checks that the edge is assigned to the same customer.";
78 83
  84 + @ApiOperation(value = "Is edges support enabled (isEdgesSupportEnabled)",
  85 + notes = "Returns 'true' if edges support enabled on server, 'false' - otherwise.")
79 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 86 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
80 @RequestMapping(value = "/edges/enabled", method = RequestMethod.GET) 87 @RequestMapping(value = "/edges/enabled", method = RequestMethod.GET)
81 @ResponseBody 88 @ResponseBody
@@ -83,10 +90,14 @@ public class EdgeController extends BaseController { @@ -83,10 +90,14 @@ public class EdgeController extends BaseController {
83 return edgesEnabled; 90 return edgesEnabled;
84 } 91 }
85 92
  93 + @ApiOperation(value = "Get Edge (getEdgeById)",
  94 + notes = "Get the Edge object based on the provided Edge Id. " + EDGE_SECURITY_CHECK,
  95 + produces = MediaType.APPLICATION_JSON_VALUE)
86 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 96 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
87 @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET) 97 @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET)
88 @ResponseBody 98 @ResponseBody
89 - public Edge getEdgeById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { 99 + public Edge getEdgeById(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
  100 + @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
90 checkParameter(EDGE_ID, strEdgeId); 101 checkParameter(EDGE_ID, strEdgeId);
91 try { 102 try {
92 EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); 103 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -100,10 +111,14 @@ public class EdgeController extends BaseController { @@ -100,10 +111,14 @@ public class EdgeController extends BaseController {
100 } 111 }
101 } 112 }
102 113
  114 + @ApiOperation(value = "Get Edge Info (getEdgeInfoById)",
  115 + notes = "Get the Edge Info object based on the provided Edge Id. " + EDGE_SECURITY_CHECK,
  116 + produces = MediaType.APPLICATION_JSON_VALUE)
103 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 117 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
104 @RequestMapping(value = "/edge/info/{edgeId}", method = RequestMethod.GET) 118 @RequestMapping(value = "/edge/info/{edgeId}", method = RequestMethod.GET)
105 @ResponseBody 119 @ResponseBody
106 - public EdgeInfo getEdgeInfoById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { 120 + public EdgeInfo getEdgeInfoById(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
  121 + @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
107 checkParameter(EDGE_ID, strEdgeId); 122 checkParameter(EDGE_ID, strEdgeId);
108 try { 123 try {
109 EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); 124 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -117,10 +132,18 @@ public class EdgeController extends BaseController { @@ -117,10 +132,18 @@ public class EdgeController extends BaseController {
117 } 132 }
118 } 133 }
119 134
  135 + @ApiOperation(value = "Create Or Update Edge (saveEdge)",
  136 + notes = "Create or update the Edge. When creating edge, platform generates Edge Id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address). " +
  137 + "The newly created edge id will be present in the response. " +
  138 + "Specify existing Edge id to update the edge. " +
  139 + "Referencing non-existing Edge Id will cause 'Not Found' error." +
  140 + "\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes.",
  141 + produces = MediaType.APPLICATION_JSON_VALUE)
120 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 142 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
121 @RequestMapping(value = "/edge", method = RequestMethod.POST) 143 @RequestMapping(value = "/edge", method = RequestMethod.POST)
122 @ResponseBody 144 @ResponseBody
123 - public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException { 145 + public Edge saveEdge(@ApiParam(value = "A JSON value representing the edge.", required = true)
  146 + @RequestBody Edge edge) throws ThingsboardException {
124 try { 147 try {
125 TenantId tenantId = getCurrentUser().getTenantId(); 148 TenantId tenantId = getCurrentUser().getTenantId();
126 edge.setTenantId(tenantId); 149 edge.setTenantId(tenantId);
@@ -163,10 +186,13 @@ public class EdgeController extends BaseController { @@ -163,10 +186,13 @@ public class EdgeController extends BaseController {
163 logEntityAction(user, edge.getId(), edge, null, updated ? ActionType.UPDATED : ActionType.ADDED, null); 186 logEntityAction(user, edge.getId(), edge, null, updated ? ActionType.UPDATED : ActionType.ADDED, null);
164 } 187 }
165 188
  189 + @ApiOperation(value = "Delete edge (deleteEdge)",
  190 + notes = "Deletes the edge. Referencing non-existing edge Id will cause an error.")
166 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 191 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
167 @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE) 192 @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE)
168 @ResponseStatus(value = HttpStatus.OK) 193 @ResponseStatus(value = HttpStatus.OK)
169 - public void deleteEdge(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { 194 + public void deleteEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
  195 + @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
170 checkParameter(EDGE_ID, strEdgeId); 196 checkParameter(EDGE_ID, strEdgeId);
171 try { 197 try {
172 EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); 198 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -191,13 +217,21 @@ public class EdgeController extends BaseController { @@ -191,13 +217,21 @@ public class EdgeController extends BaseController {
191 } 217 }
192 } 218 }
193 219
  220 + @ApiOperation(value = "Get Tenant Edges (getEdges)",
  221 + notes = "Returns a page of edges owned by tenant. " +
  222 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
194 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 223 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
195 @RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) 224 @RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
196 @ResponseBody 225 @ResponseBody
197 - public PageData<Edge> getEdges(@RequestParam int pageSize, 226 + public PageData<Edge> getEdges(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
  227 + @RequestParam int pageSize,
  228 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
198 @RequestParam int page, 229 @RequestParam int page,
  230 + @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
199 @RequestParam(required = false) String textSearch, 231 @RequestParam(required = false) String textSearch,
  232 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
200 @RequestParam(required = false) String sortProperty, 233 @RequestParam(required = false) String sortProperty,
  234 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
201 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 235 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
202 try { 236 try {
203 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 237 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -208,10 +242,15 @@ public class EdgeController extends BaseController { @@ -208,10 +242,15 @@ public class EdgeController extends BaseController {
208 } 242 }
209 } 243 }
210 244
  245 + @ApiOperation(value = "Assign edge to customer (assignEdgeToCustomer)",
  246 + notes = "Creates assignment of the edge to customer. Customer will be able to query edge afterwards.",
  247 + produces = MediaType.APPLICATION_JSON_VALUE)
211 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 248 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
212 @RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST) 249 @RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST)
213 @ResponseBody 250 @ResponseBody
214 - public Edge assignEdgeToCustomer(@PathVariable("customerId") String strCustomerId, 251 + public Edge assignEdgeToCustomer(@ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
  252 + @PathVariable("customerId") String strCustomerId,
  253 + @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
215 @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { 254 @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
216 checkParameter("customerId", strCustomerId); 255 checkParameter("customerId", strCustomerId);
217 checkParameter(EDGE_ID, strEdgeId); 256 checkParameter(EDGE_ID, strEdgeId);
@@ -243,10 +282,14 @@ public class EdgeController extends BaseController { @@ -243,10 +282,14 @@ public class EdgeController extends BaseController {
243 } 282 }
244 } 283 }
245 284
  285 + @ApiOperation(value = "Unassign edge from customer (unassignEdgeFromCustomer)",
  286 + notes = "Clears assignment of the edge to customer. Customer will not be able to query edge afterwards.",
  287 + produces = MediaType.APPLICATION_JSON_VALUE)
246 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 288 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
247 @RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE) 289 @RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE)
248 @ResponseBody 290 @ResponseBody
249 - public Edge unassignEdgeFromCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { 291 + public Edge unassignEdgeFromCustomer(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
  292 + @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
250 checkParameter(EDGE_ID, strEdgeId); 293 checkParameter(EDGE_ID, strEdgeId);
251 try { 294 try {
252 EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); 295 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -277,10 +320,16 @@ public class EdgeController extends BaseController { @@ -277,10 +320,16 @@ public class EdgeController extends BaseController {
277 } 320 }
278 } 321 }
279 322
  323 + @ApiOperation(value = "Make edge publicly available (assignEdgeToPublicCustomer)",
  324 + notes = "Edge will be available for non-authorized (not logged-in) users. " +
  325 + "This is useful to create dashboards that you plan to share/embed on a publicly available website. " +
  326 + "However, users that are logged-in and belong to different tenant will not be able to access the edge.",
  327 + produces = MediaType.APPLICATION_JSON_VALUE)
280 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 328 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
281 @RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST) 329 @RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST)
282 @ResponseBody 330 @ResponseBody
283 - public Edge assignEdgeToPublicCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { 331 + public Edge assignEdgeToPublicCustomer(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
  332 + @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
284 checkParameter(EDGE_ID, strEdgeId); 333 checkParameter(EDGE_ID, strEdgeId);
285 try { 334 try {
286 EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); 335 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -304,15 +353,24 @@ public class EdgeController extends BaseController { @@ -304,15 +353,24 @@ public class EdgeController extends BaseController {
304 } 353 }
305 } 354 }
306 355
  356 + @ApiOperation(value = "Get Tenant Edges (getTenantEdges)",
  357 + notes = "Returns a page of edges owned by tenant. " +
  358 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
307 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 359 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
308 @RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) 360 @RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
309 @ResponseBody 361 @ResponseBody
310 public PageData<Edge> getTenantEdges( 362 public PageData<Edge> getTenantEdges(
  363 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
311 @RequestParam int pageSize, 364 @RequestParam int pageSize,
  365 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
312 @RequestParam int page, 366 @RequestParam int page,
  367 + @ApiParam(value = EDGE_TYPE_DESCRIPTION)
313 @RequestParam(required = false) String type, 368 @RequestParam(required = false) String type,
  369 + @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
314 @RequestParam(required = false) String textSearch, 370 @RequestParam(required = false) String textSearch,
  371 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
315 @RequestParam(required = false) String sortProperty, 372 @RequestParam(required = false) String sortProperty,
  373 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
316 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 374 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
317 try { 375 try {
318 TenantId tenantId = getCurrentUser().getTenantId(); 376 TenantId tenantId = getCurrentUser().getTenantId();
@@ -327,15 +385,25 @@ public class EdgeController extends BaseController { @@ -327,15 +385,25 @@ public class EdgeController extends BaseController {
327 } 385 }
328 } 386 }
329 387
  388 + @ApiOperation(value = "Get Tenant Edge Infos (getTenantEdgeInfos)",
  389 + notes = "Returns a page of edges info objects owned by tenant. " +
  390 + PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION,
  391 + produces = MediaType.APPLICATION_JSON_VALUE)
330 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 392 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
331 @RequestMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) 393 @RequestMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
332 @ResponseBody 394 @ResponseBody
333 public PageData<EdgeInfo> getTenantEdgeInfos( 395 public PageData<EdgeInfo> getTenantEdgeInfos(
  396 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
334 @RequestParam int pageSize, 397 @RequestParam int pageSize,
  398 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
335 @RequestParam int page, 399 @RequestParam int page,
  400 + @ApiParam(value = EDGE_TYPE_DESCRIPTION)
336 @RequestParam(required = false) String type, 401 @RequestParam(required = false) String type,
  402 + @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
337 @RequestParam(required = false) String textSearch, 403 @RequestParam(required = false) String textSearch,
  404 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
338 @RequestParam(required = false) String sortProperty, 405 @RequestParam(required = false) String sortProperty,
  406 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
339 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 407 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
340 try { 408 try {
341 TenantId tenantId = getCurrentUser().getTenantId(); 409 TenantId tenantId = getCurrentUser().getTenantId();
@@ -350,10 +418,15 @@ public class EdgeController extends BaseController { @@ -350,10 +418,15 @@ public class EdgeController extends BaseController {
350 } 418 }
351 } 419 }
352 420
  421 + @ApiOperation(value = "Get Tenant Edge (getTenantEdge)",
  422 + notes = "Requested edge must be owned by tenant or customer that the user belongs to. " +
  423 + "Edge name is an unique property of edge. So it can be used to identify the edge.",
  424 + produces = MediaType.APPLICATION_JSON_VALUE)
353 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 425 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
354 @RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET) 426 @RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET)
355 @ResponseBody 427 @ResponseBody
356 - public Edge getTenantEdge(@RequestParam String edgeName) throws ThingsboardException { 428 + public Edge getTenantEdge(@ApiParam(value = "Unique name of the edge", required = true)
  429 + @RequestParam String edgeName) throws ThingsboardException {
357 try { 430 try {
358 TenantId tenantId = getCurrentUser().getTenantId(); 431 TenantId tenantId = getCurrentUser().getTenantId();
359 return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName)); 432 return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName));
@@ -362,10 +435,16 @@ public class EdgeController extends BaseController { @@ -362,10 +435,16 @@ public class EdgeController extends BaseController {
362 } 435 }
363 } 436 }
364 437
  438 + @ApiOperation(value = "Set root rule chain for provided edge (setRootRuleChain)",
  439 + notes = "Change root rule chain of the edge to the new provided rule chain. \n" +
  440 + "This operation will send a notification to update root rule chain on remote edge service.",
  441 + produces = MediaType.APPLICATION_JSON_VALUE)
365 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 442 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
366 @RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST) 443 @RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST)
367 @ResponseBody 444 @ResponseBody
368 - public Edge setRootRuleChain(@PathVariable(EDGE_ID) String strEdgeId, 445 + public Edge setRootRuleChain(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
  446 + @PathVariable(EDGE_ID) String strEdgeId,
  447 + @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION, required = true)
369 @PathVariable("ruleChainId") String strRuleChainId) throws ThingsboardException { 448 @PathVariable("ruleChainId") String strRuleChainId) throws ThingsboardException {
370 checkParameter(EDGE_ID, strEdgeId); 449 checkParameter(EDGE_ID, strEdgeId);
371 checkParameter("ruleChainId", strRuleChainId); 450 checkParameter("ruleChainId", strRuleChainId);
@@ -394,16 +473,26 @@ public class EdgeController extends BaseController { @@ -394,16 +473,26 @@ public class EdgeController extends BaseController {
394 } 473 }
395 } 474 }
396 475
  476 + @ApiOperation(value = "Get Customer Edges (getCustomerEdges)",
  477 + notes = "Returns a page of edges objects assigned to customer. " +
  478 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
397 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 479 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
398 @RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) 480 @RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
399 @ResponseBody 481 @ResponseBody
400 public PageData<Edge> getCustomerEdges( 482 public PageData<Edge> getCustomerEdges(
  483 + @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
401 @PathVariable("customerId") String strCustomerId, 484 @PathVariable("customerId") String strCustomerId,
  485 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
402 @RequestParam int pageSize, 486 @RequestParam int pageSize,
  487 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
403 @RequestParam int page, 488 @RequestParam int page,
  489 + @ApiParam(value = EDGE_TYPE_DESCRIPTION)
404 @RequestParam(required = false) String type, 490 @RequestParam(required = false) String type,
  491 + @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
405 @RequestParam(required = false) String textSearch, 492 @RequestParam(required = false) String textSearch,
  493 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
406 @RequestParam(required = false) String sortProperty, 494 @RequestParam(required = false) String sortProperty,
  495 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
407 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 496 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
408 checkParameter("customerId", strCustomerId); 497 checkParameter("customerId", strCustomerId);
409 try { 498 try {
@@ -429,16 +518,26 @@ public class EdgeController extends BaseController { @@ -429,16 +518,26 @@ public class EdgeController extends BaseController {
429 } 518 }
430 } 519 }
431 520
  521 + @ApiOperation(value = "Get Customer Edge Infos (getCustomerEdgeInfos)",
  522 + notes = "Returns a page of edges info objects assigned to customer. " +
  523 + PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION, produces = MediaType.APPLICATION_JSON_VALUE)
432 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 524 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
433 @RequestMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) 525 @RequestMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
434 @ResponseBody 526 @ResponseBody
435 public PageData<EdgeInfo> getCustomerEdgeInfos( 527 public PageData<EdgeInfo> getCustomerEdgeInfos(
  528 + @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
436 @PathVariable("customerId") String strCustomerId, 529 @PathVariable("customerId") String strCustomerId,
  530 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
437 @RequestParam int pageSize, 531 @RequestParam int pageSize,
  532 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
438 @RequestParam int page, 533 @RequestParam int page,
  534 + @ApiParam(value = EDGE_TYPE_DESCRIPTION)
439 @RequestParam(required = false) String type, 535 @RequestParam(required = false) String type,
  536 + @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
440 @RequestParam(required = false) String textSearch, 537 @RequestParam(required = false) String textSearch,
  538 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
441 @RequestParam(required = false) String sortProperty, 539 @RequestParam(required = false) String sortProperty,
  540 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
442 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 541 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
443 checkParameter("customerId", strCustomerId); 542 checkParameter("customerId", strCustomerId);
444 try { 543 try {
@@ -464,10 +563,14 @@ public class EdgeController extends BaseController { @@ -464,10 +563,14 @@ public class EdgeController extends BaseController {
464 } 563 }
465 } 564 }
466 565
  566 + @ApiOperation(value = "Get Edges By Ids (getEdgesByIds)",
  567 + notes = "Requested edges must be owned by tenant or assigned to customer which user is performing the request.",
  568 + produces = MediaType.APPLICATION_JSON_VALUE)
467 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 569 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
468 @RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET) 570 @RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET)
469 @ResponseBody 571 @ResponseBody
470 public List<Edge> getEdgesByIds( 572 public List<Edge> getEdgesByIds(
  573 + @ApiParam(value = "A list of edges ids, separated by comma ','", required = true)
471 @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException { 574 @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException {
472 checkArrayParameter("edgeIds", strEdgeIds); 575 checkArrayParameter("edgeIds", strEdgeIds);
473 try { 576 try {
@@ -496,6 +599,11 @@ public class EdgeController extends BaseController { @@ -496,6 +599,11 @@ public class EdgeController extends BaseController {
496 } 599 }
497 } 600 }
498 601
  602 + @ApiOperation(value = "Find related edges (findByQuery)",
  603 + notes = "Returns all edges that are related to the specific entity. " +
  604 + "The entity id, relation type, edge types, depth of the search, and other query parameters defined using complex 'EdgeSearchQuery' object. " +
  605 + "See 'Model' tab of the Parameters for more info.",
  606 + produces = MediaType.APPLICATION_JSON_VALUE)
499 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 607 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
500 @RequestMapping(value = "/edges", method = RequestMethod.POST) 608 @RequestMapping(value = "/edges", method = RequestMethod.POST)
501 @ResponseBody 609 @ResponseBody
@@ -527,6 +635,9 @@ public class EdgeController extends BaseController { @@ -527,6 +635,9 @@ public class EdgeController extends BaseController {
527 } 635 }
528 } 636 }
529 637
  638 + @ApiOperation(value = "Get Edge Types (getEdgeTypes)",
  639 + notes = "Returns a set of unique edge types based on edges that are either owned by the tenant or assigned to the customer which user is performing the request.",
  640 + produces = MediaType.APPLICATION_JSON_VALUE)
530 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 641 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
531 @RequestMapping(value = "/edge/types", method = RequestMethod.GET) 642 @RequestMapping(value = "/edge/types", method = RequestMethod.GET)
532 @ResponseBody 643 @ResponseBody
@@ -541,9 +652,13 @@ public class EdgeController extends BaseController { @@ -541,9 +652,13 @@ public class EdgeController extends BaseController {
541 } 652 }
542 } 653 }
543 654
  655 + @ApiOperation(value = "Sync edge (syncEdge)",
  656 + notes = "Starts synchronization process between edge and cloud. \n" +
  657 + "All entities that are assigned to particular edge are going to be send to remote edge service.")
544 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 658 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
545 @RequestMapping(value = "/edge/sync/{edgeId}", method = RequestMethod.POST) 659 @RequestMapping(value = "/edge/sync/{edgeId}", method = RequestMethod.POST)
546 - public void syncEdge(@PathVariable("edgeId") String strEdgeId) throws ThingsboardException { 660 + public void syncEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
  661 + @PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
547 checkParameter("edgeId", strEdgeId); 662 checkParameter("edgeId", strEdgeId);
548 try { 663 try {
549 if (isEdgesEnabled()) { 664 if (isEdgesEnabled()) {
@@ -560,10 +675,13 @@ public class EdgeController extends BaseController { @@ -560,10 +675,13 @@ public class EdgeController extends BaseController {
560 } 675 }
561 } 676 }
562 677
  678 + @ApiOperation(value = "Find missing rule chains (findMissingToRelatedRuleChains)",
  679 + notes = "Returns list of rule chains ids that are not assigned to particular edge, but these rule chains are present in the already assigned rule chains to edge.")
563 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 680 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
564 @RequestMapping(value = "/edge/missingToRelatedRuleChains/{edgeId}", method = RequestMethod.GET) 681 @RequestMapping(value = "/edge/missingToRelatedRuleChains/{edgeId}", method = RequestMethod.GET)
565 @ResponseBody 682 @ResponseBody
566 - public String findMissingToRelatedRuleChains(@PathVariable("edgeId") String strEdgeId) throws ThingsboardException { 683 + public String findMissingToRelatedRuleChains(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
  684 + @PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
567 try { 685 try {
568 EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); 686 EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
569 edgeId = checkNotNull(edgeId); 687 edgeId = checkNotNull(edgeId);
@@ -575,9 +693,12 @@ public class EdgeController extends BaseController { @@ -575,9 +693,12 @@ public class EdgeController extends BaseController {
575 } 693 }
576 } 694 }
577 695
  696 + @ApiOperation(value = "Import the bulk of edges (processEdgesBulkImport)",
  697 + notes = "There's an ability to import the bulk of edges using the only .csv file.",
  698 + produces = MediaType.APPLICATION_JSON_VALUE)
578 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 699 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
579 @PostMapping("/edge/bulk_import") 700 @PostMapping("/edge/bulk_import")
580 - public BulkImportResult<Edge> processEdgeBulkImport(@RequestBody BulkImportRequest request) throws Exception { 701 + public BulkImportResult<Edge> processEdgesBulkImport(@RequestBody BulkImportRequest request) throws Exception {
581 SecurityUser user = getCurrentUser(); 702 SecurityUser user = getCurrentUser();
582 RuleChain edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(user.getTenantId()); 703 RuleChain edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(user.getTenantId());
583 if (edgeTemplateRootRuleChain == null) { 704 if (edgeTemplateRootRuleChain == null) {
@@ -15,8 +15,11 @@ @@ -15,8 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.http.MediaType;
20 import org.springframework.security.access.prepost.PreAuthorize; 23 import org.springframework.security.access.prepost.PreAuthorize;
21 import org.springframework.web.bind.annotation.PathVariable; 24 import org.springframework.web.bind.annotation.PathVariable;
22 import org.springframework.web.bind.annotation.RequestMapping; 25 import org.springframework.web.bind.annotation.RequestMapping;
@@ -45,17 +48,28 @@ public class EdgeEventController extends BaseController { @@ -45,17 +48,28 @@ public class EdgeEventController extends BaseController {
45 48
46 public static final String EDGE_ID = "edgeId"; 49 public static final String EDGE_ID = "edgeId";
47 50
  51 + @ApiOperation(value = "Get Edge Events (getEdgeEvents)",
  52 + notes = "Returns a page of edge events for the requested edge. " +
  53 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
48 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 54 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
49 @RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET) 55 @RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET)
50 @ResponseBody 56 @ResponseBody
51 public PageData<EdgeEvent> getEdgeEvents( 57 public PageData<EdgeEvent> getEdgeEvents(
  58 + @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
52 @PathVariable(EDGE_ID) String strEdgeId, 59 @PathVariable(EDGE_ID) String strEdgeId,
  60 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
53 @RequestParam int pageSize, 61 @RequestParam int pageSize,
  62 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
54 @RequestParam int page, 63 @RequestParam int page,
  64 + @ApiParam(value = "The case insensitive 'startsWith' filter based on the edge event type name.")
55 @RequestParam(required = false) String textSearch, 65 @RequestParam(required = false) String textSearch,
  66 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
56 @RequestParam(required = false) String sortProperty, 67 @RequestParam(required = false) String sortProperty,
  68 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
57 @RequestParam(required = false) String sortOrder, 69 @RequestParam(required = false) String sortOrder,
  70 + @ApiParam(value = "Timestamp. Edge events with creation time before it won't be queried")
58 @RequestParam(required = false) Long startTime, 71 @RequestParam(required = false) Long startTime,
  72 + @ApiParam(value = "Timestamp. Edge events with creation time after it won't be queried")
59 @RequestParam(required = false) Long endTime) throws ThingsboardException { 73 @RequestParam(required = false) Long endTime) throws ThingsboardException {
60 checkParameter(EDGE_ID, strEdgeId); 74 checkParameter(EDGE_ID, strEdgeId);
61 try { 75 try {
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.http.ResponseEntity; 21 import org.springframework.http.ResponseEntity;
20 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
@@ -42,15 +44,650 @@ import org.thingsboard.server.service.query.EntityQueryService; @@ -42,15 +44,650 @@ import org.thingsboard.server.service.query.EntityQueryService;
42 @RequestMapping("/api") 44 @RequestMapping("/api")
43 public class EntityQueryController extends BaseController { 45 public class EntityQueryController extends BaseController {
44 46
  47 + private static final String SINGLE_ENTITY = "\n\n## Single Entity\n\n" +
  48 + "Allows to filter only one entity based on the id. For example, this entity filter selects certain device:\n\n"+
  49 + MARKDOWN_CODE_BLOCK_START +
  50 + "{\n" +
  51 + " \"type\": \"singleEntity\",\n" +
  52 + " \"singleEntity\": {\n" +
  53 + " \"id\": \"d521edb0-2a7a-11ec-94eb-213c95f54092\",\n" +
  54 + " \"entityType\": \"DEVICE\"\n" +
  55 + " }\n" +
  56 + "}"+
  57 + MARKDOWN_CODE_BLOCK_END +
  58 + "";
  59 +
  60 + private static final String ENTITY_LIST = "\n\n## Entity List Filter\n\n" +
  61 + "Allows to filter entities of the same type using their ids. For example, this entity filter selects two devices:\n\n"+
  62 + MARKDOWN_CODE_BLOCK_START +
  63 + "{\n" +
  64 + " \"type\": \"entityList\",\n" +
  65 + " \"entityType\": \"DEVICE\",\n" +
  66 + " \"entityList\": [\n" +
  67 + " \"e6501f30-2a7a-11ec-94eb-213c95f54092\",\n" +
  68 + " \"e6657bf0-2a7a-11ec-94eb-213c95f54092\"\n" +
  69 + " ]\n" +
  70 + "}"+
  71 + MARKDOWN_CODE_BLOCK_END +
  72 + "";
  73 +
  74 + private static final String ENTITY_NAME = "\n\n## Entity Name Filter\n\n" +
  75 + "Allows to filter entities of the same type using the **'starts with'** expression over entity name. " +
  76 + "For example, this entity filter selects all devices which name starts with 'Air Quality':\n\n"+
  77 + MARKDOWN_CODE_BLOCK_START +
  78 + "{\n" +
  79 + " \"type\": \"entityName\",\n" +
  80 + " \"entityType\": \"DEVICE\",\n" +
  81 + " \"entityNameFilter\": \"Air Quality\"\n" +
  82 + "}"+
  83 + MARKDOWN_CODE_BLOCK_END +
  84 + "";
  85 +
  86 + private static final String ENTITY_TYPE = "\n\n## Entity Type Filter\n\n" +
  87 + "Allows to filter entities based on their type (CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, etc)" +
  88 + "For example, this entity filter selects all tenant customers:\n\n"+
  89 + MARKDOWN_CODE_BLOCK_START +
  90 + "{\n" +
  91 + " \"type\": \"entityType\",\n" +
  92 + " \"entityType\": \"CUSTOMER\"\n" +
  93 + "}"+
  94 + MARKDOWN_CODE_BLOCK_END +
  95 + "";
  96 +
  97 + private static final String ASSET_TYPE = "\n\n## Asset Type Filter\n\n" +
  98 + "Allows to filter assets based on their type and the **'starts with'** expression over their name. " +
  99 + "For example, this entity filter selects all 'charging station' assets which name starts with 'Tesla':\n\n"+
  100 + MARKDOWN_CODE_BLOCK_START +
  101 + "{\n" +
  102 + " \"type\": \"assetType\",\n" +
  103 + " \"assetType\": \"charging station\",\n" +
  104 + " \"assetNameFilter\": \"Tesla\"\n" +
  105 + "}"+
  106 + MARKDOWN_CODE_BLOCK_END +
  107 + "";
  108 +
  109 + private static final String DEVICE_TYPE = "\n\n## Device Type Filter\n\n" +
  110 + "Allows to filter devices based on their type and the **'starts with'** expression over their name. " +
  111 + "For example, this entity filter selects all 'Temperature Sensor' devices which name starts with 'ABC':\n\n"+
  112 + MARKDOWN_CODE_BLOCK_START +
  113 + "{\n" +
  114 + " \"type\": \"deviceType\",\n" +
  115 + " \"deviceType\": \"Temperature Sensor\",\n" +
  116 + " \"deviceNameFilter\": \"ABC\"\n" +
  117 + "}"+
  118 + MARKDOWN_CODE_BLOCK_END +
  119 + "";
  120 +
  121 + private static final String EDGE_TYPE = "\n\n## Edge Type Filter\n\n" +
  122 + "Allows to filter edge instances based on their type and the **'starts with'** expression over their name. " +
  123 + "For example, this entity filter selects all 'Factory' edge instances which name starts with 'Nevada':\n\n"+
  124 + MARKDOWN_CODE_BLOCK_START +
  125 + "{\n" +
  126 + " \"type\": \"edgeType\",\n" +
  127 + " \"edgeType\": \"Factory\",\n" +
  128 + " \"edgeNameFilter\": \"Nevada\"\n" +
  129 + "}"+
  130 + MARKDOWN_CODE_BLOCK_END +
  131 + "";
  132 +
  133 + private static final String ENTITY_VIEW_TYPE = "\n\n## Entity View Filter\n\n" +
  134 + "Allows to filter entity views based on their type and the **'starts with'** expression over their name. " +
  135 + "For example, this entity filter selects all 'Concrete Mixer' entity views which name starts with 'CAT':\n\n"+
  136 + MARKDOWN_CODE_BLOCK_START +
  137 + "{\n" +
  138 + " \"type\": \"entityViewType\",\n" +
  139 + " \"entityViewType\": \"Concrete Mixer\",\n" +
  140 + " \"entityViewNameFilter\": \"CAT\"\n" +
  141 + "}"+
  142 + MARKDOWN_CODE_BLOCK_END +
  143 + "";
  144 +
  145 + private static final String API_USAGE = "\n\n## Api Usage Filter\n\n" +
  146 + "Allows to query for Api Usage based on optional customer id. If the customer id is not set, returns current tenant API usage." +
  147 + "For example, this entity filter selects the 'Api Usage' entity for customer with id 'e6501f30-2a7a-11ec-94eb-213c95f54092':\n\n"+
  148 + MARKDOWN_CODE_BLOCK_START +
  149 + "{\n" +
  150 + " \"type\": \"apiUsageState\",\n" +
  151 + " \"customerId\": {\n" +
  152 + " \"id\": \"d521edb0-2a7a-11ec-94eb-213c95f54092\",\n" +
  153 + " \"entityType\": \"CUSTOMER\"\n" +
  154 + " }\n" +
  155 + "}"+
  156 + MARKDOWN_CODE_BLOCK_END +
  157 + "";
  158 +
  159 + private static final String MAX_LEVEL_DESCRIPTION = "Possible direction values are 'TO' and 'FROM'. The 'maxLevel' defines how many relation levels should the query search 'recursively'. ";
  160 + private static final String FETCH_LAST_LEVEL_ONLY_DESCRIPTION = "Assuming the 'maxLevel' is > 1, the 'fetchLastLevelOnly' defines either to return all related entities or only entities that are on the last level of relations. ";
  161 +
  162 + private static final String RELATIONS_QUERY_FILTER = "\n\n## Relations Query Filter\n\n" +
  163 + "Allows to filter entities that are related to the provided root entity. " +
  164 + MAX_LEVEL_DESCRIPTION +
  165 + FETCH_LAST_LEVEL_ONLY_DESCRIPTION +
  166 + "The 'filter' object allows you to define the relation type and set of acceptable entity types to search for. " +
  167 + "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" +
  168 + "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"+
  169 + MARKDOWN_CODE_BLOCK_START +
  170 + "{\n" +
  171 + " \"type\": \"relationsQuery\",\n" +
  172 + " \"rootEntity\": {\n" +
  173 + " \"entityType\": \"ASSET\",\n" +
  174 + " \"id\": \"e51de0c0-2a7a-11ec-94eb-213c95f54092\"\n" +
  175 + " },\n" +
  176 + " \"direction\": \"FROM\",\n" +
  177 + " \"maxLevel\": 1,\n" +
  178 + " \"fetchLastLevelOnly\": false,\n" +
  179 + " \"filters\": [\n" +
  180 + " {\n" +
  181 + " \"relationType\": \"Contains\",\n" +
  182 + " \"entityTypes\": [\n" +
  183 + " \"DEVICE\",\n" +
  184 + " \"ASSET\"\n" +
  185 + " ]\n" +
  186 + " }\n" +
  187 + " ]\n" +
  188 + "}"+
  189 + MARKDOWN_CODE_BLOCK_END +
  190 + "";
  191 +
  192 +
  193 + private static final String ASSET_QUERY_FILTER = "\n\n## Asset Search Query\n\n" +
  194 + "Allows to filter assets that are related to the provided root entity. Filters related assets based on the relation type and set of asset types. " +
  195 + MAX_LEVEL_DESCRIPTION +
  196 + FETCH_LAST_LEVEL_ONLY_DESCRIPTION +
  197 + "The 'relationType' defines the type of the relation to search for. " +
  198 + "The 'assetTypes' defines the type of the asset to search for. " +
  199 + "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" +
  200 + "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"+
  201 + MARKDOWN_CODE_BLOCK_START +
  202 + "{\n" +
  203 + " \"type\": \"assetSearchQuery\",\n" +
  204 + " \"rootEntity\": {\n" +
  205 + " \"entityType\": \"ASSET\",\n" +
  206 + " \"id\": \"e51de0c0-2a7a-11ec-94eb-213c95f54092\"\n" +
  207 + " },\n" +
  208 + " \"direction\": \"FROM\",\n" +
  209 + " \"maxLevel\": 1,\n" +
  210 + " \"fetchLastLevelOnly\": false,\n" +
  211 + " \"relationType\": \"Contains\",\n" +
  212 + " \"assetTypes\": [\n" +
  213 + " \"charging station\"\n" +
  214 + " ]\n" +
  215 + "}"+
  216 + MARKDOWN_CODE_BLOCK_END +
  217 + "";
  218 +
  219 + private static final String DEVICE_QUERY_FILTER = "\n\n## Device Search Query\n\n" +
  220 + "Allows to filter devices that are related to the provided root entity. Filters related devices based on the relation type and set of device types. " +
  221 + MAX_LEVEL_DESCRIPTION +
  222 + FETCH_LAST_LEVEL_ONLY_DESCRIPTION +
  223 + "The 'relationType' defines the type of the relation to search for. " +
  224 + "The 'deviceTypes' defines the type of the device to search for. " +
  225 + "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" +
  226 + "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"+
  227 + MARKDOWN_CODE_BLOCK_START +
  228 + "{\n" +
  229 + " \"type\": \"deviceSearchQuery\",\n" +
  230 + " \"rootEntity\": {\n" +
  231 + " \"entityType\": \"ASSET\",\n" +
  232 + " \"id\": \"e52b0020-2a7a-11ec-94eb-213c95f54092\"\n" +
  233 + " },\n" +
  234 + " \"direction\": \"FROM\",\n" +
  235 + " \"maxLevel\": 2,\n" +
  236 + " \"fetchLastLevelOnly\": true,\n" +
  237 + " \"relationType\": \"Contains\",\n" +
  238 + " \"deviceTypes\": [\n" +
  239 + " \"Air Quality Sensor\",\n" +
  240 + " \"Charging port\"\n" +
  241 + " ]\n" +
  242 + "}"+
  243 + MARKDOWN_CODE_BLOCK_END +
  244 + "";
  245 +
  246 + private static final String EV_QUERY_FILTER = "\n\n## Entity View Query\n\n" +
  247 + "Allows to filter entity views that are related to the provided root entity. Filters related entity views based on the relation type and set of entity view types. " +
  248 + MAX_LEVEL_DESCRIPTION +
  249 + FETCH_LAST_LEVEL_ONLY_DESCRIPTION +
  250 + "The 'relationType' defines the type of the relation to search for. " +
  251 + "The 'entityViewTypes' defines the type of the entity view to search for. " +
  252 + "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" +
  253 + "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"+
  254 + MARKDOWN_CODE_BLOCK_START +
  255 + "{\n" +
  256 + " \"type\": \"entityViewSearchQuery\",\n" +
  257 + " \"rootEntity\": {\n" +
  258 + " \"entityType\": \"ASSET\",\n" +
  259 + " \"id\": \"e52b0020-2a7a-11ec-94eb-213c95f54092\"\n" +
  260 + " },\n" +
  261 + " \"direction\": \"FROM\",\n" +
  262 + " \"maxLevel\": 1,\n" +
  263 + " \"fetchLastLevelOnly\": false,\n" +
  264 + " \"relationType\": \"Contains\",\n" +
  265 + " \"entityViewTypes\": [\n" +
  266 + " \"Concrete mixer\"\n" +
  267 + " ]\n" +
  268 + "}"+
  269 + MARKDOWN_CODE_BLOCK_END +
  270 + "";
  271 +
  272 + private static final String EDGE_QUERY_FILTER = "\n\n## Edge Search Query\n\n" +
  273 + "Allows to filter edge instances that are related to the provided root entity. Filters related edge instances based on the relation type and set of edge types. " +
  274 + MAX_LEVEL_DESCRIPTION +
  275 + FETCH_LAST_LEVEL_ONLY_DESCRIPTION +
  276 + "The 'relationType' defines the type of the relation to search for. " +
  277 + "The 'deviceTypes' defines the type of the device to search for. " +
  278 + "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" +
  279 + "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"+
  280 + MARKDOWN_CODE_BLOCK_START +
  281 + "{\n" +
  282 + " \"type\": \"deviceSearchQuery\",\n" +
  283 + " \"rootEntity\": {\n" +
  284 + " \"entityType\": \"ASSET\",\n" +
  285 + " \"id\": \"e52b0020-2a7a-11ec-94eb-213c95f54092\"\n" +
  286 + " },\n" +
  287 + " \"direction\": \"FROM\",\n" +
  288 + " \"maxLevel\": 2,\n" +
  289 + " \"fetchLastLevelOnly\": true,\n" +
  290 + " \"relationType\": \"Contains\",\n" +
  291 + " \"edgeTypes\": [\n" +
  292 + " \"Factory\"\n" +
  293 + " ]\n" +
  294 + "}"+
  295 + MARKDOWN_CODE_BLOCK_END +
  296 + "";
  297 +
  298 + private static final String EMPTY = "\n\n## Entity Type Filter\n\n" +
  299 + "Allows to filter multiple entities of the same type using the **'starts with'** expression over entity name. " +
  300 + "For example, this entity filter selects all devices which name starts with 'Air Quality':\n\n"+
  301 + MARKDOWN_CODE_BLOCK_START +
  302 + ""+
  303 + MARKDOWN_CODE_BLOCK_END +
  304 + "";
  305 +
  306 + private static final String ENTITY_FILTERS =
  307 + "\n\n # Entity Filters" +
  308 + "\nEntity Filter body depends on the 'type' parameter. Let's review available entity filter types. In fact, they do correspond to available dashboard aliases." +
  309 + SINGLE_ENTITY + ENTITY_LIST + ENTITY_NAME + ENTITY_TYPE + ASSET_TYPE + DEVICE_TYPE + EDGE_TYPE + ENTITY_VIEW_TYPE + API_USAGE + RELATIONS_QUERY_FILTER
  310 + + ASSET_QUERY_FILTER + DEVICE_QUERY_FILTER + EV_QUERY_FILTER + EDGE_QUERY_FILTER;
  311 +
  312 + private static final String FILTER_KEY = "\n\n## Filter Key\n\n" +
  313 + "Filter Key defines either entity field, attribute or telemetry. It is a JSON object that consists the key name and type. " +
  314 + "The following filter key types are supported: \n\n"+
  315 + " * 'CLIENT_ATTRIBUTE' - used for client attributes; \n" +
  316 + " * 'SHARED_ATTRIBUTE' - used for shared attributes; \n" +
  317 + " * 'SERVER_ATTRIBUTE' - used for server attributes; \n" +
  318 + " * 'ATTRIBUTE' - used for any of the above; \n" +
  319 + " * 'TIME_SERIES' - used for time-series values; \n" +
  320 + " * 'ENTITY_FIELD' - used for accessing entity fields like 'name', 'label', etc. The list of available fields depends on the entity type; \n" +
  321 + " * 'ALARM_FIELD' - similar to entity field, but is used in alarm queries only; \n" +
  322 + "\n\n Let's review the example:\n\n" +
  323 + MARKDOWN_CODE_BLOCK_START +
  324 + "{\n" +
  325 + " \"type\": \"TIME_SERIES\",\n" +
  326 + " \"key\": \"temperature\"\n" +
  327 + "}" +
  328 + MARKDOWN_CODE_BLOCK_END +
  329 + "";
  330 +
  331 + private static final String FILTER_VALUE_TYPE = "\n\n## Value Type and Operations\n\n" +
  332 + "Provides a hint about the data type of the entity field that is defined in the filter key. " +
  333 + "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." +
  334 + "The following filter value types and corresponding predicate operations are supported: \n\n"+
  335 + " * 'STRING' - used to filter any 'String' or 'JSON' values. Operations: EQUAL, NOT_EQUAL, STARTS_WITH, ENDS_WITH, CONTAINS, NOT_CONTAINS; \n" +
  336 + " * 'NUMERIC' - used for 'Long' and 'Double' values. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n" +
  337 + " * 'BOOLEAN' - used for boolean values; Operations: EQUAL, NOT_EQUAL \n" +
  338 + " * 'DATE_TIME' - similar to numeric, transforms value to milliseconds since epoch. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n";
  339 +
  340 + private static final String FILTER_PREDICATE = "\n\n## Filter Predicate\n\n" +
  341 + "Filter Predicate defines the logical expression to evaluate. The list of available operations depends on the filter value type, see above. " +
  342 + "Platform supports 4 predicate types: 'STRING', 'NUMERIC', 'BOOLEAN' and 'COMPLEX'. The last one allows to combine multiple operations over one filter key." +
  343 + "\n\nSimple predicate example to check 'value < 100': \n\n"+
  344 + MARKDOWN_CODE_BLOCK_START +
  345 + "{\n" +
  346 + " \"operation\": \"LESS\",\n" +
  347 + " \"value\": {\n" +
  348 + " \"defaultValue\": 100,\n" +
  349 + " \"dynamicValue\": null\n" +
  350 + " },\n" +
  351 + " \"type\": \"NUMERIC\"\n" +
  352 + "}" +
  353 + MARKDOWN_CODE_BLOCK_END +
  354 + "\n\nComplex predicate example, to check 'value < 10 or value > 20': \n\n"+
  355 + MARKDOWN_CODE_BLOCK_START +
  356 + "{\n" +
  357 + " \"type\": \"COMPLEX\",\n" +
  358 + " \"operation\": \"OR\",\n" +
  359 + " \"predicates\": [\n" +
  360 + " {\n" +
  361 + " \"operation\": \"LESS\",\n" +
  362 + " \"value\": {\n" +
  363 + " \"defaultValue\": 10,\n" +
  364 + " \"dynamicValue\": null\n" +
  365 + " },\n" +
  366 + " \"type\": \"NUMERIC\"\n" +
  367 + " },\n" +
  368 + " {\n" +
  369 + " \"operation\": \"GREATER\",\n" +
  370 + " \"value\": {\n" +
  371 + " \"defaultValue\": 20,\n" +
  372 + " \"dynamicValue\": null\n" +
  373 + " },\n" +
  374 + " \"type\": \"NUMERIC\"\n" +
  375 + " }\n" +
  376 + " ]\n" +
  377 + "}" +
  378 + MARKDOWN_CODE_BLOCK_END +
  379 + "\n\nMore complex predicate example, to check 'value < 10 or (value > 50 && value < 60)': \n\n"+
  380 + MARKDOWN_CODE_BLOCK_START +
  381 + "{\n" +
  382 + " \"type\": \"COMPLEX\",\n" +
  383 + " \"operation\": \"OR\",\n" +
  384 + " \"predicates\": [\n" +
  385 + " {\n" +
  386 + " \"operation\": \"LESS\",\n" +
  387 + " \"value\": {\n" +
  388 + " \"defaultValue\": 10,\n" +
  389 + " \"dynamicValue\": null\n" +
  390 + " },\n" +
  391 + " \"type\": \"NUMERIC\"\n" +
  392 + " },\n" +
  393 + " {\n" +
  394 + " \"type\": \"COMPLEX\",\n" +
  395 + " \"operation\": \"AND\",\n" +
  396 + " \"predicates\": [\n" +
  397 + " {\n" +
  398 + " \"operation\": \"GREATER\",\n" +
  399 + " \"value\": {\n" +
  400 + " \"defaultValue\": 50,\n" +
  401 + " \"dynamicValue\": null\n" +
  402 + " },\n" +
  403 + " \"type\": \"NUMERIC\"\n" +
  404 + " },\n" +
  405 + " {\n" +
  406 + " \"operation\": \"LESS\",\n" +
  407 + " \"value\": {\n" +
  408 + " \"defaultValue\": 60,\n" +
  409 + " \"dynamicValue\": null\n" +
  410 + " },\n" +
  411 + " \"type\": \"NUMERIC\"\n" +
  412 + " }\n" +
  413 + " ]\n" +
  414 + " }\n" +
  415 + " ]\n" +
  416 + "}" +
  417 + MARKDOWN_CODE_BLOCK_END +
  418 + "\n\n You may also want to replace hardcoded values (for example, temperature > 20) with the more dynamic " +
  419 + "expression (for example, temperature > 'value of the tenant attribute with key 'temperatureThreshold'). " +
  420 + "It is possible to use 'dynamicValue' to define attribute of the tenant, customer or user that is performing the API call. " +
  421 + "See example below: \n\n" +
  422 + MARKDOWN_CODE_BLOCK_START +
  423 + "{\n" +
  424 + " \"operation\": \"GREATER\",\n" +
  425 + " \"value\": {\n" +
  426 + " \"defaultValue\": 0,\n" +
  427 + " \"dynamicValue\": {\n" +
  428 + " \"sourceType\": \"CURRENT_USER\",\n" +
  429 + " \"sourceAttribute\": \"temperatureThreshold\"\n" +
  430 + " }\n" +
  431 + " },\n" +
  432 + " \"type\": \"NUMERIC\"\n" +
  433 + "}" +
  434 + MARKDOWN_CODE_BLOCK_END +
  435 + "\n\n Note that you may use 'CURRENT_USER', '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.";
  436 +
  437 + private static final String KEY_FILTERS =
  438 + "\n\n # Key Filters" +
  439 + "\nKey Filter allows you to define complex logical expressions over entity field, attribute or latest time-series value. The filter is defined using 'key', 'valueType' and 'predicate' objects. " +
  440 + "Single Entity Query may have zero, one or multiple predicates. If multiple filters are defined, they are evaluated using logical 'AND'. " +
  441 + "The example below checks that temperature of the entity is above 20 degrees:" +
  442 + "\n\n" + MARKDOWN_CODE_BLOCK_START +
  443 + "{\n" +
  444 + " \"key\": {\n" +
  445 + " \"type\": \"TIME_SERIES\",\n" +
  446 + " \"key\": \"temperature\"\n" +
  447 + " },\n" +
  448 + " \"valueType\": \"NUMERIC\",\n" +
  449 + " \"predicate\": {\n" +
  450 + " \"operation\": \"GREATER\",\n" +
  451 + " \"value\": {\n" +
  452 + " \"defaultValue\": 20,\n" +
  453 + " \"dynamicValue\": null\n" +
  454 + " },\n" +
  455 + " \"type\": \"NUMERIC\"\n" +
  456 + " }\n" +
  457 + "}" +
  458 + MARKDOWN_CODE_BLOCK_END +
  459 + "\n\n Now let's review 'key', 'valueType' and 'predicate' objects in detail."
  460 + + FILTER_KEY + FILTER_VALUE_TYPE + FILTER_PREDICATE;
  461 +
  462 + private static final String ENTITY_COUNT_QUERY_DESCRIPTION =
  463 + "Allows to run complex queries to search the count of platform entities (devices, assets, customers, etc) " +
  464 + "based on the combination of main entity filter and multiple key filters. Returns the number of entities that match the query definition.\n\n" +
  465 + "# Query Definition\n\n" +
  466 + "\n\nMain **entity filter** is mandatory and defines generic search criteria. " +
  467 + "For example, \"find all devices with profile 'Moisture Sensor'\" or \"Find all devices related to asset 'Building A'\"" +
  468 + "\n\nOptional **key filters** allow to filter results of the entity filter by complex criteria against " +
  469 + "main entity fields (name, label, type, etc), attributes and telemetry. " +
  470 + "For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"."+
  471 + "\n\nLet's review the example:" +
  472 + "\n\n" + MARKDOWN_CODE_BLOCK_START +
  473 + "{\n" +
  474 + " \"entityFilter\": {\n" +
  475 + " \"type\": \"entityType\",\n" +
  476 + " \"entityType\": \"DEVICE\"\n" +
  477 + " },\n" +
  478 + " \"keyFilters\": [\n" +
  479 + " {\n" +
  480 + " \"key\": {\n" +
  481 + " \"type\": \"ATTRIBUTE\",\n" +
  482 + " \"key\": \"active\"\n" +
  483 + " },\n" +
  484 + " \"valueType\": \"BOOLEAN\",\n" +
  485 + " \"predicate\": {\n" +
  486 + " \"operation\": \"EQUAL\",\n" +
  487 + " \"value\": {\n" +
  488 + " \"defaultValue\": true,\n" +
  489 + " \"dynamicValue\": null\n" +
  490 + " },\n" +
  491 + " \"type\": \"BOOLEAN\"\n" +
  492 + " }\n" +
  493 + " }\n" +
  494 + " ]\n" +
  495 + "}"+
  496 + MARKDOWN_CODE_BLOCK_END +
  497 + "\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:" +
  498 + ENTITY_FILTERS +
  499 + KEY_FILTERS +
  500 + TENANT_OR_USER_AUTHORITY_PARAGRAPH;;
  501 +
  502 + private static final String ENTITY_DATA_QUERY_DESCRIPTION =
  503 + "Allows to run complex queries over platform entities (devices, assets, customers, etc) " +
  504 + "based on the combination of main entity filter and multiple key filters. " +
  505 + "Returns the paginated result of the query that contains requested entity fields and latest values of requested attributes and time-series data.\n\n" +
  506 + "# Query Definition\n\n" +
  507 + "\n\nMain **entity filter** is mandatory and defines generic search criteria. " +
  508 + "For example, \"find all devices with profile 'Moisture Sensor'\" or \"Find all devices related to asset 'Building A'\"" +
  509 + "\n\nOptional **key filters** allow to filter results of the **entity filter** by complex criteria against " +
  510 + "main entity fields (name, label, type, etc), attributes and telemetry. " +
  511 + "For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"."+
  512 + "\n\nThe **entity fields** and **latest values** contains list of entity fields and latest attribute/telemetry fields to fetch for each entity." +
  513 + "\n\nThe **page link** contains information about the page to fetch and the sort ordering." +
  514 + "\n\nLet's review the example:" +
  515 + "\n\n" + MARKDOWN_CODE_BLOCK_START +
  516 + "{\n" +
  517 + " \"entityFilter\": {\n" +
  518 + " \"type\": \"entityType\",\n" +
  519 + " \"resolveMultiple\": true,\n" +
  520 + " \"entityType\": \"DEVICE\"\n" +
  521 + " },\n" +
  522 + " \"keyFilters\": [\n" +
  523 + " {\n" +
  524 + " \"key\": {\n" +
  525 + " \"type\": \"TIME_SERIES\",\n" +
  526 + " \"key\": \"temperature\"\n" +
  527 + " },\n" +
  528 + " \"valueType\": \"NUMERIC\",\n" +
  529 + " \"predicate\": {\n" +
  530 + " \"operation\": \"GREATER\",\n" +
  531 + " \"value\": {\n" +
  532 + " \"defaultValue\": 0,\n" +
  533 + " \"dynamicValue\": {\n" +
  534 + " \"sourceType\": \"CURRENT_USER\",\n" +
  535 + " \"sourceAttribute\": \"temperatureThreshold\",\n" +
  536 + " \"inherit\": false\n" +
  537 + " }\n" +
  538 + " },\n" +
  539 + " \"type\": \"NUMERIC\"\n" +
  540 + " }\n" +
  541 + " }\n" +
  542 + " ],\n" +
  543 + " \"entityFields\": [\n" +
  544 + " {\n" +
  545 + " \"type\": \"ENTITY_FIELD\",\n" +
  546 + " \"key\": \"name\"\n" +
  547 + " },\n" +
  548 + " {\n" +
  549 + " \"type\": \"ENTITY_FIELD\",\n" +
  550 + " \"key\": \"label\"\n" +
  551 + " },\n" +
  552 + " {\n" +
  553 + " \"type\": \"ENTITY_FIELD\",\n" +
  554 + " \"key\": \"additionalInfo\"\n" +
  555 + " }\n" +
  556 + " ],\n" +
  557 + " \"latestValues\": [\n" +
  558 + " {\n" +
  559 + " \"type\": \"ATTRIBUTE\",\n" +
  560 + " \"key\": \"model\"\n" +
  561 + " },\n" +
  562 + " {\n" +
  563 + " \"type\": \"TIME_SERIES\",\n" +
  564 + " \"key\": \"temperature\"\n" +
  565 + " }\n" +
  566 + " ],\n" +
  567 + " \"pageLink\": {\n" +
  568 + " \"page\": 0,\n" +
  569 + " \"pageSize\": 10,\n" +
  570 + " \"sortOrder\": {\n" +
  571 + " \"key\": {\n" +
  572 + " \"key\": \"name\",\n" +
  573 + " \"type\": \"ENTITY_FIELD\"\n" +
  574 + " },\n" +
  575 + " \"direction\": \"ASC\"\n" +
  576 + " }\n" +
  577 + " }\n" +
  578 + "}"+
  579 + MARKDOWN_CODE_BLOCK_END +
  580 + "\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:" +
  581 + ENTITY_FILTERS +
  582 + KEY_FILTERS +
  583 + TENANT_OR_USER_AUTHORITY_PARAGRAPH;
  584 +
  585 +
  586 + private static final String ALARM_DATA_QUERY_DESCRIPTION = "This method description defines how Alarm Data Query extends the Entity Data Query. " +
  587 + "See method 'Find Entity Data by Query' first to get the info about 'Entity Data Query'." +
  588 + "\n\n The platform will first search the entities that match the entity and key filters. Then, the platform will use 'Alarm Page Link' to filter the alarms related to those entities. " +
  589 + "Finally, platform fetch the properties of alarm that are defined in the **'alarmFields'** and combine them with the other entity, attribute and latest time-series fields to return the result. " +
  590 + "\n\n See example of the alarm query below. The query will search first 100 active alarms with type 'Temperature Alarm' or 'Fire Alarm' for any device with current temperature > 0. " +
  591 + "The query will return combination of the entity fields: name of the device, device model and latest temperature reading and alarms fields: createdTime, type, severity and status: " +
  592 + "\n\n" + MARKDOWN_CODE_BLOCK_START +
  593 + "{\n" +
  594 + " \"entityFilter\": {\n" +
  595 + " \"type\": \"entityType\",\n" +
  596 + " \"resolveMultiple\": true,\n" +
  597 + " \"entityType\": \"DEVICE\"\n" +
  598 + " },\n" +
  599 + " \"pageLink\": {\n" +
  600 + " \"page\": 0,\n" +
  601 + " \"pageSize\": 100,\n" +
  602 + " \"textSearch\": null,\n" +
  603 + " \"searchPropagatedAlarms\": false,\n" +
  604 + " \"statusList\": [\n" +
  605 + " \"ACTIVE\"\n" +
  606 + " ],\n" +
  607 + " \"severityList\": [\n" +
  608 + " \"CRITICAL\",\n" +
  609 + " \"MAJOR\"\n" +
  610 + " ],\n" +
  611 + " \"typeList\": [\n" +
  612 + " \"Temperature Alarm\",\n" +
  613 + " \"Fire Alarm\"\n" +
  614 + " ],\n" +
  615 + " \"sortOrder\": {\n" +
  616 + " \"key\": {\n" +
  617 + " \"key\": \"createdTime\",\n" +
  618 + " \"type\": \"ALARM_FIELD\"\n" +
  619 + " },\n" +
  620 + " \"direction\": \"DESC\"\n" +
  621 + " },\n" +
  622 + " \"timeWindow\": 86400000\n" +
  623 + " },\n" +
  624 + " \"keyFilters\": [\n" +
  625 + " {\n" +
  626 + " \"key\": {\n" +
  627 + " \"type\": \"TIME_SERIES\",\n" +
  628 + " \"key\": \"temperature\"\n" +
  629 + " },\n" +
  630 + " \"valueType\": \"NUMERIC\",\n" +
  631 + " \"predicate\": {\n" +
  632 + " \"operation\": \"GREATER\",\n" +
  633 + " \"value\": {\n" +
  634 + " \"defaultValue\": 0,\n" +
  635 + " \"dynamicValue\": null\n" +
  636 + " },\n" +
  637 + " \"type\": \"NUMERIC\"\n" +
  638 + " }\n" +
  639 + " }\n" +
  640 + " ],\n" +
  641 + " \"alarmFields\": [\n" +
  642 + " {\n" +
  643 + " \"type\": \"ALARM_FIELD\",\n" +
  644 + " \"key\": \"createdTime\"\n" +
  645 + " },\n" +
  646 + " {\n" +
  647 + " \"type\": \"ALARM_FIELD\",\n" +
  648 + " \"key\": \"type\"\n" +
  649 + " },\n" +
  650 + " {\n" +
  651 + " \"type\": \"ALARM_FIELD\",\n" +
  652 + " \"key\": \"severity\"\n" +
  653 + " },\n" +
  654 + " {\n" +
  655 + " \"type\": \"ALARM_FIELD\",\n" +
  656 + " \"key\": \"status\"\n" +
  657 + " }\n" +
  658 + " ],\n" +
  659 + " \"entityFields\": [\n" +
  660 + " {\n" +
  661 + " \"type\": \"ENTITY_FIELD\",\n" +
  662 + " \"key\": \"name\"\n" +
  663 + " }\n" +
  664 + " ],\n" +
  665 + " \"latestValues\": [\n" +
  666 + " {\n" +
  667 + " \"type\": \"ATTRIBUTE\",\n" +
  668 + " \"key\": \"model\"\n" +
  669 + " },\n" +
  670 + " {\n" +
  671 + " \"type\": \"TIME_SERIES\",\n" +
  672 + " \"key\": \"temperature\"\n" +
  673 + " }\n" +
  674 + " ]\n" +
  675 + "}"+
  676 + MARKDOWN_CODE_BLOCK_END +
  677 + "";
  678 +
45 @Autowired 679 @Autowired
46 private EntityQueryService entityQueryService; 680 private EntityQueryService entityQueryService;
47 681
48 private static final int MAX_PAGE_SIZE = 100; 682 private static final int MAX_PAGE_SIZE = 100;
49 683
  684 + @ApiOperation(value = "Count Entities by Query", notes = ENTITY_COUNT_QUERY_DESCRIPTION)
50 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 685 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
51 @RequestMapping(value = "/entitiesQuery/count", method = RequestMethod.POST) 686 @RequestMapping(value = "/entitiesQuery/count", method = RequestMethod.POST)
52 @ResponseBody 687 @ResponseBody
53 - public long countEntitiesByQuery(@RequestBody EntityCountQuery query) throws ThingsboardException { 688 + public long countEntitiesByQuery(
  689 + @ApiParam(value = "A JSON value representing the entity count query. See API call notes above for more details.")
  690 + @RequestBody EntityCountQuery query) throws ThingsboardException {
54 checkNotNull(query); 691 checkNotNull(query);
55 try { 692 try {
56 return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query); 693 return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query);
@@ -59,10 +696,13 @@ public class EntityQueryController extends BaseController { @@ -59,10 +696,13 @@ public class EntityQueryController extends BaseController {
59 } 696 }
60 } 697 }
61 698
  699 + @ApiOperation(value = "Find Entity Data by Query", notes = ENTITY_DATA_QUERY_DESCRIPTION)
62 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 700 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
63 @RequestMapping(value = "/entitiesQuery/find", method = RequestMethod.POST) 701 @RequestMapping(value = "/entitiesQuery/find", method = RequestMethod.POST)
64 @ResponseBody 702 @ResponseBody
65 - public PageData<EntityData> findEntityDataByQuery(@RequestBody EntityDataQuery query) throws ThingsboardException { 703 + public PageData<EntityData> findEntityDataByQuery(
  704 + @ApiParam(value = "A JSON value representing the entity data query. See API call notes above for more details.")
  705 + @RequestBody EntityDataQuery query) throws ThingsboardException {
66 checkNotNull(query); 706 checkNotNull(query);
67 try { 707 try {
68 return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query); 708 return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query);
@@ -71,10 +711,13 @@ public class EntityQueryController extends BaseController { @@ -71,10 +711,13 @@ public class EntityQueryController extends BaseController {
71 } 711 }
72 } 712 }
73 713
  714 + @ApiOperation(value = "Find Alarms by Query", notes = ALARM_DATA_QUERY_DESCRIPTION)
74 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 715 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
75 @RequestMapping(value = "/alarmsQuery/find", method = RequestMethod.POST) 716 @RequestMapping(value = "/alarmsQuery/find", method = RequestMethod.POST)
76 @ResponseBody 717 @ResponseBody
77 - public PageData<AlarmData> findAlarmDataByQuery(@RequestBody AlarmDataQuery query) throws ThingsboardException { 718 + public PageData<AlarmData> findAlarmDataByQuery(
  719 + @ApiParam(value = "A JSON value representing the alarm data query. See API call notes above for more details.")
  720 + @RequestBody AlarmDataQuery query) throws ThingsboardException {
78 checkNotNull(query); 721 checkNotNull(query);
79 try { 722 try {
80 return this.entityQueryService.findAlarmDataByQuery(getCurrentUser(), query); 723 return this.entityQueryService.findAlarmDataByQuery(getCurrentUser(), query);
@@ -83,12 +726,18 @@ public class EntityQueryController extends BaseController { @@ -83,12 +726,18 @@ public class EntityQueryController extends BaseController {
83 } 726 }
84 } 727 }
85 728
  729 + @ApiOperation(value = "Find Entity Keys by Query",
  730 + notes = "Uses entity data query (see 'Find Entity Data by Query') to find first 100 entities. Then fetch and return all unique time-series and/or attribute keys. Used mostly for UI hints.")
86 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 731 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
87 @RequestMapping(value = "/entitiesQuery/find/keys", method = RequestMethod.POST) 732 @RequestMapping(value = "/entitiesQuery/find/keys", method = RequestMethod.POST)
88 @ResponseBody 733 @ResponseBody
89 - public DeferredResult<ResponseEntity> findEntityTimeseriesAndAttributesKeysByQuery(@RequestBody EntityDataQuery query,  
90 - @RequestParam("timeseries") boolean isTimeseries,  
91 - @RequestParam("attributes") boolean isAttributes) throws ThingsboardException { 734 + public DeferredResult<ResponseEntity> findEntityTimeseriesAndAttributesKeysByQuery(
  735 + @ApiParam(value = "A JSON value representing the entity data query. See API call notes above for more details.")
  736 + @RequestBody EntityDataQuery query,
  737 + @ApiParam(value = "Include all unique time-series keys to the result.")
  738 + @RequestParam("timeseries") boolean isTimeseries,
  739 + @ApiParam(value = "Include all unique attribute keys to the result.")
  740 + @RequestParam("attributes") boolean isAttributes) throws ThingsboardException {
92 TenantId tenantId = getTenantId(); 741 TenantId tenantId = getTenantId();
93 checkNotNull(query); 742 checkNotNull(query);
94 try { 743 try {
@@ -15,7 +15,10 @@ @@ -15,7 +15,10 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import org.springframework.http.HttpStatus; 20 import org.springframework.http.HttpStatus;
  21 +import org.springframework.http.MediaType;
19 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.web.bind.annotation.RequestBody; 23 import org.springframework.web.bind.annotation.RequestBody;
21 import org.springframework.web.bind.annotation.RequestMapping; 24 import org.springframework.web.bind.annotation.RequestMapping;
@@ -52,10 +55,23 @@ public class EntityRelationController extends BaseController { @@ -52,10 +55,23 @@ public class EntityRelationController extends BaseController {
52 public static final String RELATION_TYPE = "relationType"; 55 public static final String RELATION_TYPE = "relationType";
53 public static final String TO_ID = "toId"; 56 public static final String TO_ID = "toId";
54 57
  58 + private static final String SECURITY_CHECKS_ENTITIES_DESCRIPTION = "\n\nIf the user has the authority of 'System Administrator', the server checks that 'from' and 'to' entities are owned by the sysadmin. " +
  59 + "If the user has the authority of 'Tenant Administrator', the server checks that 'from' and 'to' entities are owned by the same tenant. " +
  60 + "If the user has the authority of 'Customer User', the server checks that the 'from' and 'to' entities are assigned to the same customer.";
  61 +
  62 + private static final String SECURITY_CHECKS_ENTITY_DESCRIPTION = "\n\nIf the user has the authority of 'System Administrator', the server checks that the entity is owned by the sysadmin. " +
  63 + "If the user has the authority of 'Tenant Administrator', the server checks that the entity is owned by the same tenant. " +
  64 + "If the user has the authority of 'Customer User', the server checks that the entity is assigned to the same customer.";
  65 +
  66 + @ApiOperation(value = "Create Relation (saveRelation)",
  67 + notes = "Creates or updates a relation between two entities in the platform. " +
  68 + "Relations unique key is a combination of from/to entity id and relation type group and relation type. " +
  69 + SECURITY_CHECKS_ENTITIES_DESCRIPTION)
55 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 70 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
56 @RequestMapping(value = "/relation", method = RequestMethod.POST) 71 @RequestMapping(value = "/relation", method = RequestMethod.POST)
57 @ResponseStatus(value = HttpStatus.OK) 72 @ResponseStatus(value = HttpStatus.OK)
58 - public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException { 73 + public void saveRelation(@ApiParam(value = "A JSON value representing the relation.", required = true)
  74 + @RequestBody EntityRelation relation) throws ThingsboardException {
59 try { 75 try {
60 checkNotNull(relation); 76 checkNotNull(relation);
61 checkEntityId(relation.getFrom(), Operation.WRITE); 77 checkEntityId(relation.getFrom(), Operation.WRITE);
@@ -80,14 +96,17 @@ public class EntityRelationController extends BaseController { @@ -80,14 +96,17 @@ public class EntityRelationController extends BaseController {
80 } 96 }
81 } 97 }
82 98
  99 + @ApiOperation(value = "Delete Relation (deleteRelation)",
  100 + notes = "Deletes a relation between two entities in the platform. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION)
83 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 101 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
84 @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) 102 @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE})
85 @ResponseStatus(value = HttpStatus.OK) 103 @ResponseStatus(value = HttpStatus.OK)
86 - public void deleteRelation(@RequestParam(FROM_ID) String strFromId,  
87 - @RequestParam(FROM_TYPE) String strFromType,  
88 - @RequestParam(RELATION_TYPE) String strRelationType,  
89 - @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,  
90 - @RequestParam(TO_ID) String strToId, @RequestParam(TO_TYPE) String strToType) throws ThingsboardException { 104 + public void deleteRelation(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
  105 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
  106 + @ApiParam(value = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
  107 + @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
  108 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
  109 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException {
91 checkParameter(FROM_ID, strFromId); 110 checkParameter(FROM_ID, strFromId);
92 checkParameter(FROM_TYPE, strFromType); 111 checkParameter(FROM_TYPE, strFromType);
93 checkParameter(RELATION_TYPE, strRelationType); 112 checkParameter(RELATION_TYPE, strRelationType);
@@ -119,11 +138,14 @@ public class EntityRelationController extends BaseController { @@ -119,11 +138,14 @@ public class EntityRelationController extends BaseController {
119 } 138 }
120 } 139 }
121 140
  141 + @ApiOperation(value = "Delete Relations (deleteRelations)",
  142 + notes = "Deletes all the relation (both 'from' and 'to' direction) for the specified entity. " +
  143 + SECURITY_CHECKS_ENTITY_DESCRIPTION)
122 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") 144 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')")
123 - @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"}) 145 + @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"entityId", "entityType"})
124 @ResponseStatus(value = HttpStatus.OK) 146 @ResponseStatus(value = HttpStatus.OK)
125 - public void deleteRelations(@RequestParam("entityId") String strId,  
126 - @RequestParam("entityType") String strType) throws ThingsboardException { 147 + public void deleteRelations(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam("entityId") String strId,
  148 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam("entityType") String strType) throws ThingsboardException {
127 checkParameter("entityId", strId); 149 checkParameter("entityId", strId);
128 checkParameter("entityType", strType); 150 checkParameter("entityType", strType);
129 EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId); 151 EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
@@ -137,14 +159,18 @@ public class EntityRelationController extends BaseController { @@ -137,14 +159,18 @@ public class EntityRelationController extends BaseController {
137 } 159 }
138 } 160 }
139 161
  162 + @ApiOperation(value = "Get Relation (getRelation)",
  163 + notes = "Returns relation object between two specified entities if present. Otherwise throws exception. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION,
  164 + produces = MediaType.APPLICATION_JSON_VALUE)
140 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 165 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
141 @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) 166 @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE})
142 @ResponseBody 167 @ResponseBody
143 - public EntityRelation getRelation(@RequestParam(FROM_ID) String strFromId,  
144 - @RequestParam(FROM_TYPE) String strFromType,  
145 - @RequestParam(RELATION_TYPE) String strRelationType,  
146 - @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,  
147 - @RequestParam(TO_ID) String strToId, @RequestParam(TO_TYPE) String strToType) throws ThingsboardException { 168 + public EntityRelation getRelation(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
  169 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
  170 + @ApiParam(value = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
  171 + @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
  172 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
  173 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException {
148 try { 174 try {
149 checkParameter(FROM_ID, strFromId); 175 checkParameter(FROM_ID, strFromId);
150 checkParameter(FROM_TYPE, strFromType); 176 checkParameter(FROM_TYPE, strFromType);
@@ -162,11 +188,16 @@ public class EntityRelationController extends BaseController { @@ -162,11 +188,16 @@ public class EntityRelationController extends BaseController {
162 } 188 }
163 } 189 }
164 190
  191 + @ApiOperation(value = "Get List of Relations (findByFrom)",
  192 + notes = "Returns list of relation objects for the specified entity by the 'from' direction. " +
  193 + SECURITY_CHECKS_ENTITY_DESCRIPTION,
  194 + produces = MediaType.APPLICATION_JSON_VALUE)
165 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 195 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
166 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) 196 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE})
167 @ResponseBody 197 @ResponseBody
168 - public List<EntityRelation> findByFrom(@RequestParam(FROM_ID) String strFromId,  
169 - @RequestParam(FROM_TYPE) String strFromType, 198 + public List<EntityRelation> findByFrom(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
  199 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
  200 + @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
170 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { 201 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
171 checkParameter(FROM_ID, strFromId); 202 checkParameter(FROM_ID, strFromId);
172 checkParameter(FROM_TYPE, strFromType); 203 checkParameter(FROM_TYPE, strFromType);
@@ -180,11 +211,16 @@ public class EntityRelationController extends BaseController { @@ -180,11 +211,16 @@ public class EntityRelationController extends BaseController {
180 } 211 }
181 } 212 }
182 213
  214 + @ApiOperation(value = "Get List of Relation Infos (findInfoByFrom)",
  215 + notes = "Returns list of relation info objects for the specified entity by the 'from' direction. " +
  216 + SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION,
  217 + produces = MediaType.APPLICATION_JSON_VALUE)
183 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 218 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
184 @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) 219 @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE})
185 @ResponseBody 220 @ResponseBody
186 - public List<EntityRelationInfo> findInfoByFrom(@RequestParam(FROM_ID) String strFromId,  
187 - @RequestParam(FROM_TYPE) String strFromType, 221 + public List<EntityRelationInfo> findInfoByFrom(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
  222 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
  223 + @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
188 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { 224 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
189 checkParameter(FROM_ID, strFromId); 225 checkParameter(FROM_ID, strFromId);
190 checkParameter(FROM_TYPE, strFromType); 226 checkParameter(FROM_TYPE, strFromType);
@@ -198,12 +234,17 @@ public class EntityRelationController extends BaseController { @@ -198,12 +234,17 @@ public class EntityRelationController extends BaseController {
198 } 234 }
199 } 235 }
200 236
  237 + @ApiOperation(value = "Get List of Relations (findByFrom)",
  238 + notes = "Returns list of relation objects for the specified entity by the 'from' direction and relation type. " +
  239 + SECURITY_CHECKS_ENTITY_DESCRIPTION,
  240 + produces = MediaType.APPLICATION_JSON_VALUE)
201 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 241 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
202 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE}) 242 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE})
203 @ResponseBody 243 @ResponseBody
204 - public List<EntityRelation> findByFrom(@RequestParam(FROM_ID) String strFromId,  
205 - @RequestParam(FROM_TYPE) String strFromType,  
206 - @RequestParam(RELATION_TYPE) String strRelationType, 244 + public List<EntityRelation> findByFrom(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
  245 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
  246 + @ApiParam(value = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
  247 + @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
207 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { 248 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
208 checkParameter(FROM_ID, strFromId); 249 checkParameter(FROM_ID, strFromId);
209 checkParameter(FROM_TYPE, strFromType); 250 checkParameter(FROM_TYPE, strFromType);
@@ -218,11 +259,16 @@ public class EntityRelationController extends BaseController { @@ -218,11 +259,16 @@ public class EntityRelationController extends BaseController {
218 } 259 }
219 } 260 }
220 261
  262 + @ApiOperation(value = "Get List of Relations (findByTo)",
  263 + notes = "Returns list of relation objects for the specified entity by the 'to' direction. " +
  264 + SECURITY_CHECKS_ENTITY_DESCRIPTION,
  265 + produces = MediaType.APPLICATION_JSON_VALUE)
221 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 266 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
222 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) 267 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE})
223 @ResponseBody 268 @ResponseBody
224 - public List<EntityRelation> findByTo(@RequestParam(TO_ID) String strToId,  
225 - @RequestParam(TO_TYPE) String strToType, 269 + public List<EntityRelation> findByTo(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
  270 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
  271 + @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
226 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { 272 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
227 checkParameter(TO_ID, strToId); 273 checkParameter(TO_ID, strToId);
228 checkParameter(TO_TYPE, strToType); 274 checkParameter(TO_TYPE, strToType);
@@ -236,11 +282,16 @@ public class EntityRelationController extends BaseController { @@ -236,11 +282,16 @@ public class EntityRelationController extends BaseController {
236 } 282 }
237 } 283 }
238 284
  285 + @ApiOperation(value = "Get List of Relation Infos (findInfoByTo)",
  286 + notes = "Returns list of relation info objects for the specified entity by the 'to' direction. " +
  287 + SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION,
  288 + produces = MediaType.APPLICATION_JSON_VALUE)
239 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 289 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
240 @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) 290 @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE})
241 @ResponseBody 291 @ResponseBody
242 - public List<EntityRelationInfo> findInfoByTo(@RequestParam(TO_ID) String strToId,  
243 - @RequestParam(TO_TYPE) String strToType, 292 + public List<EntityRelationInfo> findInfoByTo(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
  293 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
  294 + @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
244 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { 295 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
245 checkParameter(TO_ID, strToId); 296 checkParameter(TO_ID, strToId);
246 checkParameter(TO_TYPE, strToType); 297 checkParameter(TO_TYPE, strToType);
@@ -254,12 +305,17 @@ public class EntityRelationController extends BaseController { @@ -254,12 +305,17 @@ public class EntityRelationController extends BaseController {
254 } 305 }
255 } 306 }
256 307
  308 + @ApiOperation(value = "Get List of Relations (findByTo)",
  309 + notes = "Returns list of relation objects for the specified entity by the 'to' direction and relation type. " +
  310 + SECURITY_CHECKS_ENTITY_DESCRIPTION,
  311 + produces = MediaType.APPLICATION_JSON_VALUE)
257 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 312 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
258 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE, RELATION_TYPE}) 313 @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE, RELATION_TYPE})
259 @ResponseBody 314 @ResponseBody
260 - public List<EntityRelation> findByTo(@RequestParam(TO_ID) String strToId,  
261 - @RequestParam(TO_TYPE) String strToType,  
262 - @RequestParam(RELATION_TYPE) String strRelationType, 315 + public List<EntityRelation> findByTo(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
  316 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
  317 + @ApiParam(value = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
  318 + @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
263 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { 319 @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
264 checkParameter(TO_ID, strToId); 320 checkParameter(TO_ID, strToId);
265 checkParameter(TO_TYPE, strToType); 321 checkParameter(TO_TYPE, strToType);
@@ -274,10 +330,15 @@ public class EntityRelationController extends BaseController { @@ -274,10 +330,15 @@ public class EntityRelationController extends BaseController {
274 } 330 }
275 } 331 }
276 332
  333 + @ApiOperation(value = "Find related entities (findByQuery)",
  334 + notes = "Returns all entities that are related to the specific entity. " +
  335 + "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " +
  336 + "See 'Model' tab of the Parameters for more info.", produces = MediaType.APPLICATION_JSON_VALUE)
277 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 337 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
278 @RequestMapping(value = "/relations", method = RequestMethod.POST) 338 @RequestMapping(value = "/relations", method = RequestMethod.POST)
279 @ResponseBody 339 @ResponseBody
280 - public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException { 340 + public List<EntityRelation> findByQuery(@ApiParam(value = "A JSON value representing the entity relations query object.", required = true)
  341 + @RequestBody EntityRelationsQuery query) throws ThingsboardException {
281 checkNotNull(query); 342 checkNotNull(query);
282 checkNotNull(query.getParameters()); 343 checkNotNull(query.getParameters());
283 checkNotNull(query.getFilters()); 344 checkNotNull(query.getFilters());
@@ -289,10 +350,15 @@ public class EntityRelationController extends BaseController { @@ -289,10 +350,15 @@ public class EntityRelationController extends BaseController {
289 } 350 }
290 } 351 }
291 352
  353 + @ApiOperation(value = "Find related entity infos (findInfoByQuery)",
  354 + notes = "Returns all entity infos that are related to the specific entity. " +
  355 + "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " +
  356 + "See 'Model' tab of the Parameters for more info. " + RELATION_INFO_DESCRIPTION, produces = MediaType.APPLICATION_JSON_VALUE)
292 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 357 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
293 @RequestMapping(value = "/relations/info", method = RequestMethod.POST) 358 @RequestMapping(value = "/relations/info", method = RequestMethod.POST)
294 @ResponseBody 359 @ResponseBody
295 - public List<EntityRelationInfo> findInfoByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException { 360 + public List<EntityRelationInfo> findInfoByQuery(@ApiParam(value = "A JSON value representing the entity relations query object.", required = true)
  361 + @RequestBody EntityRelationsQuery query) throws ThingsboardException {
296 checkNotNull(query); 362 checkNotNull(query);
297 checkNotNull(query.getParameters()); 363 checkNotNull(query.getParameters());
298 checkNotNull(query.getFilters()); 364 checkNotNull(query.getFilters());
@@ -20,9 +20,11 @@ import com.google.common.util.concurrent.Futures; @@ -20,9 +20,11 @@ import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.MoreExecutors; 21 import com.google.common.util.concurrent.MoreExecutors;
22 import com.google.common.util.concurrent.SettableFuture; 22 import com.google.common.util.concurrent.SettableFuture;
  23 +import io.swagger.annotations.ApiOperation;
23 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
24 import org.springframework.beans.factory.annotation.Autowired; 25 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.http.HttpStatus; 26 import org.springframework.http.HttpStatus;
  27 +import org.springframework.http.MediaType;
26 import org.springframework.security.access.prepost.PreAuthorize; 28 import org.springframework.security.access.prepost.PreAuthorize;
27 import org.springframework.web.bind.annotation.PathVariable; 29 import org.springframework.web.bind.annotation.PathVariable;
28 import org.springframework.web.bind.annotation.RequestBody; 30 import org.springframework.web.bind.annotation.RequestBody;
@@ -611,6 +613,13 @@ public class EntityViewController extends BaseController { @@ -611,6 +613,13 @@ public class EntityViewController extends BaseController {
611 } 613 }
612 } 614 }
613 615
  616 + @ApiOperation(value = "Assign entity view to edge (assignEntityViewToEdge)",
  617 + notes = "Creates assignment of an existing entity view to an instance of The Edge. " +
  618 + EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  619 + "Second, remote edge service will receive a copy of assignment entity view " +
  620 + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  621 + "Third, once entity view will be delivered to edge service, it's going to be available for usage on remote edge instance.",
  622 + produces = MediaType.APPLICATION_JSON_VALUE)
614 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 623 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
615 @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST) 624 @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST)
616 @ResponseBody 625 @ResponseBody
@@ -641,6 +650,13 @@ public class EntityViewController extends BaseController { @@ -641,6 +650,13 @@ public class EntityViewController extends BaseController {
641 } 650 }
642 } 651 }
643 652
  653 + @ApiOperation(value = "Unassign entity view from edge (unassignEntityViewFromEdge)",
  654 + notes = "Clears assignment of the entity view to the edge. " +
  655 + EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  656 + "Second, remote edge service will receive an 'unassign' command to remove entity view " +
  657 + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  658 + "Third, once 'unassign' command will be delivered to edge service, it's going to remove entity view locally.",
  659 + produces = MediaType.APPLICATION_JSON_VALUE)
644 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 660 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
645 @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE) 661 @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE)
646 @ResponseBody 662 @ResponseBody
@@ -15,7 +15,10 @@ @@ -15,7 +15,10 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
  21 +import org.springframework.http.MediaType;
19 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.web.bind.annotation.PathVariable; 23 import org.springframework.web.bind.annotation.PathVariable;
21 import org.springframework.web.bind.annotation.RequestBody; 24 import org.springframework.web.bind.annotation.RequestBody;
@@ -25,7 +28,7 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -25,7 +28,7 @@ import org.springframework.web.bind.annotation.RequestParam;
25 import org.springframework.web.bind.annotation.ResponseBody; 28 import org.springframework.web.bind.annotation.ResponseBody;
26 import org.springframework.web.bind.annotation.RestController; 29 import org.springframework.web.bind.annotation.RestController;
27 import org.thingsboard.server.common.data.Event; 30 import org.thingsboard.server.common.data.Event;
28 -import org.thingsboard.server.common.data.event.EventFilter; 31 +import org.thingsboard.server.common.data.event.BaseEventFilter;
29 import org.thingsboard.server.common.data.exception.ThingsboardException; 32 import org.thingsboard.server.common.data.exception.ThingsboardException;
30 import org.thingsboard.server.common.data.id.EntityId; 33 import org.thingsboard.server.common.data.id.EntityId;
31 import org.thingsboard.server.common.data.id.EntityIdFactory; 34 import org.thingsboard.server.common.data.id.EntityIdFactory;
@@ -42,23 +45,39 @@ import org.thingsboard.server.service.security.permission.Operation; @@ -42,23 +45,39 @@ import org.thingsboard.server.service.security.permission.Operation;
42 @RequestMapping("/api") 45 @RequestMapping("/api")
43 public class EventController extends BaseController { 46 public class EventController extends BaseController {
44 47
  48 + private static final String NEW_LINE = "\n\n";
  49 +
45 @Autowired 50 @Autowired
46 private EventService eventService; 51 private EventService eventService;
47 52
  53 + @ApiOperation(value = "Get Events by type (getEvents)",
  54 + notes = "Returns a page of events for specified entity by specifying event type. " +
  55 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
48 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 56 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
49 @RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET) 57 @RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET)
50 @ResponseBody 58 @ResponseBody
51 public PageData<Event> getEvents( 59 public PageData<Event> getEvents(
52 - @PathVariable("entityType") String strEntityType,  
53 - @PathVariable("entityId") String strEntityId, 60 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
  61 + @PathVariable(ENTITY_TYPE) String strEntityType,
  62 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
  63 + @PathVariable(ENTITY_ID) String strEntityId,
  64 + @ApiParam(value = "A string value representing event type", example = "STATS", required = true)
54 @PathVariable("eventType") String eventType, 65 @PathVariable("eventType") String eventType,
55 - @RequestParam("tenantId") String strTenantId, 66 + @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION, required = true)
  67 + @RequestParam(TENANT_ID) String strTenantId,
  68 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
56 @RequestParam int pageSize, 69 @RequestParam int pageSize,
  70 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
57 @RequestParam int page, 71 @RequestParam int page,
  72 + @ApiParam(value = EVENT_TEXT_SEARCH_DESCRIPTION)
58 @RequestParam(required = false) String textSearch, 73 @RequestParam(required = false) String textSearch,
  74 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EVENT_SORT_PROPERTY_ALLOWABLE_VALUES)
59 @RequestParam(required = false) String sortProperty, 75 @RequestParam(required = false) String sortProperty,
  76 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
60 @RequestParam(required = false) String sortOrder, 77 @RequestParam(required = false) String sortOrder,
  78 + @ApiParam(value = EVENT_START_TIME_DESCRIPTION)
61 @RequestParam(required = false) Long startTime, 79 @RequestParam(required = false) Long startTime,
  80 + @ApiParam(value = EVENT_END_TIME_DESCRIPTION)
62 @RequestParam(required = false) Long endTime) throws ThingsboardException { 81 @RequestParam(required = false) Long endTime) throws ThingsboardException {
63 checkParameter("EntityId", strEntityId); 82 checkParameter("EntityId", strEntityId);
64 checkParameter("EntityType", strEntityType); 83 checkParameter("EntityType", strEntityType);
@@ -74,19 +93,32 @@ public class EventController extends BaseController { @@ -74,19 +93,32 @@ public class EventController extends BaseController {
74 } 93 }
75 } 94 }
76 95
  96 + @ApiOperation(value = "Get Events (getEvents)",
  97 + notes = "Returns a page of events for specified entity. " +
  98 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
77 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 99 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
78 @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET) 100 @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET)
79 @ResponseBody 101 @ResponseBody
80 public PageData<Event> getEvents( 102 public PageData<Event> getEvents(
81 - @PathVariable("entityType") String strEntityType,  
82 - @PathVariable("entityId") String strEntityId, 103 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
  104 + @PathVariable(ENTITY_TYPE) String strEntityType,
  105 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
  106 + @PathVariable(ENTITY_ID) String strEntityId,
  107 + @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION, required = true)
83 @RequestParam("tenantId") String strTenantId, 108 @RequestParam("tenantId") String strTenantId,
  109 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
84 @RequestParam int pageSize, 110 @RequestParam int pageSize,
  111 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
85 @RequestParam int page, 112 @RequestParam int page,
  113 + @ApiParam(value = EVENT_TEXT_SEARCH_DESCRIPTION)
86 @RequestParam(required = false) String textSearch, 114 @RequestParam(required = false) String textSearch,
  115 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EVENT_SORT_PROPERTY_ALLOWABLE_VALUES)
87 @RequestParam(required = false) String sortProperty, 116 @RequestParam(required = false) String sortProperty,
  117 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
88 @RequestParam(required = false) String sortOrder, 118 @RequestParam(required = false) String sortOrder,
  119 + @ApiParam(value = EVENT_START_TIME_DESCRIPTION)
89 @RequestParam(required = false) Long startTime, 120 @RequestParam(required = false) Long startTime,
  121 + @ApiParam(value = EVENT_END_TIME_DESCRIPTION)
90 @RequestParam(required = false) Long endTime) throws ThingsboardException { 122 @RequestParam(required = false) Long endTime) throws ThingsboardException {
91 checkParameter("EntityId", strEntityId); 123 checkParameter("EntityId", strEntityId);
92 checkParameter("EntityType", strEntityType); 124 checkParameter("EntityType", strEntityType);
@@ -104,20 +136,42 @@ public class EventController extends BaseController { @@ -104,20 +136,42 @@ public class EventController extends BaseController {
104 } 136 }
105 } 137 }
106 138
  139 + @ApiOperation(value = "Get Events by event filter (getEvents)",
  140 + notes = "Returns a page of events for the chosen entity by specifying the event filter. " +
  141 + PAGE_DATA_PARAMETERS + NEW_LINE + "5 different eventFilter objects could be set for different event types. " +
  142 + "The eventType field is required. Others are optional. If some of them are set, the filtering will be applied according to them. " +
  143 + "See the examples below for all the fields used for each event type filtering. " + NEW_LINE +
  144 + EVENT_ERROR_FILTER_OBJ + NEW_LINE +
  145 + EVENT_LC_EVENT_FILTER_OBJ + NEW_LINE +
  146 + EVENT_STATS_FILTER_OBJ + NEW_LINE +
  147 + EVENT_DEBUG_RULE_NODE_FILTER_OBJ + NEW_LINE +
  148 + EVENT_DEBUG_RULE_CHAIN_FILTER_OBJ + NEW_LINE,
  149 + produces = MediaType.APPLICATION_JSON_VALUE)
107 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 150 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
108 @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST) 151 @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST)
109 @ResponseBody 152 @ResponseBody
110 public PageData<Event> getEvents( 153 public PageData<Event> getEvents(
111 - @PathVariable("entityType") String strEntityType,  
112 - @PathVariable("entityId") String strEntityId,  
113 - @RequestParam("tenantId") String strTenantId, 154 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
  155 + @PathVariable(ENTITY_TYPE) String strEntityType,
  156 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
  157 + @PathVariable(ENTITY_ID) String strEntityId,
  158 + @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION, required = true)
  159 + @RequestParam(TENANT_ID) String strTenantId,
  160 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
114 @RequestParam int pageSize, 161 @RequestParam int pageSize,
  162 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
115 @RequestParam int page, 163 @RequestParam int page,
116 - @RequestBody EventFilter eventFilter, 164 + @ApiParam(value = "A JSON value representing the event filter.", required = true)
  165 + @RequestBody BaseEventFilter eventFilter,
  166 + @ApiParam(value = EVENT_TEXT_SEARCH_DESCRIPTION)
117 @RequestParam(required = false) String textSearch, 167 @RequestParam(required = false) String textSearch,
  168 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EVENT_SORT_PROPERTY_ALLOWABLE_VALUES)
118 @RequestParam(required = false) String sortProperty, 169 @RequestParam(required = false) String sortProperty,
  170 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
119 @RequestParam(required = false) String sortOrder, 171 @RequestParam(required = false) String sortOrder,
  172 + @ApiParam(value = EVENT_START_TIME_DESCRIPTION)
120 @RequestParam(required = false) Long startTime, 173 @RequestParam(required = false) Long startTime,
  174 + @ApiParam(value = EVENT_END_TIME_DESCRIPTION)
121 @RequestParam(required = false) Long endTime) throws ThingsboardException { 175 @RequestParam(required = false) Long endTime) throws ThingsboardException {
122 checkParameter("EntityId", strEntityId); 176 checkParameter("EntityId", strEntityId);
123 checkParameter("EntityType", strEntityType); 177 checkParameter("EntityType", strEntityType);
@@ -127,7 +181,7 @@ public class EventController extends BaseController { @@ -127,7 +181,7 @@ public class EventController extends BaseController {
127 EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); 181 EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
128 checkEntityId(entityId, Operation.READ); 182 checkEntityId(entityId, Operation.READ);
129 183
130 - if(sortProperty != null && sortProperty.equals("createdTime") && eventFilter.hasFilterForJsonBody()) { 184 + if (sortProperty != null && sortProperty.equals("createdTime") && eventFilter.hasFilterForJsonBody()) {
131 sortProperty = ModelConstants.CREATED_TIME_PROPERTY; 185 sortProperty = ModelConstants.CREATED_TIME_PROPERTY;
132 } 186 }
133 187
@@ -15,12 +15,18 @@ @@ -15,12 +15,18 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.http.HttpStatus; 21 import org.springframework.http.HttpStatus;
20 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
21 -import org.springframework.web.bind.annotation.*;  
22 -import org.thingsboard.server.common.data.EntityType;  
23 -import org.thingsboard.server.common.data.audit.ActionType; 23 +import org.springframework.web.bind.annotation.PathVariable;
  24 +import org.springframework.web.bind.annotation.RequestBody;
  25 +import org.springframework.web.bind.annotation.RequestMapping;
  26 +import org.springframework.web.bind.annotation.RequestMethod;
  27 +import org.springframework.web.bind.annotation.ResponseBody;
  28 +import org.springframework.web.bind.annotation.ResponseStatus;
  29 +import org.springframework.web.bind.annotation.RestController;
24 import org.thingsboard.server.common.data.exception.ThingsboardException; 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
25 import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; 31 import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
26 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; 32 import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
@@ -37,6 +43,10 @@ import java.util.List; @@ -37,6 +43,10 @@ import java.util.List;
37 public class OAuth2ConfigTemplateController extends BaseController { 43 public class OAuth2ConfigTemplateController extends BaseController {
38 private static final String CLIENT_REGISTRATION_TEMPLATE_ID = "clientRegistrationTemplateId"; 44 private static final String CLIENT_REGISTRATION_TEMPLATE_ID = "clientRegistrationTemplateId";
39 45
  46 + private static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_DEFINITION = "Client registration template is OAuth2 provider configuration template with default settings for registering new OAuth2 clients";
  47 +
  48 + @ApiOperation(value = "Create or update OAuth2 client registration template (saveClientRegistrationTemplate)",
  49 + notes = OAUTH2_CLIENT_REGISTRATION_TEMPLATE_DEFINITION)
40 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 50 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
41 @RequestMapping(method = RequestMethod.POST) 51 @RequestMapping(method = RequestMethod.POST)
42 @ResponseStatus(value = HttpStatus.OK) 52 @ResponseStatus(value = HttpStatus.OK)
@@ -49,10 +59,13 @@ public class OAuth2ConfigTemplateController extends BaseController { @@ -49,10 +59,13 @@ public class OAuth2ConfigTemplateController extends BaseController {
49 } 59 }
50 } 60 }
51 61
  62 + @ApiOperation(value = "Delete OAuth2 client registration template by id (deleteClientRegistrationTemplate)",
  63 + notes = OAUTH2_CLIENT_REGISTRATION_TEMPLATE_DEFINITION)
52 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 64 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
53 @RequestMapping(value = "/{clientRegistrationTemplateId}", method = RequestMethod.DELETE) 65 @RequestMapping(value = "/{clientRegistrationTemplateId}", method = RequestMethod.DELETE)
54 @ResponseStatus(value = HttpStatus.OK) 66 @ResponseStatus(value = HttpStatus.OK)
55 - public void deleteClientRegistrationTemplate(@PathVariable(CLIENT_REGISTRATION_TEMPLATE_ID) String strClientRegistrationTemplateId) throws ThingsboardException { 67 + public void deleteClientRegistrationTemplate(@ApiParam(value = "String representation of client registration template id to delete", example = "139b1f81-2f5d-11ec-9dbe-9b627e1a88f4")
  68 + @PathVariable(CLIENT_REGISTRATION_TEMPLATE_ID) String strClientRegistrationTemplateId) throws ThingsboardException {
56 checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId); 69 checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId);
57 try { 70 try {
58 accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE); 71 accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE);
@@ -63,6 +76,8 @@ public class OAuth2ConfigTemplateController extends BaseController { @@ -63,6 +76,8 @@ public class OAuth2ConfigTemplateController extends BaseController {
63 } 76 }
64 } 77 }
65 78
  79 + @ApiOperation(value = "Get the list of all OAuth2 client registration templates (getClientRegistrationTemplates)",
  80 + notes = OAUTH2_CLIENT_REGISTRATION_TEMPLATE_DEFINITION)
66 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 81 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
67 @RequestMapping(method = RequestMethod.GET, produces = "application/json") 82 @RequestMapping(method = RequestMethod.GET, produces = "application/json")
68 @ResponseBody 83 @ResponseBody
@@ -74,4 +89,5 @@ public class OAuth2ConfigTemplateController extends BaseController { @@ -74,4 +89,5 @@ public class OAuth2ConfigTemplateController extends BaseController {
74 throw handleException(e); 89 throw handleException(e);
75 } 90 }
76 } 91 }
  92 +
77 } 93 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.http.HttpStatus; 22 import org.springframework.http.HttpStatus;
@@ -50,10 +52,20 @@ public class OAuth2Controller extends BaseController { @@ -50,10 +52,20 @@ public class OAuth2Controller extends BaseController {
50 @Autowired 52 @Autowired
51 private OAuth2Configuration oAuth2Configuration; 53 private OAuth2Configuration oAuth2Configuration;
52 54
  55 +
  56 + @ApiOperation(value = "Get OAuth2 clients (getOAuth2Clients)", notes = "Get the list of OAuth2 clients " +
  57 + "to log in with, available for such domain scheme (HTTP or HTTPS) (if x-forwarded-proto request header is present - " +
  58 + "the scheme is known from it) and domain name and port (port may be known from x-forwarded-port header)")
53 @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) 59 @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST)
54 @ResponseBody 60 @ResponseBody
55 public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request, 61 public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request,
  62 + @ApiParam(value = "Mobile application package name, to find OAuth2 clients " +
  63 + "where there is configured mobile application with such package name")
56 @RequestParam(required = false) String pkgName, 64 @RequestParam(required = false) String pkgName,
  65 + @ApiParam(value = "Platform type to search OAuth2 clients for which " +
  66 + "the usage with this platform type is allowed in the settings. " +
  67 + "If platform type is not one of allowable values - it will just be ignored",
  68 + allowableValues = "WEB, ANDROID, IOS")
57 @RequestParam(required = false) String platform) throws ThingsboardException { 69 @RequestParam(required = false) String platform) throws ThingsboardException {
58 try { 70 try {
59 if (log.isDebugEnabled()) { 71 if (log.isDebugEnabled()) {
@@ -76,6 +88,7 @@ public class OAuth2Controller extends BaseController { @@ -76,6 +88,7 @@ public class OAuth2Controller extends BaseController {
76 } 88 }
77 } 89 }
78 90
  91 + @ApiOperation(value = "Get current OAuth2 settings (getCurrentOAuth2Info)")
79 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 92 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
80 @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") 93 @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json")
81 @ResponseBody 94 @ResponseBody
@@ -88,6 +101,7 @@ public class OAuth2Controller extends BaseController { @@ -88,6 +101,7 @@ public class OAuth2Controller extends BaseController {
88 } 101 }
89 } 102 }
90 103
  104 + @ApiOperation(value = "Save OAuth2 settings (saveOAuth2Info)")
91 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 105 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
92 @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) 106 @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST)
93 @ResponseStatus(value = HttpStatus.OK) 107 @ResponseStatus(value = HttpStatus.OK)
@@ -101,6 +115,10 @@ public class OAuth2Controller extends BaseController { @@ -101,6 +115,10 @@ public class OAuth2Controller extends BaseController {
101 } 115 }
102 } 116 }
103 117
  118 + @ApiOperation(value = "Get OAuth2 log in processing URL (getLoginProcessingUrl)", notes = "Returns the URL enclosed in " +
  119 + "double quotes. After successful authentication with OAuth2 provider, it makes a redirect to this path so that the platform can do " +
  120 + "further log in processing. This URL may be configured as 'security.oauth2.loginProcessingUrl' property in yml configuration file, or " +
  121 + "as 'SECURITY_OAUTH2_LOGIN_PROCESSING_URL' env variable. By default it is '/login/oauth2/code/'")
104 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 122 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
105 @RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET) 123 @RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET)
106 @ResponseBody 124 @ResponseBody
@@ -112,4 +130,5 @@ public class OAuth2Controller extends BaseController { @@ -112,4 +130,5 @@ public class OAuth2Controller extends BaseController {
112 throw handleException(e); 130 throw handleException(e);
113 } 131 }
114 } 132 }
  133 +
115 } 134 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.http.HttpStatus; 21 import org.springframework.http.HttpStatus;
20 import org.springframework.http.ResponseEntity; 22 import org.springframework.http.ResponseEntity;
@@ -38,17 +40,27 @@ import java.util.UUID; @@ -38,17 +40,27 @@ import java.util.UUID;
38 @Slf4j 40 @Slf4j
39 public class RpcV1Controller extends AbstractRpcController { 41 public class RpcV1Controller extends AbstractRpcController {
40 42
  43 + @ApiOperation(value = "Send one-way RPC request (handleOneWayDeviceRPCRequest)", notes = "Deprecated. See 'Rpc V 2 Controller' instead.")
41 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 44 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
42 @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST) 45 @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
43 @ResponseBody 46 @ResponseBody
44 - public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException { 47 + public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(
  48 + @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
  49 + @PathVariable("deviceId") String deviceIdStr,
  50 + @ApiParam(value = "A JSON value representing the RPC request.")
  51 + @RequestBody String requestBody) throws ThingsboardException {
45 return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT); 52 return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
46 } 53 }
47 54
  55 + @ApiOperation(value = "Send two-way RPC request (handleTwoWayDeviceRPCRequest)", notes = "Deprecated. See 'Rpc V 2 Controller' instead.")
48 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 56 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
49 @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST) 57 @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
50 @ResponseBody 58 @ResponseBody
51 - public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException { 59 + public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(
  60 + @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
  61 + @PathVariable("deviceId") String deviceIdStr,
  62 + @ApiParam(value = "A JSON value representing the RPC request.")
  63 + @RequestBody String requestBody) throws ThingsboardException {
52 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT); 64 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
53 } 65 }
54 66
@@ -15,6 +15,10 @@ @@ -15,6 +15,10 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
  20 +import io.swagger.annotations.ApiResponse;
  21 +import io.swagger.annotations.ApiResponses;
18 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.http.HttpStatus; 23 import org.springframework.http.HttpStatus;
20 import org.springframework.http.ResponseEntity; 24 import org.springframework.http.ResponseEntity;
@@ -52,24 +56,88 @@ import static org.thingsboard.server.common.data.DataConstants.RPC_DELETED; @@ -52,24 +56,88 @@ import static org.thingsboard.server.common.data.DataConstants.RPC_DELETED;
52 @Slf4j 56 @Slf4j
53 public class RpcV2Controller extends AbstractRpcController { 57 public class RpcV2Controller extends AbstractRpcController {
54 58
  59 + private static final String RPC_REQUEST_DESCRIPTION = "Sends the one-way remote-procedure call (RPC) request to device. " +
  60 + "The RPC call is A JSON that contains the method name ('method'), parameters ('params') and multiple optional fields. " +
  61 + "See example below. We will review the properties of the RPC call one-by-one below. " +
  62 + "\n\n" + MARKDOWN_CODE_BLOCK_START +
  63 + "{\n" +
  64 + " \"method\": \"setGpio\",\n" +
  65 + " \"params\": {\n" +
  66 + " \"pin\": 7,\n" +
  67 + " \"value\": 1\n" +
  68 + " },\n" +
  69 + " \"persistent\": false,\n" +
  70 + " \"timeout\": 5000\n" +
  71 + "}" +
  72 + MARKDOWN_CODE_BLOCK_END +
  73 + "\n\n### Server-side RPC structure\n" +
  74 + "\n" +
  75 + "The body of server-side RPC request consists of multiple fields:\n" +
  76 + "\n" +
  77 + "* **method** - mandatory, name of the method to distinct the RPC calls.\n" +
  78 + " For example, \"getCurrentTime\" or \"getWeatherForecast\". The value of the parameter is a string.\n" +
  79 + "* **params** - mandatory, parameters used for processing of the request. The value is a JSON. Leave empty JSON \"{}\" if no parameters needed.\n" +
  80 + "* **timeout** - optional, value of the processing timeout in milliseconds. The default value is 10000 (10 seconds). The minimum value is 5000 (5 seconds).\n" +
  81 + "* **expirationTime** - optional, value of the epoch time (in milliseconds, UTC timezone). Overrides **timeout** if present.\n" +
  82 + "* **persistent** - optional, indicates persistent RPC. The default value is \"false\".\n" +
  83 + "* **retries** - optional, defines how many times persistent RPC will be re-sent in case of failures on the network and/or device side.\n" +
  84 + "* **additionalInfo** - optional, defines metadata for the persistent RPC that will be added to the persistent RPC events.";
  85 +
  86 + private static final String ONE_WAY_RPC_RESULT = "\n\n### RPC Result\n" +
  87 + "In case of persistent RPC, the result of this call is 'rpcId' UUID. In case of lightweight RPC, " +
  88 + "the result of this call is either 200 OK if the message was sent to device, or 504 Gateway Timeout if device is offline.";
  89 +
  90 + private static final String TWO_WAY_RPC_RESULT = "\n\n### RPC Result\n" +
  91 + "In case of persistent RPC, the result of this call is 'rpcId' UUID. In case of lightweight RPC, " +
  92 + "the result of this call is the response from device, or 504 Gateway Timeout if device is offline.";
  93 +
  94 + private static final String ONE_WAY_RPC_REQUEST_DESCRIPTION = "Sends the one-way remote-procedure call (RPC) request to device. " + RPC_REQUEST_DESCRIPTION + ONE_WAY_RPC_RESULT + TENANT_OR_USER_AUTHORITY_PARAGRAPH;
  95 +
  96 + private static final String TWO_WAY_RPC_REQUEST_DESCRIPTION = "Sends the two-way remote-procedure call (RPC) request to device. " + RPC_REQUEST_DESCRIPTION + TWO_WAY_RPC_RESULT + TENANT_OR_USER_AUTHORITY_PARAGRAPH;
  97 +
  98 + @ApiOperation(value = "Send one-way RPC request", notes = ONE_WAY_RPC_REQUEST_DESCRIPTION)
  99 + @ApiResponses(value = {
  100 + @ApiResponse(code = 200, message = "Persistent RPC request was saved to the database or lightweight RPC request was sent to the device."),
  101 + @ApiResponse(code = 400, message = "Invalid structure of the request."),
  102 + @ApiResponse(code = 401, message = "User is not authorized to send the RPC request. Most likely, User belongs to different Customer or Tenant."),
  103 + @ApiResponse(code = 504, message = "Timeout to process the RPC call. Most likely, device is offline."),
  104 + })
55 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 105 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
56 @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST) 106 @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
57 @ResponseBody 107 @ResponseBody
58 - public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException { 108 + public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(
  109 + @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
  110 + @PathVariable("deviceId") String deviceIdStr,
  111 + @ApiParam(value = "A JSON value representing the RPC request.")
  112 + @RequestBody String requestBody) throws ThingsboardException {
59 return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT); 113 return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
60 } 114 }
61 115
  116 + @ApiOperation(value = "Send two-way RPC request", notes = TWO_WAY_RPC_REQUEST_DESCRIPTION)
  117 + @ApiResponses(value = {
  118 + @ApiResponse(code = 200, message = "Persistent RPC request was saved to the database or lightweight RPC response received."),
  119 + @ApiResponse(code = 400, message = "Invalid structure of the request."),
  120 + @ApiResponse(code = 401, message = "User is not authorized to send the RPC request. Most likely, User belongs to different Customer or Tenant."),
  121 + @ApiResponse(code = 504, message = "Timeout to process the RPC call. Most likely, device is offline."),
  122 + })
62 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 123 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
63 @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST) 124 @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
64 @ResponseBody 125 @ResponseBody
65 - public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException { 126 + public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(
  127 + @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
  128 + @PathVariable(DEVICE_ID) String deviceIdStr,
  129 + @ApiParam(value = "A JSON value representing the RPC request.")
  130 + @RequestBody String requestBody) throws ThingsboardException {
66 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT); 131 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
67 } 132 }
68 133
  134 + @ApiOperation(value = "Get persistent RPC request", notes = "Get information about the status of the RPC call." + TENANT_OR_USER_AUTHORITY_PARAGRAPH)
69 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 135 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
70 @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET) 136 @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)
71 @ResponseBody 137 @ResponseBody
72 - public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException { 138 + public Rpc getPersistedRpc(
  139 + @ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true)
  140 + @PathVariable(RPC_ID) String strRpc) throws ThingsboardException {
73 checkParameter("RpcId", strRpc); 141 checkParameter("RpcId", strRpc);
74 try { 142 try {
75 RpcId rpcId = new RpcId(UUID.fromString(strRpc)); 143 RpcId rpcId = new RpcId(UUID.fromString(strRpc));
@@ -79,16 +147,25 @@ public class RpcV2Controller extends AbstractRpcController { @@ -79,16 +147,25 @@ public class RpcV2Controller extends AbstractRpcController {
79 } 147 }
80 } 148 }
81 149
  150 + @ApiOperation(value = "Get persistent RPC requests", notes = "Allows to query RPC calls for specific device using pagination." + TENANT_OR_USER_AUTHORITY_PARAGRAPH)
82 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 151 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
83 @RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET) 152 @RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET)
84 @ResponseBody 153 @ResponseBody
85 - public PageData<Rpc> getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId,  
86 - @RequestParam int pageSize,  
87 - @RequestParam int page,  
88 - @RequestParam RpcStatus rpcStatus,  
89 - @RequestParam(required = false) String textSearch,  
90 - @RequestParam(required = false) String sortProperty,  
91 - @RequestParam(required = false) String sortOrder) throws ThingsboardException { 154 + public PageData<Rpc> getPersistedRpcByDevice(
  155 + @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true)
  156 + @PathVariable(DEVICE_ID) String strDeviceId,
  157 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
  158 + @RequestParam int pageSize,
  159 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
  160 + @RequestParam int page,
  161 + @ApiParam(value = "Status of the RPC", required = true, allowableValues = RPC_STATUS_ALLOWABLE_VALUES)
  162 + @RequestParam RpcStatus rpcStatus,
  163 + @ApiParam(value = RPC_TEXT_SEARCH_DESCRIPTION)
  164 + @RequestParam(required = false) String textSearch,
  165 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RPC_SORT_PROPERTY_ALLOWABLE_VALUES)
  166 + @RequestParam(required = false) String sortProperty,
  167 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
  168 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
92 checkParameter("DeviceId", strDeviceId); 169 checkParameter("DeviceId", strDeviceId);
93 try { 170 try {
94 TenantId tenantId = getCurrentUser().getTenantId(); 171 TenantId tenantId = getCurrentUser().getTenantId();
@@ -100,10 +177,13 @@ public class RpcV2Controller extends AbstractRpcController { @@ -100,10 +177,13 @@ public class RpcV2Controller extends AbstractRpcController {
100 } 177 }
101 } 178 }
102 179
  180 + @ApiOperation(value = "Delete persistent RPC", notes = "Deletes the persistent RPC request." + TENANT_AUTHORITY_PARAGRAPH)
103 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 181 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
104 @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE) 182 @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)
105 @ResponseBody 183 @ResponseBody
106 - public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException { 184 + public void deleteResource(
  185 + @ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true)
  186 + @PathVariable(RPC_ID) String strRpc) throws ThingsboardException {
107 checkParameter("RpcId", strRpc); 187 checkParameter("RpcId", strRpc);
108 try { 188 try {
109 RpcId rpcId = new RpcId(UUID.fromString(strRpc)); 189 RpcId rpcId = new RpcId(UUID.fromString(strRpc));
@@ -20,10 +20,13 @@ import com.fasterxml.jackson.databind.JsonNode; @@ -20,10 +20,13 @@ import com.fasterxml.jackson.databind.JsonNode;
20 import com.fasterxml.jackson.databind.ObjectMapper; 20 import com.fasterxml.jackson.databind.ObjectMapper;
21 import com.fasterxml.jackson.databind.node.ArrayNode; 21 import com.fasterxml.jackson.databind.node.ArrayNode;
22 import com.fasterxml.jackson.databind.node.ObjectNode; 22 import com.fasterxml.jackson.databind.node.ObjectNode;
  23 +import io.swagger.annotations.ApiOperation;
  24 +import io.swagger.annotations.ApiParam;
23 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
24 import org.springframework.beans.factory.annotation.Autowired; 26 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.beans.factory.annotation.Value; 27 import org.springframework.beans.factory.annotation.Value;
26 import org.springframework.http.HttpStatus; 28 import org.springframework.http.HttpStatus;
  29 +import org.springframework.http.MediaType;
27 import org.springframework.security.access.prepost.PreAuthorize; 30 import org.springframework.security.access.prepost.PreAuthorize;
28 import org.springframework.util.StringUtils; 31 import org.springframework.util.StringUtils;
29 import org.springframework.web.bind.annotation.PathVariable; 32 import org.springframework.web.bind.annotation.PathVariable;
@@ -92,6 +95,25 @@ public class RuleChainController extends BaseController { @@ -92,6 +95,25 @@ public class RuleChainController extends BaseController {
92 private static final ObjectMapper objectMapper = new ObjectMapper(); 95 private static final ObjectMapper objectMapper = new ObjectMapper();
93 public static final int TIMEOUT = 20; 96 public static final int TIMEOUT = 20;
94 97
  98 + private static final String RULE_CHAIN_DESCRIPTION = "The rule chain object is lightweight and contains general information about the rule chain. " +
  99 + "List of rule nodes and their connection is stored in a separate 'metadata' object.";
  100 + private static final String RULE_CHAIN_METADATA_DESCRIPTION = "The metadata object contains information about the rule nodes and their connections.";
  101 + private static final String TEST_JS_FUNCTION = "Execute the JavaScript function and return the result. The format of request: \n\n"
  102 + + MARKDOWN_CODE_BLOCK_START
  103 + + "{\n" +
  104 + " \"script\": \"Your JS Function as String\",\n" +
  105 + " \"scriptType\": \"One of: update, generate, filter, switch, json, string\",\n" +
  106 + " \"argNames\": [\"msg\", \"metadata\", \"type\"],\n" +
  107 + " \"msg\": \"{\\\"temperature\\\": 42}\", \n" +
  108 + " \"metadata\": {\n" +
  109 + " \"deviceName\": \"Device A\",\n" +
  110 + " \"deviceType\": \"Thermometer\"\n" +
  111 + " },\n" +
  112 + " \"msgType\": \"POST_TELEMETRY_REQUEST\"\n" +
  113 + "}"
  114 + + MARKDOWN_CODE_BLOCK_END
  115 + + "\n\n Expected result JSON contains \"output\" and \"error\".";
  116 +
95 @Autowired 117 @Autowired
96 private InstallScripts installScripts; 118 private InstallScripts installScripts;
97 119
@@ -107,10 +129,14 @@ public class RuleChainController extends BaseController { @@ -107,10 +129,14 @@ public class RuleChainController extends BaseController {
107 @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled}") 129 @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled}")
108 private boolean debugPerTenantEnabled; 130 private boolean debugPerTenantEnabled;
109 131
  132 + @ApiOperation(value = "Get Rule Chain (getRuleChainById)",
  133 + notes = "Fetch the Rule Chain object based on the provided Rule Chain Id. " + RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
110 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 134 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
111 @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET) 135 @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET)
112 @ResponseBody 136 @ResponseBody
113 - public RuleChain getRuleChainById(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { 137 + public RuleChain getRuleChainById(
  138 + @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
  139 + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
114 checkParameter(RULE_CHAIN_ID, strRuleChainId); 140 checkParameter(RULE_CHAIN_ID, strRuleChainId);
115 try { 141 try {
116 RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); 142 RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -120,10 +146,14 @@ public class RuleChainController extends BaseController { @@ -120,10 +146,14 @@ public class RuleChainController extends BaseController {
120 } 146 }
121 } 147 }
122 148
  149 + @ApiOperation(value = "Get Rule Chain (getRuleChainById)",
  150 + notes = "Fetch the Rule Chain Metadata object based on the provided Rule Chain Id. " + RULE_CHAIN_METADATA_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
123 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 151 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
124 @RequestMapping(value = "/ruleChain/{ruleChainId}/metadata", method = RequestMethod.GET) 152 @RequestMapping(value = "/ruleChain/{ruleChainId}/metadata", method = RequestMethod.GET)
125 @ResponseBody 153 @ResponseBody
126 - public RuleChainMetaData getRuleChainMetaData(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { 154 + public RuleChainMetaData getRuleChainMetaData(
  155 + @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
  156 + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
127 checkParameter(RULE_CHAIN_ID, strRuleChainId); 157 checkParameter(RULE_CHAIN_ID, strRuleChainId);
128 try { 158 try {
129 RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); 159 RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -134,11 +164,18 @@ public class RuleChainController extends BaseController { @@ -134,11 +164,18 @@ public class RuleChainController extends BaseController {
134 } 164 }
135 } 165 }
136 166
137 - 167 + @ApiOperation(value = "Create Or Update Rule Chain (saveRuleChain)",
  168 + notes = "Create or update the Rule Chain. When creating Rule Chain, platform generates Rule Chain Id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address). " +
  169 + "The newly created Rule Chain Id will be present in the response. " +
  170 + "Specify existing Rule Chain id to update the rule chain. " +
  171 + "Referencing non-existing rule chain Id will cause 'Not Found' error." +
  172 + "\n\n" + RULE_CHAIN_DESCRIPTION)
138 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 173 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
139 @RequestMapping(value = "/ruleChain", method = RequestMethod.POST) 174 @RequestMapping(value = "/ruleChain", method = RequestMethod.POST)
140 @ResponseBody 175 @ResponseBody
141 - public RuleChain saveRuleChain(@RequestBody RuleChain ruleChain) throws ThingsboardException { 176 + public RuleChain saveRuleChain(
  177 + @ApiParam(value = "A JSON value representing the rule chain.")
  178 + @RequestBody RuleChain ruleChain) throws ThingsboardException {
142 try { 179 try {
143 boolean created = ruleChain.getId() == null; 180 boolean created = ruleChain.getId() == null;
144 ruleChain.setTenantId(getCurrentUser().getTenantId()); 181 ruleChain.setTenantId(getCurrentUser().getTenantId());
@@ -172,10 +209,15 @@ public class RuleChainController extends BaseController { @@ -172,10 +209,15 @@ public class RuleChainController extends BaseController {
172 } 209 }
173 } 210 }
174 211
  212 + @ApiOperation(value = "Create Default Rule Chain",
  213 + notes = "Create rule chain from template, based on the specified name in the request. " +
  214 + "Creates the rule chain based on the template that is used to create root rule chain. " + TENANT_AUTHORITY_PARAGRAPH)
175 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 215 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
176 @RequestMapping(value = "/ruleChain/device/default", method = RequestMethod.POST) 216 @RequestMapping(value = "/ruleChain/device/default", method = RequestMethod.POST)
177 @ResponseBody 217 @ResponseBody
178 - public RuleChain saveRuleChain(@RequestBody DefaultRuleChainCreateRequest request) throws ThingsboardException { 218 + public RuleChain saveRuleChain(
  219 + @ApiParam(value = "A JSON value representing the request.")
  220 + @RequestBody DefaultRuleChainCreateRequest request) throws ThingsboardException {
179 try { 221 try {
180 checkNotNull(request); 222 checkNotNull(request);
181 checkParameter(request.getName(), "name"); 223 checkParameter(request.getName(), "name");
@@ -195,10 +237,14 @@ public class RuleChainController extends BaseController { @@ -195,10 +237,14 @@ public class RuleChainController extends BaseController {
195 } 237 }
196 } 238 }
197 239
  240 + @ApiOperation(value = "Set Root Rule Chain (setRootRuleChain)",
  241 + notes = "Makes the rule chain to be root rule chain. Updates previous root rule chain as well. " + TENANT_AUTHORITY_PARAGRAPH)
198 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 242 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
199 @RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST) 243 @RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST)
200 @ResponseBody 244 @ResponseBody
201 - public RuleChain setRootRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { 245 + public RuleChain setRootRuleChain(
  246 + @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
  247 + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
202 checkParameter(RULE_CHAIN_ID, strRuleChainId); 248 checkParameter(RULE_CHAIN_ID, strRuleChainId);
203 try { 249 try {
204 RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); 250 RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -234,10 +280,14 @@ public class RuleChainController extends BaseController { @@ -234,10 +280,14 @@ public class RuleChainController extends BaseController {
234 } 280 }
235 } 281 }
236 282
  283 + @ApiOperation(value = "Update Rule Chain Metadata",
  284 + notes = "Updates the rule chain metadata. " + RULE_CHAIN_METADATA_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
237 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 285 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
238 @RequestMapping(value = "/ruleChain/metadata", method = RequestMethod.POST) 286 @RequestMapping(value = "/ruleChain/metadata", method = RequestMethod.POST)
239 @ResponseBody 287 @ResponseBody
240 - public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException { 288 + public RuleChainMetaData saveRuleChainMetaData(
  289 + @ApiParam(value = "A JSON value representing the rule chain metadata.")
  290 + @RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException {
241 try { 291 try {
242 TenantId tenantId = getTenantId(); 292 TenantId tenantId = getTenantId();
243 if (debugPerTenantEnabled) { 293 if (debugPerTenantEnabled) {
@@ -275,15 +325,24 @@ public class RuleChainController extends BaseController { @@ -275,15 +325,24 @@ public class RuleChainController extends BaseController {
275 } 325 }
276 } 326 }
277 327
  328 +
  329 + @ApiOperation(value = "Get Rule Chains (getRuleChains)",
  330 + notes = "Returns a page of Rule Chains owned by tenant. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS)
278 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 331 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
279 @RequestMapping(value = "/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET) 332 @RequestMapping(value = "/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
280 @ResponseBody 333 @ResponseBody
281 public PageData<RuleChain> getRuleChains( 334 public PageData<RuleChain> getRuleChains(
  335 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
282 @RequestParam int pageSize, 336 @RequestParam int pageSize,
  337 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
283 @RequestParam int page, 338 @RequestParam int page,
  339 + @ApiParam(value = RULE_CHAIN_TYPE_DESCRIPTION, allowableValues = RULE_CHAIN_TYPES_ALLOWABLE_VALUES)
284 @RequestParam(value = "type", required = false) String typeStr, 340 @RequestParam(value = "type", required = false) String typeStr,
  341 + @ApiParam(value = RULE_CHAIN_TEXT_SEARCH_DESCRIPTION)
285 @RequestParam(required = false) String textSearch, 342 @RequestParam(required = false) String textSearch,
  343 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES)
286 @RequestParam(required = false) String sortProperty, 344 @RequestParam(required = false) String sortProperty,
  345 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
287 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 346 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
288 try { 347 try {
289 TenantId tenantId = getCurrentUser().getTenantId(); 348 TenantId tenantId = getCurrentUser().getTenantId();
@@ -298,10 +357,14 @@ public class RuleChainController extends BaseController { @@ -298,10 +357,14 @@ public class RuleChainController extends BaseController {
298 } 357 }
299 } 358 }
300 359
  360 + @ApiOperation(value = "Delete rule chain (deleteRuleChain)",
  361 + notes = "Deletes the rule chain. Referencing non-existing rule chain Id will cause an error. Referencing rule chain that is used in the device profiles will cause an error.")
301 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 362 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
302 @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.DELETE) 363 @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
303 @ResponseStatus(value = HttpStatus.OK) 364 @ResponseStatus(value = HttpStatus.OK)
304 - public void deleteRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { 365 + public void deleteRuleChain(
  366 + @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
  367 + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
305 checkParameter(RULE_CHAIN_ID, strRuleChainId); 368 checkParameter(RULE_CHAIN_ID, strRuleChainId);
306 try { 369 try {
307 RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); 370 RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -344,10 +407,15 @@ public class RuleChainController extends BaseController { @@ -344,10 +407,15 @@ public class RuleChainController extends BaseController {
344 } 407 }
345 } 408 }
346 409
  410 + @ApiOperation(value = "Get latest input message (getLatestRuleNodeDebugInput)",
  411 + notes = "Gets the input message from the debug events for specified Rule Chain Id. " +
  412 + "Referencing non-existing rule chain Id will cause an error. ")
347 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 413 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
348 @RequestMapping(value = "/ruleNode/{ruleNodeId}/debugIn", method = RequestMethod.GET) 414 @RequestMapping(value = "/ruleNode/{ruleNodeId}/debugIn", method = RequestMethod.GET)
349 @ResponseBody 415 @ResponseBody
350 - public JsonNode getLatestRuleNodeDebugInput(@PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException { 416 + public JsonNode getLatestRuleNodeDebugInput(
  417 + @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
  418 + @PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException {
351 checkParameter(RULE_NODE_ID, strRuleNodeId); 419 checkParameter(RULE_NODE_ID, strRuleNodeId);
352 try { 420 try {
353 RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId)); 421 RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId));
@@ -370,10 +438,15 @@ public class RuleChainController extends BaseController { @@ -370,10 +438,15 @@ public class RuleChainController extends BaseController {
370 } 438 }
371 } 439 }
372 440
  441 +
  442 + @ApiOperation(value = "Test JavaScript function",
  443 + notes = TEST_JS_FUNCTION + TENANT_AUTHORITY_PARAGRAPH)
373 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 444 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
374 @RequestMapping(value = "/ruleChain/testScript", method = RequestMethod.POST) 445 @RequestMapping(value = "/ruleChain/testScript", method = RequestMethod.POST)
375 @ResponseBody 446 @ResponseBody
376 - public JsonNode testScript(@RequestBody JsonNode inputParams) throws ThingsboardException { 447 + public JsonNode testScript(
  448 + @ApiParam(value = "Test JS request. See API call description above.")
  449 + @RequestBody JsonNode inputParams) throws ThingsboardException {
377 try { 450 try {
378 String script = inputParams.get("script").asText(); 451 String script = inputParams.get("script").asText();
379 String scriptType = inputParams.get("scriptType").asText(); 452 String scriptType = inputParams.get("scriptType").asText();
@@ -433,10 +506,13 @@ public class RuleChainController extends BaseController { @@ -433,10 +506,13 @@ public class RuleChainController extends BaseController {
433 } 506 }
434 } 507 }
435 508
  509 + @ApiOperation(value = "Export Rule Chains", notes = "Exports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH)
436 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 510 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
437 @RequestMapping(value = "/ruleChains/export", params = {"limit"}, method = RequestMethod.GET) 511 @RequestMapping(value = "/ruleChains/export", params = {"limit"}, method = RequestMethod.GET)
438 @ResponseBody 512 @ResponseBody
439 - public RuleChainData exportRuleChains(@RequestParam("limit") int limit) throws ThingsboardException { 513 + public RuleChainData exportRuleChains(
  514 + @ApiParam(value = "A limit of rule chains to export.", required = true)
  515 + @RequestParam("limit") int limit) throws ThingsboardException {
440 try { 516 try {
441 TenantId tenantId = getCurrentUser().getTenantId(); 517 TenantId tenantId = getCurrentUser().getTenantId();
442 PageLink pageLink = new PageLink(limit); 518 PageLink pageLink = new PageLink(limit);
@@ -446,10 +522,15 @@ public class RuleChainController extends BaseController { @@ -446,10 +522,15 @@ public class RuleChainController extends BaseController {
446 } 522 }
447 } 523 }
448 524
  525 + @ApiOperation(value = "Import Rule Chains", notes = "Imports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH)
449 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 526 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
450 @RequestMapping(value = "/ruleChains/import", method = RequestMethod.POST) 527 @RequestMapping(value = "/ruleChains/import", method = RequestMethod.POST)
451 @ResponseBody 528 @ResponseBody
452 - public List<RuleChainImportResult> importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException { 529 + public List<RuleChainImportResult> importRuleChains(
  530 + @ApiParam(value = "A JSON value representing the rule chains.")
  531 + @RequestBody RuleChainData ruleChainData,
  532 + @ApiParam(value = "Enables overwrite for existing rule chains with the same name.")
  533 + @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
453 try { 534 try {
454 TenantId tenantId = getCurrentUser().getTenantId(); 535 TenantId tenantId = getCurrentUser().getTenantId();
455 List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); 536 List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite);
@@ -495,6 +576,14 @@ public class RuleChainController extends BaseController { @@ -495,6 +576,14 @@ public class RuleChainController extends BaseController {
495 return msgData; 576 return msgData;
496 } 577 }
497 578
  579 + @ApiOperation(value = "Assign rule chain to edge (assignRuleChainToEdge)",
  580 + notes = "Creates assignment of an existing rule chain to an instance of The Edge. " +
  581 + EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  582 + "Second, remote edge service will receive a copy of assignment rule chain " +
  583 + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  584 + "Third, once rule chain will be delivered to edge service, it's going to start processing messages locally. " +
  585 + "\n\nOnly rule chain with type 'EDGE' can be assigned to edge.",
  586 + produces = MediaType.APPLICATION_JSON_VALUE)
498 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 587 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
499 @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST) 588 @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST)
500 @ResponseBody 589 @ResponseBody
@@ -528,6 +617,13 @@ public class RuleChainController extends BaseController { @@ -528,6 +617,13 @@ public class RuleChainController extends BaseController {
528 } 617 }
529 } 618 }
530 619
  620 + @ApiOperation(value = "Unassign rule chain from edge (unassignRuleChainFromEdge)",
  621 + notes = "Clears assignment of the rule chain to the edge. " +
  622 + EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION +
  623 + "Second, remote edge service will receive an 'unassign' command to remove rule chain " +
  624 + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + ". " +
  625 + "Third, once 'unassign' command will be delivered to edge service, it's going to remove rule chain locally.",
  626 + produces = MediaType.APPLICATION_JSON_VALUE)
531 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 627 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
532 @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE) 628 @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
533 @ResponseBody 629 @ResponseBody
@@ -26,10 +26,15 @@ import com.google.common.util.concurrent.MoreExecutors; @@ -26,10 +26,15 @@ import com.google.common.util.concurrent.MoreExecutors;
26 import com.google.gson.JsonElement; 26 import com.google.gson.JsonElement;
27 import com.google.gson.JsonParseException; 27 import com.google.gson.JsonParseException;
28 import com.google.gson.JsonParser; 28 import com.google.gson.JsonParser;
  29 +import io.swagger.annotations.ApiOperation;
  30 +import io.swagger.annotations.ApiParam;
  31 +import io.swagger.annotations.ApiResponse;
  32 +import io.swagger.annotations.ApiResponses;
29 import lombok.extern.slf4j.Slf4j; 33 import lombok.extern.slf4j.Slf4j;
30 import org.springframework.beans.factory.annotation.Autowired; 34 import org.springframework.beans.factory.annotation.Autowired;
31 import org.springframework.beans.factory.annotation.Value; 35 import org.springframework.beans.factory.annotation.Value;
32 import org.springframework.http.HttpStatus; 36 import org.springframework.http.HttpStatus;
  37 +import org.springframework.http.MediaType;
33 import org.springframework.http.ResponseEntity; 38 import org.springframework.http.ResponseEntity;
34 import org.springframework.security.access.prepost.PreAuthorize; 39 import org.springframework.security.access.prepost.PreAuthorize;
35 import org.springframework.util.StringUtils; 40 import org.springframework.util.StringUtils;
@@ -48,7 +53,6 @@ import org.thingsboard.server.common.data.EntityType; @@ -48,7 +53,6 @@ import org.thingsboard.server.common.data.EntityType;
48 import org.thingsboard.server.common.data.TenantProfile; 53 import org.thingsboard.server.common.data.TenantProfile;
49 import org.thingsboard.server.common.data.audit.ActionType; 54 import org.thingsboard.server.common.data.audit.ActionType;
50 import org.thingsboard.server.common.data.exception.ThingsboardException; 55 import org.thingsboard.server.common.data.exception.ThingsboardException;
51 -import org.thingsboard.server.common.data.id.CustomerId;  
52 import org.thingsboard.server.common.data.id.DeviceId; 56 import org.thingsboard.server.common.data.id.DeviceId;
53 import org.thingsboard.server.common.data.id.EntityId; 57 import org.thingsboard.server.common.data.id.EntityId;
54 import org.thingsboard.server.common.data.id.EntityIdFactory; 58 import org.thingsboard.server.common.data.id.EntityIdFactory;
@@ -108,6 +112,48 @@ import java.util.stream.Collectors; @@ -108,6 +112,48 @@ import java.util.stream.Collectors;
108 @Slf4j 112 @Slf4j
109 public class TelemetryController extends BaseController { 113 public class TelemetryController extends BaseController {
110 114
  115 + private static final String ATTRIBUTES_SCOPE_DESCRIPTION = "A string value representing the attributes scope. For example, 'SERVER_SCOPE'.";
  116 + private static final String ATTRIBUTES_KEYS_DESCRIPTION = "A string value representing the comma-separated list of attributes keys. For example, 'active,inactivityAlarmTime'.";
  117 + private static final String ATTRIBUTES_SCOPE_ALLOWED_VALUES = "SERVER_SCOPE, CLIENT_SCOPE, SHARED_SCOPE";
  118 + private static final String ATTRIBUTES_JSON_REQUEST_DESCRIPTION = "A string value representing the json object. For example, '{\"key\":\"value\"}'";
  119 + private static final String ATTRIBUTE_DATA_CLASS_DESCRIPTION = "AttributeData class represents information regarding a particular attribute and includes the next parameters: 'lastUpdatesTs' - a long value representing the timestamp of the last attribute modification in milliseconds. 'key' - attribute key name, and 'value' - attribute value.";
  120 + private static final String GET_ALL_ATTRIBUTES_BASE_DESCRIPTION = "Returns a JSON structure that represents a list of AttributeData class objects for the selected entity based on the specified comma-separated list of attribute key names. " + ATTRIBUTE_DATA_CLASS_DESCRIPTION;
  121 + private static final String GET_ALL_ATTRIBUTES_BY_SCOPE_BASE_DESCRIPTION = "Returns a JSON structure that represents a list of AttributeData class objects for the selected entity based on the attributes scope selected and a comma-separated list of attribute key names. " + ATTRIBUTE_DATA_CLASS_DESCRIPTION;
  122 +
  123 + private static final String TS_DATA_CLASS_DESCRIPTION = "TsData class is a timeseries data point for specific telemetry key that includes 'value' - object value, and 'ts' - a long value representing timestamp in milliseconds for this value. ";
  124 +
  125 + private static final String TELEMETRY_KEYS_BASE_DESCRIPTION = "A string value representing the comma-separated list of telemetry keys.";
  126 + private static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest timeseries. For example, 'temp,humidity'.";
  127 + private static final String TELEMETRY_SCOPE_DESCRIPTION = "Value is not used in the API call implementation. However, you need to specify whatever value cause scope is a path variable.";
  128 + private static final String TELEMETRY_JSON_REQUEST_DESCRIPTION = "A string value representing the json object. For example, '{\"key\":\"value\"}' or '{\"ts\":1527863043000,\"values\":{\"key1\":\"value1\",\"key2\":\"value2\"}}' or [{\"ts\":1527863043000,\"values\":{\"key1\":\"value1\",\"key2\":\"value2\"}}, {\"ts\":1527863053000,\"values\":{\"key1\":\"value3\",\"key2\":\"value4\"}}]";
  129 +
  130 +
  131 + private static final String STRICT_DATA_TYPES_DESCRIPTION = "A boolean value to specify if values of selected telemetry keys will represent string values(by default) or use strict data type.";
  132 + private static final String INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION = "Referencing a non-existing entity Id or invalid entity type will cause an error. ";
  133 +
  134 + private static final String SAVE_ENTITY_ATTRIBUTES_DESCRIPTION = "Creates or updates the entity attributes based on entity id, entity type, specified attributes scope " +
  135 + "and request payload that represents a JSON object with key-value format of attributes to create or update. " +
  136 + "For example, '{\"temperature\": 26}'. Key is a unique parameter and cannot be overwritten. Only value can be overwritten for the key. ";
  137 + private static final String SAVE_ATTIRIBUTES_STATUS_OK = "Attribute from the request was created or updated. ";
  138 + private static final String INVALID_STRUCTURE_OF_THE_REQUEST = "Invalid structure of the request";
  139 + private static final String SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST = INVALID_STRUCTURE_OF_THE_REQUEST + " or invalid attributes scope provided.";
  140 + private static final String SAVE_ENTITY_ATTRIBUTES_STATUS_OK = "Platform creates an audit log event about entity attributes updates with action type 'ATTRIBUTES_UPDATED', " +
  141 + "and also sends event msg to the rule engine with msg type 'ATTRIBUTES_UPDATED'.";
  142 + private static final String SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED = "User is not authorized to save entity attributes for selected entity. Most likely, User belongs to different Customer or Tenant.";
  143 + private static final String SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR = "The exception was thrown during processing the request. " +
  144 + "Platform creates an audit log event about entity attributes updates with action type 'ATTRIBUTES_UPDATED' that includes an error stacktrace.";
  145 + private static final String SAVE_ENTITY_TIMESERIES_DESCRIPTION = "Creates or updates the entity timeseries based on entity id, entity type " +
  146 + "and request payload that represents a JSON object with key-value or ts-values format. " +
  147 + "For example, '{\"temperature\": 26}' or '{\"ts\":1634712287000,\"values\":{\"temperature\":26, \"humidity\":87}}', " +
  148 + "or JSON array with inner objects inside of ts-values format. " +
  149 + "For example, '[{\"ts\":1634712287000,\"values\":{\"temperature\":26, \"humidity\":87}}, {\"ts\":1634712588000,\"values\":{\"temperature\":25, \"humidity\":88}}]'. " +
  150 + "The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. ";
  151 + private static final String SAVE_ENTITY_TIMESERIES_STATUS_OK = "Timeseries from the request was created or updated. " +
  152 + "Platform creates an audit log event about entity timeseries updates with action type 'TIMESERIES_UPDATED'.";
  153 + private static final String SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED = "User is not authorized to save entity timeseries for selected entity. Most likely, User belongs to different Customer or Tenant.";
  154 + private static final String SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR = "The exception was thrown during processing the request. " +
  155 + "Platform creates an audit log event about entity timeseries updates with action type 'TIMESERIES_UPDATED' that includes an error stacktrace.";
  156 +
111 @Autowired 157 @Autowired
112 private TimeseriesService tsService; 158 private TimeseriesService tsService;
113 159
@@ -133,83 +179,132 @@ public class TelemetryController extends BaseController { @@ -133,83 +179,132 @@ public class TelemetryController extends BaseController {
133 } 179 }
134 } 180 }
135 181
  182 + @ApiOperation(value = "Get all attribute keys (getAttributeKeys)",
  183 + notes = "Returns a list of all attribute key names for the selected entity. " +
  184 + "In the case of device entity specified, a response will include merged attribute key names list from each scope: " +
  185 + "SERVER_SCOPE, CLIENT_SCOPE, SHARED_SCOPE. " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  186 + produces = MediaType.APPLICATION_JSON_VALUE)
136 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 187 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
137 @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes", method = RequestMethod.GET) 188 @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes", method = RequestMethod.GET)
138 @ResponseBody 189 @ResponseBody
139 public DeferredResult<ResponseEntity> getAttributeKeys( 190 public DeferredResult<ResponseEntity> getAttributeKeys(
140 - @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { 191 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  192 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
141 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); 193 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback);
142 } 194 }
143 195
  196 + @ApiOperation(value = "Get all attributes keys by scope (getAttributeKeysByScope)",
  197 + notes = "Returns a list of attribute key names from the specified attributes scope for the selected entity. " +
  198 + "If scope parameter is omitted, Get all attribute keys(getAttributeKeys) API will be called. " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  199 + produces = MediaType.APPLICATION_JSON_VALUE)
144 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 200 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
145 @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}", method = RequestMethod.GET) 201 @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}", method = RequestMethod.GET)
146 @ResponseBody 202 @ResponseBody
147 public DeferredResult<ResponseEntity> getAttributeKeysByScope( 203 public DeferredResult<ResponseEntity> getAttributeKeysByScope(
148 - @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr  
149 - , @PathVariable("scope") String scope) throws ThingsboardException { 204 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  205 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  206 + @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException {
150 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, 207 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
151 (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); 208 (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope));
152 } 209 }
153 210
  211 + @ApiOperation(value = "Get attributes (getAttributes)",
  212 + notes = GET_ALL_ATTRIBUTES_BASE_DESCRIPTION + " If 'keys' parameter is omitted, AttributeData class objects will be added to the response for all existing keys of the selected entity. " +
  213 + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  214 + produces = MediaType.APPLICATION_JSON_VALUE)
154 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 215 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
155 @RequestMapping(value = "/{entityType}/{entityId}/values/attributes", method = RequestMethod.GET) 216 @RequestMapping(value = "/{entityType}/{entityId}/values/attributes", method = RequestMethod.GET)
156 @ResponseBody 217 @ResponseBody
157 public DeferredResult<ResponseEntity> getAttributes( 218 public DeferredResult<ResponseEntity> getAttributes(
158 - @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
159 - @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { 219 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  220 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  221 + @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
160 SecurityUser user = getCurrentUser(); 222 SecurityUser user = getCurrentUser();
161 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, 223 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
162 (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); 224 (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr));
163 } 225 }
164 226
  227 + @ApiOperation(value = "Get attributes by scope (getAttributesByScope)",
  228 + notes = GET_ALL_ATTRIBUTES_BY_SCOPE_BASE_DESCRIPTION + " In case that 'keys' parameter is not selected, " +
  229 + "AttributeData class objects will be added to the response for all existing attribute keys from the " +
  230 + "specified attributes scope of the selected entity. If 'scope' parameter is omitted, " +
  231 + "Get attributes (getAttributes) API will be called. " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  232 + produces = MediaType.APPLICATION_JSON_VALUE)
165 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 233 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
166 @RequestMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}", method = RequestMethod.GET) 234 @RequestMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}", method = RequestMethod.GET)
167 @ResponseBody 235 @ResponseBody
168 public DeferredResult<ResponseEntity> getAttributesByScope( 236 public DeferredResult<ResponseEntity> getAttributesByScope(
169 - @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
170 - @PathVariable("scope") String scope,  
171 - @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { 237 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  238 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  239 + @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
  240 + @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
172 SecurityUser user = getCurrentUser(); 241 SecurityUser user = getCurrentUser();
173 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, 242 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
174 (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); 243 (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
175 } 244 }
176 245
  246 + @ApiOperation(value = "Get timeseries keys (getTimeseriesKeys)",
  247 + notes = "Returns a list of all telemetry key names for the selected entity based on entity id and entity type specified. " +
  248 + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  249 + produces = MediaType.APPLICATION_JSON_VALUE)
177 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 250 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
178 @RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET) 251 @RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET)
179 @ResponseBody 252 @ResponseBody
180 public DeferredResult<ResponseEntity> getTimeseriesKeys( 253 public DeferredResult<ResponseEntity> getTimeseriesKeys(
181 - @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { 254 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  255 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
182 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, 256 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
183 (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); 257 (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor()));
184 } 258 }
185 259
  260 + @ApiOperation(value = "Get latest timeseries (getLatestTimeseries)",
  261 + notes = "Returns a JSON structure that represents a Map, where the map key is a telemetry key name " +
  262 + "and map value - is a singleton list of TsData class objects. " + TS_DATA_CLASS_DESCRIPTION + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  263 + produces = MediaType.APPLICATION_JSON_VALUE)
186 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 264 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
187 @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET) 265 @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET)
188 @ResponseBody 266 @ResponseBody
189 public DeferredResult<ResponseEntity> getLatestTimeseries( 267 public DeferredResult<ResponseEntity> getLatestTimeseries(
190 - @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
191 - @RequestParam(name = "keys", required = false) String keysStr, 268 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  269 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  270 + @ApiParam(value = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
  271 + @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION)
192 @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { 272 @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
193 SecurityUser user = getCurrentUser(); 273 SecurityUser user = getCurrentUser();
194 -  
195 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, 274 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
196 (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); 275 (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes));
197 } 276 }
198 277
199 - 278 + @ApiOperation(value = "Get timeseries (getTimeseries)",
  279 + notes = "Returns a JSON structure that represents a Map, where the map key is a telemetry key name " +
  280 + "and map value - is a list of TsData class objects. " + TS_DATA_CLASS_DESCRIPTION +
  281 + "This method allows us to group original data into intervals and aggregate it using one of the aggregation methods or just limit the number of TsData objects to fetch for each key specified. " +
  282 + "See the desription of the request parameters for more details. " +
  283 + "The result can also be sorted in ascending or descending order. " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  284 + produces = MediaType.APPLICATION_JSON_VALUE)
200 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 285 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
201 @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"}) 286 @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"})
202 @ResponseBody 287 @ResponseBody
203 public DeferredResult<ResponseEntity> getTimeseries( 288 public DeferredResult<ResponseEntity> getTimeseries(
204 - @PathVariable("entityType") String entityType,  
205 - @PathVariable("entityId") String entityIdStr,  
206 - @RequestParam(name = "keys") String keys, 289 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  290 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  291 + @ApiParam(value = TELEMETRY_KEYS_BASE_DESCRIPTION) @RequestParam(name = "keys") String keys,
  292 + @ApiParam(value = "A long value representing the start timestamp of search time range in milliseconds.")
207 @RequestParam(name = "startTs") Long startTs, 293 @RequestParam(name = "startTs") Long startTs,
  294 + @ApiParam(value = "A long value representing the end timestamp of search time range in milliseconds.")
208 @RequestParam(name = "endTs") Long endTs, 295 @RequestParam(name = "endTs") Long endTs,
  296 + @ApiParam(value = "A long value representing the aggregation interval range in milliseconds.")
209 @RequestParam(name = "interval", defaultValue = "0") Long interval, 297 @RequestParam(name = "interval", defaultValue = "0") Long interval,
  298 + @ApiParam(value = "An integer value that represents a max number of timeseries data points to fetch." +
  299 + " This parameter is used only in the case if 'agg' parameter is set to 'NONE'.", defaultValue = "100")
210 @RequestParam(name = "limit", defaultValue = "100") Integer limit, 300 @RequestParam(name = "limit", defaultValue = "100") Integer limit,
  301 + @ApiParam(value = "A string value representing the aggregation function. " +
  302 + "If the interval is not specified, 'agg' parameter will use 'NONE' value.",
  303 + allowableValues = "MIN, MAX, AVG, SUM, COUNT, NONE")
211 @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, 304 @RequestParam(name = "agg", defaultValue = "NONE") String aggStr,
  305 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
212 @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, 306 @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy,
  307 + @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION)
213 @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { 308 @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
214 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, 309 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
215 (result, tenantId, entityId) -> { 310 (result, tenantId, entityId) -> {
@@ -222,64 +317,145 @@ public class TelemetryController extends BaseController { @@ -222,64 +317,145 @@ public class TelemetryController extends BaseController {
222 }); 317 });
223 } 318 }
224 319
  320 + @ApiOperation(value = "Save or update device attributes (saveDeviceAttributes)",
  321 + notes = "Creates or updates the device attributes based on device id, specified attribute scope, " +
  322 + "and request payload that represents a JSON object with key-value format of attributes to create or update. " +
  323 + "For example, '{\"temperature\": 26}'. Key is a unique parameter and cannot be overwritten. Only value can " +
  324 + "be overwritten for the key. ",
  325 + produces = MediaType.APPLICATION_JSON_VALUE)
  326 + @ApiResponses(value = {
  327 + @ApiResponse(code = 200, message = SAVE_ATTIRIBUTES_STATUS_OK +
  328 + "Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED', " +
  329 + "and also sends event msg to the rule engine with msg type 'ATTRIBUTES_UPDATED'."),
  330 + @ApiResponse(code = 400, message = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
  331 + @ApiResponse(code = 401, message = "User is not authorized to save device attributes for selected device. Most likely, User belongs to different Customer or Tenant."),
  332 + @ApiResponse(code = 500, message = "The exception was thrown during processing the request. " +
  333 + "Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED' that includes an error stacktrace."),
  334 + })
225 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 335 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
226 @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.POST) 336 @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.POST)
227 @ResponseBody 337 @ResponseBody
228 - public DeferredResult<ResponseEntity> saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope,  
229 - @RequestBody JsonNode request) throws ThingsboardException { 338 + public DeferredResult<ResponseEntity> saveDeviceAttributes(
  339 + @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION) @PathVariable("deviceId") String deviceIdStr,
  340 + @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
  341 + @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION) @RequestBody JsonNode request) throws ThingsboardException {
230 EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); 342 EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
231 return saveAttributes(getTenantId(), entityId, scope, request); 343 return saveAttributes(getTenantId(), entityId, scope, request);
232 } 344 }
233 345
  346 + @ApiOperation(value = "Save or update attributes (saveEntityAttributesV1)",
  347 + notes = SAVE_ENTITY_ATTRIBUTES_DESCRIPTION + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  348 + produces = MediaType.APPLICATION_JSON_VALUE)
  349 + @ApiResponses(value = {
  350 + @ApiResponse(code = 200, message = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK),
  351 + @ApiResponse(code = 400, message = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
  352 + @ApiResponse(code = 401, message = SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED),
  353 + @ApiResponse(code = 500, message = SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR),
  354 + })
234 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 355 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
235 @RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.POST) 356 @RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.POST)
236 @ResponseBody 357 @ResponseBody
237 - public DeferredResult<ResponseEntity> saveEntityAttributesV1(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
238 - @PathVariable("scope") String scope,  
239 - @RequestBody JsonNode request) throws ThingsboardException { 358 + public DeferredResult<ResponseEntity> saveEntityAttributesV1(
  359 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  360 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  361 + @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
  362 + @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION) @RequestBody JsonNode request) throws ThingsboardException {
240 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); 363 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
241 return saveAttributes(getTenantId(), entityId, scope, request); 364 return saveAttributes(getTenantId(), entityId, scope, request);
242 } 365 }
243 366
  367 + @ApiOperation(value = "Save or update attributes (saveEntityAttributesV2)",
  368 + notes = SAVE_ENTITY_ATTRIBUTES_DESCRIPTION + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  369 + produces = MediaType.APPLICATION_JSON_VALUE)
  370 + @ApiResponses(value = {
  371 + @ApiResponse(code = 200, message = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK),
  372 + @ApiResponse(code = 400, message = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
  373 + @ApiResponse(code = 401, message = SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED),
  374 + @ApiResponse(code = 500, message = SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR),
  375 + })
244 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 376 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
245 @RequestMapping(value = "/{entityType}/{entityId}/attributes/{scope}", method = RequestMethod.POST) 377 @RequestMapping(value = "/{entityType}/{entityId}/attributes/{scope}", method = RequestMethod.POST)
246 @ResponseBody 378 @ResponseBody
247 - public DeferredResult<ResponseEntity> saveEntityAttributesV2(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
248 - @PathVariable("scope") String scope,  
249 - @RequestBody JsonNode request) throws ThingsboardException { 379 + public DeferredResult<ResponseEntity> saveEntityAttributesV2(
  380 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  381 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  382 + @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
  383 + @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION) @RequestBody JsonNode request) throws ThingsboardException {
250 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); 384 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
251 return saveAttributes(getTenantId(), entityId, scope, request); 385 return saveAttributes(getTenantId(), entityId, scope, request);
252 } 386 }
253 387
  388 + @ApiOperation(value = "Save or update telemetry (saveEntityTelemetry)",
  389 + notes = SAVE_ENTITY_TIMESERIES_DESCRIPTION + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  390 + produces = MediaType.APPLICATION_JSON_VALUE)
  391 + @ApiResponses(value = {
  392 + @ApiResponse(code = 200, message = SAVE_ENTITY_TIMESERIES_STATUS_OK),
  393 + @ApiResponse(code = 400, message = INVALID_STRUCTURE_OF_THE_REQUEST),
  394 + @ApiResponse(code = 401, message = SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED),
  395 + @ApiResponse(code = 500, message = SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR),
  396 + })
254 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 397 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
255 @RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}", method = RequestMethod.POST) 398 @RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}", method = RequestMethod.POST)
256 @ResponseBody 399 @ResponseBody
257 - public DeferredResult<ResponseEntity> saveEntityTelemetry(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
258 - @PathVariable("scope") String scope,  
259 - @RequestBody String requestBody) throws ThingsboardException { 400 + public DeferredResult<ResponseEntity> saveEntityTelemetry(
  401 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  402 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  403 + @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION) @PathVariable("scope") String scope,
  404 + @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION) @RequestBody String requestBody) throws ThingsboardException {
260 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); 405 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
261 return saveTelemetry(getTenantId(), entityId, requestBody, 0L); 406 return saveTelemetry(getTenantId(), entityId, requestBody, 0L);
262 } 407 }
263 408
  409 + @ApiOperation(value = "Save or update telemetry with TTL (saveEntityTelemetryWithTTL)",
  410 + notes = SAVE_ENTITY_TIMESERIES_DESCRIPTION + "The ttl parameter used only in case of Cassandra DB use for timeseries data storage. " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  411 + produces = MediaType.APPLICATION_JSON_VALUE)
  412 + @ApiResponses(value = {
  413 + @ApiResponse(code = 200, message = SAVE_ENTITY_TIMESERIES_STATUS_OK),
  414 + @ApiResponse(code = 400, message = INVALID_STRUCTURE_OF_THE_REQUEST),
  415 + @ApiResponse(code = 401, message = SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED),
  416 + @ApiResponse(code = 500, message = SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR),
  417 + })
264 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 418 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
265 @RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}/{ttl}", method = RequestMethod.POST) 419 @RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}/{ttl}", method = RequestMethod.POST)
266 @ResponseBody 420 @ResponseBody
267 - public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
268 - @PathVariable("scope") String scope, @PathVariable("ttl") Long ttl,  
269 - @RequestBody String requestBody) throws ThingsboardException { 421 + public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL(
  422 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  423 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  424 + @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION) @PathVariable("scope") String scope,
  425 + @ApiParam(value = "A long value representing TTL (Time to Live) parameter.") @PathVariable("ttl") Long ttl,
  426 + @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION) @RequestBody String requestBody) throws ThingsboardException {
270 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); 427 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
271 return saveTelemetry(getTenantId(), entityId, requestBody, ttl); 428 return saveTelemetry(getTenantId(), entityId, requestBody, ttl);
272 } 429 }
273 430
  431 + @ApiOperation(value = "Delete entity timeseries (deleteEntityTimeseries)",
  432 + notes = "Delete timeseries for selected entity based on entity id, entity type, keys " +
  433 + "and removal time range. To delete all data for keys parameter 'deleteAllDataForKeys' should be set to true, " +
  434 + "otherwise, will be deleted data that is in range of the selected time interval. ",
  435 + produces = MediaType.APPLICATION_JSON_VALUE)
  436 + @ApiResponses(value = {
  437 + @ApiResponse(code = 200, message = "Timeseries for the selected keys in the request was removed. " +
  438 + "Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED'."),
  439 + @ApiResponse(code = 400, message = "Platform returns a bad request in case if keys list is empty or start and end timestamp values is empty when deleteAllDataForKeys is set to false."),
  440 + @ApiResponse(code = 401, message = "User is not authorized to delete entity timeseries for selected entity. Most likely, User belongs to different Customer or Tenant."),
  441 + @ApiResponse(code = 500, message = "The exception was thrown during processing the request. " +
  442 + "Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."),
  443 + })
274 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 444 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
275 @RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE) 445 @RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE)
276 @ResponseBody 446 @ResponseBody
277 - public DeferredResult<ResponseEntity> deleteEntityTimeseries(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
278 - @RequestParam(name = "keys") String keysStr,  
279 - @RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys,  
280 - @RequestParam(name = "startTs", required = false) Long startTs,  
281 - @RequestParam(name = "endTs", required = false) Long endTs,  
282 - @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { 447 + public DeferredResult<ResponseEntity> deleteEntityTimeseries(
  448 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  449 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  450 + @ApiParam(value = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys") String keysStr,
  451 + @ApiParam(value = "A boolean value to specify if should be deleted all data for selected keys or only data that are in the selected time range.")
  452 + @RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys,
  453 + @ApiParam(value = "A long value representing the start timestamp of removal time range in milliseconds.")
  454 + @RequestParam(name = "startTs", required = false) Long startTs,
  455 + @ApiParam(value = "A long value representing the end timestamp of removal time range in milliseconds.")
  456 + @RequestParam(name = "endTs", required = false) Long endTs,
  457 + @ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.")
  458 + @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException {
283 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); 459 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
284 return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); 460 return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted);
285 } 461 }
@@ -311,7 +487,6 @@ public class TelemetryController extends BaseController { @@ -311,7 +487,6 @@ public class TelemetryController extends BaseController {
311 for (String key : keys) { 487 for (String key : keys) {
312 deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted)); 488 deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted));
313 } 489 }
314 -  
315 ListenableFuture<List<Void>> future = tsService.remove(user.getTenantId(), entityId, deleteTsKvQueries); 490 ListenableFuture<List<Void>> future = tsService.remove(user.getTenantId(), entityId, deleteTsKvQueries);
316 Futures.addCallback(future, new FutureCallback<List<Void>>() { 491 Futures.addCallback(future, new FutureCallback<List<Void>>() {
317 @Override 492 @Override
@@ -329,22 +504,49 @@ public class TelemetryController extends BaseController { @@ -329,22 +504,49 @@ public class TelemetryController extends BaseController {
329 }); 504 });
330 } 505 }
331 506
  507 + @ApiOperation(value = "Delete device attributes (deleteEntityAttributes)",
  508 + notes = "Delete device attributes from the specified attributes scope based on device id and a list of keys to delete. " +
  509 + "Selected keys will be deleted only if there are exist in the specified attribute scope. Referencing a non-existing device Id will cause an error",
  510 + produces = MediaType.APPLICATION_JSON_VALUE)
  511 + @ApiResponses(value = {
  512 + @ApiResponse(code = 200, message = "Device attributes was removed for the selected keys in the request. " +
  513 + "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED'."),
  514 + @ApiResponse(code = 400, message = "Platform returns a bad request in case if keys or scope are not specified."),
  515 + @ApiResponse(code = 401, message = "User is not authorized to delete device attributes for selected entity. Most likely, User belongs to different Customer or Tenant."),
  516 + @ApiResponse(code = 500, message = "The exception was thrown during processing the request. " +
  517 + "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."),
  518 + })
332 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 519 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
333 @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) 520 @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE)
334 @ResponseBody 521 @ResponseBody
335 - public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr,  
336 - @PathVariable("scope") String scope,  
337 - @RequestParam(name = "keys") String keysStr) throws ThingsboardException { 522 + public DeferredResult<ResponseEntity> deleteEntityAttributes(
  523 + @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION) @PathVariable("deviceId") String deviceIdStr,
  524 + @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
  525 + @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys") String keysStr) throws ThingsboardException {
338 EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); 526 EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
339 return deleteAttributes(entityId, scope, keysStr); 527 return deleteAttributes(entityId, scope, keysStr);
340 } 528 }
341 529
  530 + @ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)",
  531 + notes = "Delete entity attributes from the specified attributes scope based on entity id, entity type and a list of keys to delete. " +
  532 + "Selected keys will be deleted only if there are exist in the specified attribute scope." + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION,
  533 + produces = MediaType.APPLICATION_JSON_VALUE)
  534 + @ApiResponses(value = {
  535 + @ApiResponse(code = 200, message = "Entity attributes was removed for the selected keys in the request. " +
  536 + "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED'."),
  537 + @ApiResponse(code = 400, message = "Platform returns a bad request in case if keys or scope are not specified."),
  538 + @ApiResponse(code = 401, message = "User is not authorized to delete entity attributes for selected entity. Most likely, User belongs to different Customer or Tenant."),
  539 + @ApiResponse(code = 500, message = "The exception was thrown during processing the request. " +
  540 + "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."),
  541 + })
342 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 542 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
343 @RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.DELETE) 543 @RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.DELETE)
344 @ResponseBody 544 @ResponseBody
345 - public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,  
346 - @PathVariable("scope") String scope,  
347 - @RequestParam(name = "keys") String keysStr) throws ThingsboardException { 545 + public DeferredResult<ResponseEntity> deleteEntityAttributes(
  546 + @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION) @PathVariable("entityType") String entityType,
  547 + @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION) @PathVariable("entityId") String entityIdStr,
  548 + @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
  549 + @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys") String keysStr) throws ThingsboardException {
348 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); 550 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
349 return deleteAttributes(entityId, scope, keysStr); 551 return deleteAttributes(entityId, scope, keysStr);
350 } 552 }
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
18 import com.fasterxml.jackson.databind.node.ObjectNode; 18 import com.fasterxml.jackson.databind.node.ObjectNode;
  19 +import io.swagger.annotations.ApiOperation;
  20 +import io.swagger.annotations.ApiParam;
19 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
20 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
21 import org.springframework.http.HttpStatus; 23 import org.springframework.http.HttpStatus;
@@ -47,21 +49,26 @@ import org.thingsboard.server.service.security.permission.Resource; @@ -47,21 +49,26 @@ import org.thingsboard.server.service.security.permission.Resource;
47 @Slf4j 49 @Slf4j
48 public class TenantController extends BaseController { 50 public class TenantController extends BaseController {
49 51
  52 + private static final String TENANT_INFO_DESCRIPTION = "The Tenant Info object extends regular Tenant object and includes Tenant Profile name. ";
50 @Autowired 53 @Autowired
51 private InstallScripts installScripts; 54 private InstallScripts installScripts;
52 55
53 @Autowired 56 @Autowired
54 private TenantService tenantService; 57 private TenantService tenantService;
55 58
  59 + @ApiOperation(value = "Get Tenant (getTenantById)",
  60 + notes = "Fetch the Tenant object based on the provided Tenant Id. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
56 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 61 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
57 @RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.GET) 62 @RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.GET)
58 @ResponseBody 63 @ResponseBody
59 - public Tenant getTenantById(@PathVariable("tenantId") String strTenantId) throws ThingsboardException { 64 + public Tenant getTenantById(
  65 + @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION)
  66 + @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException {
60 checkParameter(TENANT_ID, strTenantId); 67 checkParameter(TENANT_ID, strTenantId);
61 try { 68 try {
62 TenantId tenantId = new TenantId(toUUID(strTenantId)); 69 TenantId tenantId = new TenantId(toUUID(strTenantId));
63 Tenant tenant = checkTenantId(tenantId, Operation.READ); 70 Tenant tenant = checkTenantId(tenantId, Operation.READ);
64 - if(!tenant.getAdditionalInfo().isNull()) { 71 + if (!tenant.getAdditionalInfo().isNull()) {
65 processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD); 72 processDashboardIdFromAdditionalInfo((ObjectNode) tenant.getAdditionalInfo(), HOME_DASHBOARD);
66 } 73 }
67 return tenant; 74 return tenant;
@@ -70,10 +77,15 @@ public class TenantController extends BaseController { @@ -70,10 +77,15 @@ public class TenantController extends BaseController {
70 } 77 }
71 } 78 }
72 79
  80 + @ApiOperation(value = "Get Tenant Info (getTenantInfoById)",
  81 + notes = "Fetch the Tenant Info object based on the provided Tenant Id. " +
  82 + TENANT_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
73 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 83 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
74 @RequestMapping(value = "/tenant/info/{tenantId}", method = RequestMethod.GET) 84 @RequestMapping(value = "/tenant/info/{tenantId}", method = RequestMethod.GET)
75 @ResponseBody 85 @ResponseBody
76 - public TenantInfo getTenantInfoById(@PathVariable("tenantId") String strTenantId) throws ThingsboardException { 86 + public TenantInfo getTenantInfoById(
  87 + @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION)
  88 + @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException {
77 checkParameter(TENANT_ID, strTenantId); 89 checkParameter(TENANT_ID, strTenantId);
78 try { 90 try {
79 TenantId tenantId = new TenantId(toUUID(strTenantId)); 91 TenantId tenantId = new TenantId(toUUID(strTenantId));
@@ -83,10 +95,19 @@ public class TenantController extends BaseController { @@ -83,10 +95,19 @@ public class TenantController extends BaseController {
83 } 95 }
84 } 96 }
85 97
  98 + @ApiOperation(value = "Create Or update Tenant (saveTenant)",
  99 + notes = "Create or update the Tenant. When creating tenant, platform generates Tenant Id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address). " +
  100 + "Default Rule Chain and Device profile are also generated for the new tenants automatically. " +
  101 + "The newly created Tenant Id will be present in the response. " +
  102 + "Specify existing Tenant Id id to update the Tenant. " +
  103 + "Referencing non-existing Tenant Id will cause 'Not Found' error." +
  104 + SYSTEM_AUTHORITY_PARAGRAPH)
86 @PreAuthorize("hasAuthority('SYS_ADMIN')") 105 @PreAuthorize("hasAuthority('SYS_ADMIN')")
87 @RequestMapping(value = "/tenant", method = RequestMethod.POST) 106 @RequestMapping(value = "/tenant", method = RequestMethod.POST)
88 @ResponseBody 107 @ResponseBody
89 - public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException { 108 + public Tenant saveTenant(
  109 + @ApiParam(value = "A JSON value representing the tenant.")
  110 + @RequestBody Tenant tenant) throws ThingsboardException {
90 try { 111 try {
91 boolean newTenant = tenant.getId() == null; 112 boolean newTenant = tenant.getId() == null;
92 113
@@ -107,11 +128,15 @@ public class TenantController extends BaseController { @@ -107,11 +128,15 @@ public class TenantController extends BaseController {
107 } 128 }
108 } 129 }
109 130
  131 + @ApiOperation(value = "Delete Tenant (deleteTenant)",
  132 + notes = "Deletes the tenant, it's customers, rule chains, devices and all other related entities. Referencing non-existing tenant Id will cause an error." + SYSTEM_AUTHORITY_PARAGRAPH)
110 @PreAuthorize("hasAuthority('SYS_ADMIN')") 133 @PreAuthorize("hasAuthority('SYS_ADMIN')")
111 @RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.DELETE) 134 @RequestMapping(value = "/tenant/{tenantId}", method = RequestMethod.DELETE)
112 @ResponseStatus(value = HttpStatus.OK) 135 @ResponseStatus(value = HttpStatus.OK)
113 - public void deleteTenant(@PathVariable("tenantId") String strTenantId) throws ThingsboardException {  
114 - checkParameter("tenantId", strTenantId); 136 + public void deleteTenant(
  137 + @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION)
  138 + @PathVariable(TENANT_ID) String strTenantId) throws ThingsboardException {
  139 + checkParameter(TENANT_ID, strTenantId);
115 try { 140 try {
116 TenantId tenantId = new TenantId(toUUID(strTenantId)); 141 TenantId tenantId = new TenantId(toUUID(strTenantId));
117 Tenant tenant = checkTenantId(tenantId, Operation.DELETE); 142 Tenant tenant = checkTenantId(tenantId, Operation.DELETE);
@@ -124,14 +149,21 @@ public class TenantController extends BaseController { @@ -124,14 +149,21 @@ public class TenantController extends BaseController {
124 } 149 }
125 } 150 }
126 151
  152 + @ApiOperation(value = "Get Tenants (getTenants)", notes = "Returns a page of tenants registered in the platform. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
127 @PreAuthorize("hasAuthority('SYS_ADMIN')") 153 @PreAuthorize("hasAuthority('SYS_ADMIN')")
128 @RequestMapping(value = "/tenants", params = {"pageSize", "page"}, method = RequestMethod.GET) 154 @RequestMapping(value = "/tenants", params = {"pageSize", "page"}, method = RequestMethod.GET)
129 @ResponseBody 155 @ResponseBody
130 - public PageData<Tenant> getTenants(@RequestParam int pageSize,  
131 - @RequestParam int page,  
132 - @RequestParam(required = false) String textSearch,  
133 - @RequestParam(required = false) String sortProperty,  
134 - @RequestParam(required = false) String sortOrder) throws ThingsboardException { 156 + public PageData<Tenant> getTenants(
  157 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
  158 + @RequestParam int pageSize,
  159 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
  160 + @RequestParam int page,
  161 + @ApiParam(value = TENANT_TEXT_SEARCH_DESCRIPTION)
  162 + @RequestParam(required = false) String textSearch,
  163 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = TENANT_SORT_PROPERTY_ALLOWABLE_VALUES)
  164 + @RequestParam(required = false) String sortProperty,
  165 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
  166 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
135 try { 167 try {
136 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 168 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
137 return checkNotNull(tenantService.findTenants(pageLink)); 169 return checkNotNull(tenantService.findTenants(pageLink));
@@ -140,14 +172,23 @@ public class TenantController extends BaseController { @@ -140,14 +172,23 @@ public class TenantController extends BaseController {
140 } 172 }
141 } 173 }
142 174
  175 + @ApiOperation(value = "Get Tenants Info (getTenants)", notes = "Returns a page of tenant info objects registered in the platform. "
  176 + + TENANT_INFO_DESCRIPTION + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
143 @PreAuthorize("hasAuthority('SYS_ADMIN')") 177 @PreAuthorize("hasAuthority('SYS_ADMIN')")
144 @RequestMapping(value = "/tenantInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) 178 @RequestMapping(value = "/tenantInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
145 @ResponseBody 179 @ResponseBody
146 - public PageData<TenantInfo> getTenantInfos(@RequestParam int pageSize,  
147 - @RequestParam int page,  
148 - @RequestParam(required = false) String textSearch,  
149 - @RequestParam(required = false) String sortProperty,  
150 - @RequestParam(required = false) String sortOrder) throws ThingsboardException { 180 + public PageData<TenantInfo> getTenantInfos(
  181 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
  182 + @RequestParam int pageSize,
  183 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
  184 + @RequestParam int page,
  185 + @ApiParam(value = TENANT_TEXT_SEARCH_DESCRIPTION)
  186 + @RequestParam(required = false) String textSearch,
  187 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = TENANT_INFO_SORT_PROPERTY_ALLOWABLE_VALUES)
  188 + @RequestParam(required = false) String sortProperty,
  189 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
  190 + @RequestParam(required = false) String sortOrder
  191 + ) throws ThingsboardException {
151 try { 192 try {
152 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 193 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
153 return checkNotNull(tenantService.findTenantInfos(pageLink)); 194 return checkNotNull(tenantService.findTenantInfos(pageLink));
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.http.HttpStatus; 21 import org.springframework.http.HttpStatus;
20 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
@@ -44,10 +46,16 @@ import org.thingsboard.server.service.security.permission.Resource; @@ -44,10 +46,16 @@ import org.thingsboard.server.service.security.permission.Resource;
44 @Slf4j 46 @Slf4j
45 public class TenantProfileController extends BaseController { 47 public class TenantProfileController extends BaseController {
46 48
  49 + private static final String TENANT_PROFILE_INFO_DESCRIPTION = "Tenant Profile Info is a lightweight object that contains only id and name of the profile. ";
  50 +
  51 + @ApiOperation(value = "Get Tenant Profile (getTenantProfileById)",
  52 + notes = "Fetch the Tenant Profile object based on the provided Tenant Profile Id. " + SYSTEM_AUTHORITY_PARAGRAPH)
47 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 53 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
48 @RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.GET) 54 @RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.GET)
49 @ResponseBody 55 @ResponseBody
50 - public TenantProfile getTenantProfileById(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { 56 + public TenantProfile getTenantProfileById(
  57 + @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION)
  58 + @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException {
51 checkParameter("tenantProfileId", strTenantProfileId); 59 checkParameter("tenantProfileId", strTenantProfileId);
52 try { 60 try {
53 TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); 61 TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
@@ -57,10 +65,14 @@ public class TenantProfileController extends BaseController { @@ -57,10 +65,14 @@ public class TenantProfileController extends BaseController {
57 } 65 }
58 } 66 }
59 67
  68 + @ApiOperation(value = "Get Tenant Profile Info (getTenantProfileInfoById)",
  69 + notes = "Fetch the Tenant Profile Info object based on the provided Tenant Profile Id. " + TENANT_PROFILE_INFO_DESCRIPTION + SYSTEM_AUTHORITY_PARAGRAPH)
60 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 70 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
61 @RequestMapping(value = "/tenantProfileInfo/{tenantProfileId}", method = RequestMethod.GET) 71 @RequestMapping(value = "/tenantProfileInfo/{tenantProfileId}", method = RequestMethod.GET)
62 @ResponseBody 72 @ResponseBody
63 - public EntityInfo getTenantProfileInfoById(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { 73 + public EntityInfo getTenantProfileInfoById(
  74 + @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION)
  75 + @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException {
64 checkParameter("tenantProfileId", strTenantProfileId); 76 checkParameter("tenantProfileId", strTenantProfileId);
65 try { 77 try {
66 TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); 78 TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
@@ -70,6 +82,8 @@ public class TenantProfileController extends BaseController { @@ -70,6 +82,8 @@ public class TenantProfileController extends BaseController {
70 } 82 }
71 } 83 }
72 84
  85 + @ApiOperation(value = "Get default Tenant Profile Info (getDefaultTenantProfileInfo)",
  86 + notes = "Fetch the default Tenant Profile Info object based. " + TENANT_PROFILE_INFO_DESCRIPTION + SYSTEM_AUTHORITY_PARAGRAPH)
73 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 87 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
74 @RequestMapping(value = "/tenantProfileInfo/default", method = RequestMethod.GET) 88 @RequestMapping(value = "/tenantProfileInfo/default", method = RequestMethod.GET)
75 @ResponseBody 89 @ResponseBody
@@ -81,10 +95,65 @@ public class TenantProfileController extends BaseController { @@ -81,10 +95,65 @@ public class TenantProfileController extends BaseController {
81 } 95 }
82 } 96 }
83 97
  98 + @ApiOperation(value = "Create Or update Tenant Profile (saveTenantProfile)",
  99 + notes = "Create or update the Tenant Profile. When creating tenant profile, platform generates Tenant Profile Id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address). " +
  100 + "The newly created Tenant Profile Id will be present in the response. " +
  101 + "Specify existing Tenant Profile Id id to update the Tenant Profile. " +
  102 + "Referencing non-existing Tenant Profile Id will cause 'Not Found' error. " +
  103 + "\n\nUpdate of the tenant profile configuration will cause immediate recalculation of API limits for all affected Tenants. " +
  104 + "\n\nThe **'profileData'** object is the part of Tenant Profile that defines API limits and Rate limits. " +
  105 + "\n\nYou have an ability to define maximum number of devices ('maxDevice'), assets ('maxAssets') and other entities. " +
  106 + "You may also define maximum number of messages to be processed per month ('maxTransportMessages', 'maxREExecutions', etc). " +
  107 + "The '*RateLimit' defines the rate limits using simple syntax. For example, '1000:1,20000:60' means up to 1000 events per second but no more than 20000 event per minute. " +
  108 + "Let's review the example of tenant profile data below: " +
  109 + "\n\n" + MARKDOWN_CODE_BLOCK_START +
  110 + "{\n" +
  111 + " \"name\": \"Default\",\n" +
  112 + " \"description\": \"Default tenant profile\",\n" +
  113 + " \"isolatedTbCore\": false,\n" +
  114 + " \"isolatedTbRuleEngine\": false,\n" +
  115 + " \"profileData\": {\n" +
  116 + " \"configuration\": {\n" +
  117 + " \"type\": \"DEFAULT\",\n" +
  118 + " \"maxDevices\": 0,\n" +
  119 + " \"maxAssets\": 0,\n" +
  120 + " \"maxCustomers\": 0,\n" +
  121 + " \"maxUsers\": 0,\n" +
  122 + " \"maxDashboards\": 0,\n" +
  123 + " \"maxRuleChains\": 0,\n" +
  124 + " \"maxResourcesInBytes\": 0,\n" +
  125 + " \"maxOtaPackagesInBytes\": 0,\n" +
  126 + " \"transportTenantMsgRateLimit\": \"1000:1,20000:60\",\n" +
  127 + " \"transportTenantTelemetryMsgRateLimit\": \"1000:1,20000:60\",\n" +
  128 + " \"transportTenantTelemetryDataPointsRateLimit\": \"1000:1,20000:60\",\n" +
  129 + " \"transportDeviceMsgRateLimit\": \"20:1,600:60\",\n" +
  130 + " \"transportDeviceTelemetryMsgRateLimit\": \"20:1,600:60\",\n" +
  131 + " \"transportDeviceTelemetryDataPointsRateLimit\": \"20:1,600:60\",\n" +
  132 + " \"maxTransportMessages\": 10000000,\n" +
  133 + " \"maxTransportDataPoints\": 10000000,\n" +
  134 + " \"maxREExecutions\": 4000000,\n" +
  135 + " \"maxJSExecutions\": 5000000,\n" +
  136 + " \"maxDPStorageDays\": 0,\n" +
  137 + " \"maxRuleNodeExecutionsPerMessage\": 50,\n" +
  138 + " \"maxEmails\": 0,\n" +
  139 + " \"maxSms\": 0,\n" +
  140 + " \"maxCreatedAlarms\": 1000,\n" +
  141 + " \"defaultStorageTtlDays\": 0,\n" +
  142 + " \"alarmsTtlDays\": 0,\n" +
  143 + " \"rpcTtlDays\": 0,\n" +
  144 + " \"warnThreshold\": 0\n" +
  145 + " }\n" +
  146 + " },\n" +
  147 + " \"default\": true\n" +
  148 + "}" +
  149 + MARKDOWN_CODE_BLOCK_END +
  150 + SYSTEM_AUTHORITY_PARAGRAPH)
84 @PreAuthorize("hasAuthority('SYS_ADMIN')") 151 @PreAuthorize("hasAuthority('SYS_ADMIN')")
85 @RequestMapping(value = "/tenantProfile", method = RequestMethod.POST) 152 @RequestMapping(value = "/tenantProfile", method = RequestMethod.POST)
86 @ResponseBody 153 @ResponseBody
87 - public TenantProfile saveTenantProfile(@RequestBody TenantProfile tenantProfile) throws ThingsboardException { 154 + public TenantProfile saveTenantProfile(
  155 + @ApiParam(value = "A JSON value representing the tenant profile.")
  156 + @RequestBody TenantProfile tenantProfile) throws ThingsboardException {
88 try { 157 try {
89 boolean newTenantProfile = tenantProfile.getId() == null; 158 boolean newTenantProfile = tenantProfile.getId() == null;
90 if (newTenantProfile) { 159 if (newTenantProfile) {
@@ -105,10 +174,14 @@ public class TenantProfileController extends BaseController { @@ -105,10 +174,14 @@ public class TenantProfileController extends BaseController {
105 } 174 }
106 } 175 }
107 176
  177 + @ApiOperation(value = "Delete Tenant Profile (deleteTenantProfile)",
  178 + notes = "Deletes the tenant profile. Referencing non-existing tenant profile Id will cause an error. Referencing profile that is used by the tenants will cause an error. " + SYSTEM_AUTHORITY_PARAGRAPH)
108 @PreAuthorize("hasAuthority('SYS_ADMIN')") 179 @PreAuthorize("hasAuthority('SYS_ADMIN')")
109 @RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.DELETE) 180 @RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.DELETE)
110 @ResponseStatus(value = HttpStatus.OK) 181 @ResponseStatus(value = HttpStatus.OK)
111 - public void deleteTenantProfile(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { 182 + public void deleteTenantProfile(
  183 + @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION)
  184 + @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException {
112 checkParameter("tenantProfileId", strTenantProfileId); 185 checkParameter("tenantProfileId", strTenantProfileId);
113 try { 186 try {
114 TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); 187 TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
@@ -120,10 +193,14 @@ public class TenantProfileController extends BaseController { @@ -120,10 +193,14 @@ public class TenantProfileController extends BaseController {
120 } 193 }
121 } 194 }
122 195
  196 + @ApiOperation(value = "Make tenant profile default (setDefaultTenantProfile)",
  197 + notes = "Makes specified tenant profile to be default. Referencing non-existing tenant profile Id will cause an error. " + SYSTEM_AUTHORITY_PARAGRAPH)
123 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") 198 @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
124 @RequestMapping(value = "/tenantProfile/{tenantProfileId}/default", method = RequestMethod.POST) 199 @RequestMapping(value = "/tenantProfile/{tenantProfileId}/default", method = RequestMethod.POST)
125 @ResponseBody 200 @ResponseBody
126 - public TenantProfile setDefaultTenantProfile(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { 201 + public TenantProfile setDefaultTenantProfile(
  202 + @ApiParam(value = TENANT_PROFILE_ID_PARAM_DESCRIPTION)
  203 + @PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException {
127 checkParameter("tenantProfileId", strTenantProfileId); 204 checkParameter("tenantProfileId", strTenantProfileId);
128 try { 205 try {
129 TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); 206 TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
@@ -135,14 +212,21 @@ public class TenantProfileController extends BaseController { @@ -135,14 +212,21 @@ public class TenantProfileController extends BaseController {
135 } 212 }
136 } 213 }
137 214
  215 + @ApiOperation(value = "Get Tenant Profiles (getTenantProfiles)", notes = "Returns a page of tenant profiles registered in the platform. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
138 @PreAuthorize("hasAuthority('SYS_ADMIN')") 216 @PreAuthorize("hasAuthority('SYS_ADMIN')")
139 @RequestMapping(value = "/tenantProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) 217 @RequestMapping(value = "/tenantProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET)
140 @ResponseBody 218 @ResponseBody
141 - public PageData<TenantProfile> getTenantProfiles(@RequestParam int pageSize,  
142 - @RequestParam int page,  
143 - @RequestParam(required = false) String textSearch,  
144 - @RequestParam(required = false) String sortProperty,  
145 - @RequestParam(required = false) String sortOrder) throws ThingsboardException { 219 + public PageData<TenantProfile> getTenantProfiles(
  220 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
  221 + @RequestParam int pageSize,
  222 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
  223 + @RequestParam int page,
  224 + @ApiParam(value = TENANT_PROFILE_TEXT_SEARCH_DESCRIPTION)
  225 + @RequestParam(required = false) String textSearch,
  226 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = TENANT_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES)
  227 + @RequestParam(required = false) String sortProperty,
  228 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
  229 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
146 try { 230 try {
147 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 231 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
148 return checkNotNull(tenantProfileService.findTenantProfiles(getTenantId(), pageLink)); 232 return checkNotNull(tenantProfileService.findTenantProfiles(getTenantId(), pageLink));
@@ -151,14 +235,22 @@ public class TenantProfileController extends BaseController { @@ -151,14 +235,22 @@ public class TenantProfileController extends BaseController {
151 } 235 }
152 } 236 }
153 237
  238 + @ApiOperation(value = "Get Tenant Profiles Info (getTenantProfileInfos)", notes = "Returns a page of tenant profile info objects registered in the platform. "
  239 + + TENANT_PROFILE_INFO_DESCRIPTION + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
154 @PreAuthorize("hasAuthority('SYS_ADMIN')") 240 @PreAuthorize("hasAuthority('SYS_ADMIN')")
155 @RequestMapping(value = "/tenantProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) 241 @RequestMapping(value = "/tenantProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
156 @ResponseBody 242 @ResponseBody
157 - public PageData<EntityInfo> getTenantProfileInfos(@RequestParam int pageSize,  
158 - @RequestParam int page,  
159 - @RequestParam(required = false) String textSearch,  
160 - @RequestParam(required = false) String sortProperty,  
161 - @RequestParam(required = false) String sortOrder) throws ThingsboardException { 243 + public PageData<EntityInfo> getTenantProfileInfos(
  244 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
  245 + @RequestParam int pageSize,
  246 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
  247 + @RequestParam int page,
  248 + @ApiParam(value = TENANT_PROFILE_TEXT_SEARCH_DESCRIPTION)
  249 + @RequestParam(required = false) String textSearch,
  250 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = TENANT_PROFILE_INFO_SORT_PROPERTY_ALLOWABLE_VALUES)
  251 + @RequestParam(required = false) String sortProperty,
  252 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
  253 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
162 try { 254 try {
163 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 255 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
164 return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink)); 256 return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink));
@@ -15,9 +15,9 @@ @@ -15,9 +15,9 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
18 -import com.fasterxml.jackson.databind.JsonNode;  
19 -import com.fasterxml.jackson.databind.ObjectMapper;  
20 import com.fasterxml.jackson.databind.node.ObjectNode; 18 import com.fasterxml.jackson.databind.node.ObjectNode;
  19 +import io.swagger.annotations.ApiOperation;
  20 +import io.swagger.annotations.ApiParam;
21 import lombok.Getter; 21 import lombok.Getter;
22 import lombok.RequiredArgsConstructor; 22 import lombok.RequiredArgsConstructor;
23 import org.springframework.beans.factory.annotation.Value; 23 import org.springframework.beans.factory.annotation.Value;
@@ -32,7 +32,6 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -32,7 +32,6 @@ import org.springframework.web.bind.annotation.RequestParam;
32 import org.springframework.web.bind.annotation.ResponseBody; 32 import org.springframework.web.bind.annotation.ResponseBody;
33 import org.springframework.web.bind.annotation.ResponseStatus; 33 import org.springframework.web.bind.annotation.ResponseStatus;
34 import org.springframework.web.bind.annotation.RestController; 34 import org.springframework.web.bind.annotation.RestController;
35 -import org.thingsboard.common.util.JacksonUtil;  
36 import org.thingsboard.rule.engine.api.MailService; 35 import org.thingsboard.rule.engine.api.MailService;
37 import org.thingsboard.server.common.data.EntityType; 36 import org.thingsboard.server.common.data.EntityType;
38 import org.thingsboard.server.common.data.User; 37 import org.thingsboard.server.common.data.User;
@@ -52,6 +51,7 @@ import org.thingsboard.server.common.data.security.event.UserAuthDataChangedEven @@ -52,6 +51,7 @@ import org.thingsboard.server.common.data.security.event.UserAuthDataChangedEven
52 import org.thingsboard.server.common.data.security.model.JwtToken; 51 import org.thingsboard.server.common.data.security.model.JwtToken;
53 import org.thingsboard.server.queue.util.TbCoreComponent; 52 import org.thingsboard.server.queue.util.TbCoreComponent;
54 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; 53 import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
  54 +import org.thingsboard.server.service.security.model.JwtTokenPair;
55 import org.thingsboard.server.service.security.model.SecurityUser; 55 import org.thingsboard.server.service.security.model.SecurityUser;
56 import org.thingsboard.server.service.security.model.UserPrincipal; 56 import org.thingsboard.server.service.security.model.UserPrincipal;
57 import org.thingsboard.server.service.security.model.token.JwtTokenFactory; 57 import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
@@ -82,20 +82,27 @@ public class UserController extends BaseController { @@ -82,20 +82,27 @@ public class UserController extends BaseController {
82 private final SystemSecurityService systemSecurityService; 82 private final SystemSecurityService systemSecurityService;
83 private final ApplicationEventPublisher eventPublisher; 83 private final ApplicationEventPublisher eventPublisher;
84 84
  85 + @ApiOperation(value = "Get User (getUserById)",
  86 + notes = "Fetch the User object based on the provided User Id. " +
  87 + "If the user has the authority of 'SYS_ADMIN', the server does not perform additional checks. " +
  88 + "If the user has the authority of 'TENANT_ADMIN', the server checks that the requested user is owned by the same tenant. " +
  89 + "If the user has the authority of 'CUSTOMER_USER', the server checks that the requested user is owned by the same customer.")
85 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 90 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
86 @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET) 91 @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET)
87 @ResponseBody 92 @ResponseBody
88 - public User getUserById(@PathVariable(USER_ID) String strUserId) throws ThingsboardException { 93 + public User getUserById(
  94 + @ApiParam(value = USER_ID_PARAM_DESCRIPTION)
  95 + @PathVariable(USER_ID) String strUserId) throws ThingsboardException {
89 checkParameter(USER_ID, strUserId); 96 checkParameter(USER_ID, strUserId);
90 try { 97 try {
91 UserId userId = new UserId(toUUID(strUserId)); 98 UserId userId = new UserId(toUUID(strUserId));
92 User user = checkUserId(userId, Operation.READ); 99 User user = checkUserId(userId, Operation.READ);
93 - if(user.getAdditionalInfo().isObject()) { 100 + if (user.getAdditionalInfo().isObject()) {
94 ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo(); 101 ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo();
95 processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD); 102 processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD);
96 processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD); 103 processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD);
97 UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); 104 UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
98 - if(userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) { 105 + if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) {
99 additionalInfo.put("userCredentialsEnabled", true); 106 additionalInfo.put("userCredentialsEnabled", true);
100 } 107 }
101 } 108 }
@@ -105,6 +112,10 @@ public class UserController extends BaseController { @@ -105,6 +112,10 @@ public class UserController extends BaseController {
105 } 112 }
106 } 113 }
107 114
  115 + @ApiOperation(value = "Check Token Access Enabled (isUserTokenAccessEnabled)",
  116 + notes = "Checks that the system is configured to allow administrators to impersonate themself as other users. " +
  117 + "If the user who performs the request has the authority of 'SYS_ADMIN', it is possible to login as any tenant administrator. " +
  118 + "If the user who performs the request has the authority of 'TENANT_ADMIN', it is possible to login as any customer user. ")
108 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 119 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
109 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) 120 @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET)
110 @ResponseBody 121 @ResponseBody
@@ -112,10 +123,16 @@ public class UserController extends BaseController { @@ -112,10 +123,16 @@ public class UserController extends BaseController {
112 return userTokenAccessEnabled; 123 return userTokenAccessEnabled;
113 } 124 }
114 125
  126 + @ApiOperation(value = "Get User Token (getUserToken)",
  127 + notes = "Returns the token of the User based on the provided User Id. " +
  128 + "If the user who performs the request has the authority of 'SYS_ADMIN', it is possible to get the token of any tenant administrator. " +
  129 + "If the user who performs the request has the authority of 'TENANT_ADMIN', it is possible to get the token of any customer user that belongs to the same tenant. ")
115 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 130 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
116 @RequestMapping(value = "/user/{userId}/token", method = RequestMethod.GET) 131 @RequestMapping(value = "/user/{userId}/token", method = RequestMethod.GET)
117 @ResponseBody 132 @ResponseBody
118 - public JsonNode getUserToken(@PathVariable(USER_ID) String strUserId) throws ThingsboardException { 133 + public JwtTokenPair getUserToken(
  134 + @ApiParam(value = USER_ID_PARAM_DESCRIPTION)
  135 + @PathVariable(USER_ID) String strUserId) throws ThingsboardException {
119 checkParameter(USER_ID, strUserId); 136 checkParameter(USER_ID, strUserId);
120 try { 137 try {
121 if (!userTokenAccessEnabled) { 138 if (!userTokenAccessEnabled) {
@@ -130,22 +147,26 @@ public class UserController extends BaseController { @@ -130,22 +147,26 @@ public class UserController extends BaseController {
130 SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); 147 SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal);
131 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); 148 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
132 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); 149 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
133 - ObjectMapper objectMapper = new ObjectMapper();  
134 - ObjectNode tokenObject = objectMapper.createObjectNode();  
135 - tokenObject.put("token", accessToken.getToken());  
136 - tokenObject.put("refreshToken", refreshToken.getToken());  
137 - return tokenObject; 150 + return new JwtTokenPair(accessToken.getToken(), refreshToken.getToken());
138 } catch (Exception e) { 151 } catch (Exception e) {
139 throw handleException(e); 152 throw handleException(e);
140 } 153 }
141 } 154 }
142 155
  156 + @ApiOperation(value = "Save Or update User (saveUser)",
  157 + notes = "Create or update the User. When creating user, platform generates User Id as [time-based UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address). " +
  158 + "The newly created User Id will be present in the response. " +
  159 + "Specify existing User Id to update the device. " +
  160 + "Referencing non-existing User Id will cause 'Not Found' error." +
  161 + "\n\nDevice email is unique for entire platform setup.")
143 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") 162 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
144 @RequestMapping(value = "/user", method = RequestMethod.POST) 163 @RequestMapping(value = "/user", method = RequestMethod.POST)
145 @ResponseBody 164 @ResponseBody
146 - public User saveUser(@RequestBody User user,  
147 - @RequestParam(required = false, defaultValue = "true") boolean sendActivationMail,  
148 - HttpServletRequest request) throws ThingsboardException { 165 + public User saveUser(
  166 + @ApiParam(value = "A JSON value representing the User.", required = true)
  167 + @RequestBody User user,
  168 + @ApiParam(value = "Send activation email (or use activation link)", defaultValue = "true")
  169 + @RequestParam(required = false, defaultValue = "true") boolean sendActivationMail, HttpServletRequest request) throws ThingsboardException {
149 try { 170 try {
150 171
151 if (Authority.TENANT_ADMIN.equals(getCurrentUser().getAuthority())) { 172 if (Authority.TENANT_ADMIN.equals(getCurrentUser().getAuthority())) {
@@ -188,10 +209,13 @@ public class UserController extends BaseController { @@ -188,10 +209,13 @@ public class UserController extends BaseController {
188 } 209 }
189 } 210 }
190 211
  212 + @ApiOperation(value = "Send or re-send the activation email",
  213 + notes = "Force send the activation email to the user. Useful to resend the email if user has accidentally deleted it. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
191 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 214 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
192 @RequestMapping(value = "/user/sendActivationMail", method = RequestMethod.POST) 215 @RequestMapping(value = "/user/sendActivationMail", method = RequestMethod.POST)
193 @ResponseStatus(value = HttpStatus.OK) 216 @ResponseStatus(value = HttpStatus.OK)
194 public void sendActivationEmail( 217 public void sendActivationEmail(
  218 + @ApiParam(value = "Email of the user", required = true)
195 @RequestParam(value = "email") String email, 219 @RequestParam(value = "email") String email,
196 HttpServletRequest request) throws ThingsboardException { 220 HttpServletRequest request) throws ThingsboardException {
197 try { 221 try {
@@ -214,10 +238,14 @@ public class UserController extends BaseController { @@ -214,10 +238,14 @@ public class UserController extends BaseController {
214 } 238 }
215 } 239 }
216 240
  241 + @ApiOperation(value = "Get the activation link (getActivationLink)",
  242 + notes = "Get the activation link for the user. " +
  243 + "The base url for activation link is configurable in the general settings of system administrator. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
217 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 244 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
218 @RequestMapping(value = "/user/{userId}/activationLink", method = RequestMethod.GET, produces = "text/plain") 245 @RequestMapping(value = "/user/{userId}/activationLink", method = RequestMethod.GET, produces = "text/plain")
219 @ResponseBody 246 @ResponseBody
220 public String getActivationLink( 247 public String getActivationLink(
  248 + @ApiParam(value = USER_ID_PARAM_DESCRIPTION)
221 @PathVariable(USER_ID) String strUserId, 249 @PathVariable(USER_ID) String strUserId,
222 HttpServletRequest request) throws ThingsboardException { 250 HttpServletRequest request) throws ThingsboardException {
223 checkParameter(USER_ID, strUserId); 251 checkParameter(USER_ID, strUserId);
@@ -239,10 +267,15 @@ public class UserController extends BaseController { @@ -239,10 +267,15 @@ public class UserController extends BaseController {
239 } 267 }
240 } 268 }
241 269
  270 + @ApiOperation(value = "Delete User (deleteUser)",
  271 + notes = "Deletes the User, it's credentials and all the relations (from and to the User). " +
  272 + "Referencing non-existing User Id will cause an error. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
242 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 273 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
243 @RequestMapping(value = "/user/{userId}", method = RequestMethod.DELETE) 274 @RequestMapping(value = "/user/{userId}", method = RequestMethod.DELETE)
244 @ResponseStatus(value = HttpStatus.OK) 275 @ResponseStatus(value = HttpStatus.OK)
245 - public void deleteUser(@PathVariable(USER_ID) String strUserId) throws ThingsboardException { 276 + public void deleteUser(
  277 + @ApiParam(value = USER_ID_PARAM_DESCRIPTION)
  278 + @PathVariable(USER_ID) String strUserId) throws ThingsboardException {
246 checkParameter(USER_ID, strUserId); 279 checkParameter(USER_ID, strUserId);
247 try { 280 try {
248 UserId userId = new UserId(toUUID(strUserId)); 281 UserId userId = new UserId(toUUID(strUserId));
@@ -267,14 +300,22 @@ public class UserController extends BaseController { @@ -267,14 +300,22 @@ public class UserController extends BaseController {
267 } 300 }
268 } 301 }
269 302
  303 + @ApiOperation(value = "Get Users (getUsers)",
  304 + notes = "Returns a page of users owned by tenant or customer. The scope depends on authority of the user that performs the request." +
  305 + PAGE_DATA_PARAMETERS + TENANT_OR_USER_AUTHORITY_PARAGRAPH)
270 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 306 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
271 @RequestMapping(value = "/users", params = {"pageSize", "page"}, method = RequestMethod.GET) 307 @RequestMapping(value = "/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
272 @ResponseBody 308 @ResponseBody
273 public PageData<User> getUsers( 309 public PageData<User> getUsers(
  310 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
274 @RequestParam int pageSize, 311 @RequestParam int pageSize,
  312 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
275 @RequestParam int page, 313 @RequestParam int page,
  314 + @ApiParam(value = USER_TEXT_SEARCH_DESCRIPTION)
276 @RequestParam(required = false) String textSearch, 315 @RequestParam(required = false) String textSearch,
  316 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = USER_SORT_PROPERTY_ALLOWABLE_VALUES)
277 @RequestParam(required = false) String sortProperty, 317 @RequestParam(required = false) String sortProperty,
  318 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
278 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 319 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
279 try { 320 try {
280 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 321 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -289,15 +330,23 @@ public class UserController extends BaseController { @@ -289,15 +330,23 @@ public class UserController extends BaseController {
289 } 330 }
290 } 331 }
291 332
  333 + @ApiOperation(value = "Get Tenant Users (getTenantAdmins)",
  334 + notes = "Returns a page of users owned by tenant. " + PAGE_DATA_PARAMETERS + SYSTEM_AUTHORITY_PARAGRAPH)
292 @PreAuthorize("hasAuthority('SYS_ADMIN')") 335 @PreAuthorize("hasAuthority('SYS_ADMIN')")
293 @RequestMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET) 336 @RequestMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
294 @ResponseBody 337 @ResponseBody
295 public PageData<User> getTenantAdmins( 338 public PageData<User> getTenantAdmins(
296 - @PathVariable("tenantId") String strTenantId, 339 + @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION, required = true)
  340 + @PathVariable(TENANT_ID) String strTenantId,
  341 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
297 @RequestParam int pageSize, 342 @RequestParam int pageSize,
  343 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
298 @RequestParam int page, 344 @RequestParam int page,
  345 + @ApiParam(value = USER_TEXT_SEARCH_DESCRIPTION)
299 @RequestParam(required = false) String textSearch, 346 @RequestParam(required = false) String textSearch,
  347 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = USER_SORT_PROPERTY_ALLOWABLE_VALUES)
300 @RequestParam(required = false) String sortProperty, 348 @RequestParam(required = false) String sortProperty,
  349 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
301 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 350 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
302 checkParameter("tenantId", strTenantId); 351 checkParameter("tenantId", strTenantId);
303 try { 352 try {
@@ -309,15 +358,23 @@ public class UserController extends BaseController { @@ -309,15 +358,23 @@ public class UserController extends BaseController {
309 } 358 }
310 } 359 }
311 360
  361 + @ApiOperation(value = "Get Customer Users (getCustomerUsers)",
  362 + notes = "Returns a page of users owned by customer. " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
312 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 363 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
313 @RequestMapping(value = "/customer/{customerId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET) 364 @RequestMapping(value = "/customer/{customerId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
314 @ResponseBody 365 @ResponseBody
315 public PageData<User> getCustomerUsers( 366 public PageData<User> getCustomerUsers(
316 - @PathVariable("customerId") String strCustomerId, 367 + @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
  368 + @PathVariable(CUSTOMER_ID) String strCustomerId,
  369 + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
317 @RequestParam int pageSize, 370 @RequestParam int pageSize,
  371 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
318 @RequestParam int page, 372 @RequestParam int page,
  373 + @ApiParam(value = USER_TEXT_SEARCH_DESCRIPTION)
319 @RequestParam(required = false) String textSearch, 374 @RequestParam(required = false) String textSearch,
  375 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = USER_SORT_PROPERTY_ALLOWABLE_VALUES)
320 @RequestParam(required = false) String sortProperty, 376 @RequestParam(required = false) String sortProperty,
  377 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
321 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 378 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
322 checkParameter("customerId", strCustomerId); 379 checkParameter("customerId", strCustomerId);
323 try { 380 try {
@@ -331,11 +388,16 @@ public class UserController extends BaseController { @@ -331,11 +388,16 @@ public class UserController extends BaseController {
331 } 388 }
332 } 389 }
333 390
  391 + @ApiOperation(value = "Enable/Disable User credentials (setUserCredentialsEnabled)",
  392 + notes = "Enables or Disables user credentials. Useful when you would like to block user account without deleting it. " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
334 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 393 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
335 @RequestMapping(value = "/user/{userId}/userCredentialsEnabled", method = RequestMethod.POST) 394 @RequestMapping(value = "/user/{userId}/userCredentialsEnabled", method = RequestMethod.POST)
336 @ResponseBody 395 @ResponseBody
337 - public void setUserCredentialsEnabled(@PathVariable(USER_ID) String strUserId,  
338 - @RequestParam(required = false, defaultValue = "true") boolean userCredentialsEnabled) throws ThingsboardException { 396 + public void setUserCredentialsEnabled(
  397 + @ApiParam(value = USER_ID_PARAM_DESCRIPTION)
  398 + @PathVariable(USER_ID) String strUserId,
  399 + @ApiParam(value = "Disable (\"true\") or enable (\"false\") the credentials.", defaultValue = "true")
  400 + @RequestParam(required = false, defaultValue = "true") boolean userCredentialsEnabled) throws ThingsboardException {
339 checkParameter(USER_ID, strUserId); 401 checkParameter(USER_ID, strUserId);
340 try { 402 try {
341 UserId userId = new UserId(toUUID(strUserId)); 403 UserId userId = new UserId(toUUID(strUserId));
@@ -15,6 +15,10 @@ @@ -15,6 +15,10 @@
15 */ 15 */
16 package org.thingsboard.server.service.telemetry; 16 package org.thingsboard.server.service.telemetry;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
  20 +
  21 +@ApiModel
18 public class AttributeData implements Comparable<AttributeData>{ 22 public class AttributeData implements Comparable<AttributeData>{
19 23
20 private final long lastUpdateTs; 24 private final long lastUpdateTs;
@@ -28,14 +32,17 @@ public class AttributeData implements Comparable<AttributeData>{ @@ -28,14 +32,17 @@ public class AttributeData implements Comparable<AttributeData>{
28 this.value = value; 32 this.value = value;
29 } 33 }
30 34
  35 + @ApiModelProperty(position = 1, value = "Timestamp last updated attribute, in milliseconds", example = "1609459200000", readOnly = true)
31 public long getLastUpdateTs() { 36 public long getLastUpdateTs() {
32 return lastUpdateTs; 37 return lastUpdateTs;
33 } 38 }
34 39
  40 + @ApiModelProperty(position = 2, value = "String representing attribute key", example = "active", readOnly = true)
35 public String getKey() { 41 public String getKey() {
36 return key; 42 return key;
37 } 43 }
38 44
  45 + @ApiModelProperty(position = 3, value = "Object representing value of attribute key", example = "false", readOnly = true)
39 public Object getValue() { 46 public Object getValue() {
40 return value; 47 return value;
41 } 48 }
@@ -15,6 +15,10 @@ @@ -15,6 +15,10 @@
15 */ 15 */
16 package org.thingsboard.server.service.telemetry; 16 package org.thingsboard.server.service.telemetry;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
  20 +
  21 +@ApiModel
18 public class TsData implements Comparable<TsData>{ 22 public class TsData implements Comparable<TsData>{
19 23
20 private final long ts; 24 private final long ts;
@@ -26,10 +30,12 @@ public class TsData implements Comparable<TsData>{ @@ -26,10 +30,12 @@ public class TsData implements Comparable<TsData>{
26 this.value = value; 30 this.value = value;
27 } 31 }
28 32
  33 + @ApiModelProperty(position = 1, value = "Timestamp last updated timeseries, in milliseconds", example = "1609459200000", readOnly = true)
29 public long getTs() { 34 public long getTs() {
30 return ts; 35 return ts;
31 } 36 }
32 37
  38 + @ApiModelProperty(position = 2, value = "Object representing value of timeseries key", example = "20", readOnly = true)
33 public Object getValue() { 39 public Object getValue() {
34 return value; 40 return value;
35 } 41 }
@@ -18,6 +18,8 @@ package org.thingsboard.server.common.data; @@ -18,6 +18,8 @@ package org.thingsboard.server.common.data;
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.annotation.JsonProperty; 19 import com.fasterxml.jackson.annotation.JsonProperty;
20 import com.fasterxml.jackson.annotation.JsonProperty.Access; 20 import com.fasterxml.jackson.annotation.JsonProperty.Access;
  21 +import com.fasterxml.jackson.databind.JsonNode;
  22 +import io.swagger.annotations.ApiModelProperty;
21 import org.thingsboard.server.common.data.id.CustomerId; 23 import org.thingsboard.server.common.data.id.CustomerId;
22 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
23 import org.thingsboard.server.common.data.validation.NoXss; 25 import org.thingsboard.server.common.data.validation.NoXss;
@@ -27,7 +29,9 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId { @@ -27,7 +29,9 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId {
27 private static final long serialVersionUID = -1599722990298929275L; 29 private static final long serialVersionUID = -1599722990298929275L;
28 30
29 @NoXss 31 @NoXss
  32 + @ApiModelProperty(position = 3, value = "Title of the customer", example = "Company A")
30 private String title; 33 private String title;
  34 + @ApiModelProperty(position = 5, required = true, value = "JSON object with Tenant Id")
31 private TenantId tenantId; 35 private TenantId tenantId;
32 36
33 public Customer() { 37 public Customer() {
@@ -51,7 +55,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId { @@ -51,7 +55,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId {
51 public void setTenantId(TenantId tenantId) { 55 public void setTenantId(TenantId tenantId) {
52 this.tenantId = tenantId; 56 this.tenantId = tenantId;
53 } 57 }
54 - 58 +
55 public String getTitle() { 59 public String getTitle() {
56 return title; 60 return title;
57 } 61 }
@@ -60,6 +64,75 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId { @@ -60,6 +64,75 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId {
60 this.title = title; 64 this.title = title;
61 } 65 }
62 66
  67 + @ApiModelProperty(position = 1, value = "JSON object with the customer Id. " +
  68 + "Specify this field to update the customer. " +
  69 + "Referencing non-existing customer Id will cause error. " +
  70 + "Omit this field to create new customer." )
  71 + @Override
  72 + public CustomerId getId() {
  73 + return super.getId();
  74 + }
  75 +
  76 + @ApiModelProperty(position = 2, value = "Timestamp of the customer creation, in milliseconds", example = "1609459200000", readOnly = true)
  77 + @Override
  78 + public long getCreatedTime() {
  79 + return super.getCreatedTime();
  80 + }
  81 +
  82 + @ApiModelProperty(position = 6, required = true, value = "Country", example = "US")
  83 + @Override
  84 + public String getCountry() {
  85 + return super.getCountry();
  86 + }
  87 +
  88 + @ApiModelProperty(position = 7, required = true, value = "State", example = "NY")
  89 + @Override
  90 + public String getState() {
  91 + return super.getState();
  92 + }
  93 +
  94 + @ApiModelProperty(position = 8, required = true, value = "City", example = "New York")
  95 + @Override
  96 + public String getCity() {
  97 + return super.getCity();
  98 + }
  99 +
  100 + @ApiModelProperty(position = 9, required = true, value = "Address Line 1", example = "42 Broadway Suite 12-400")
  101 + @Override
  102 + public String getAddress() {
  103 + return super.getAddress();
  104 + }
  105 +
  106 + @ApiModelProperty(position = 10, required = true, value = "Address Line 2", example = "")
  107 + @Override
  108 + public String getAddress2() {
  109 + return super.getAddress2();
  110 + }
  111 +
  112 + @ApiModelProperty(position = 11, required = true, value = "Zip code", example = "10004")
  113 + @Override
  114 + public String getZip() {
  115 + return super.getZip();
  116 + }
  117 +
  118 + @ApiModelProperty(position = 12, required = true, value = "Phone number", example = "+1(415)777-7777")
  119 + @Override
  120 + public String getPhone() {
  121 + return super.getPhone();
  122 + }
  123 +
  124 + @ApiModelProperty(position = 13, required = true, value = "Email", example = "example@company.com")
  125 + @Override
  126 + public String getEmail() {
  127 + return super.getEmail();
  128 + }
  129 +
  130 + @ApiModelProperty(position = 14, value = "Additional parameters of the device", dataType = "com.fasterxml.jackson.databind.JsonNode")
  131 + @Override
  132 + public JsonNode getAdditionalInfo() {
  133 + return super.getAdditionalInfo();
  134 + }
  135 +
63 @JsonIgnore 136 @JsonIgnore
64 public boolean isPublic() { 137 public boolean isPublic() {
65 if (getAdditionalInfo() != null && getAdditionalInfo().has("isPublic")) { 138 if (getAdditionalInfo() != null && getAdditionalInfo().has("isPublic")) {
@@ -76,6 +149,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId { @@ -76,6 +149,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId {
76 149
77 @Override 150 @Override
78 @JsonProperty(access = Access.READ_ONLY) 151 @JsonProperty(access = Access.READ_ONLY)
  152 + @ApiModelProperty(position = 4, value = "Name of the customer. Read-only, duplicated from title for backward compatibility", example = "Company A", readOnly = true)
79 public String getName() { 153 public String getName() {
80 return title; 154 return title;
81 } 155 }
@@ -17,6 +17,8 @@ package org.thingsboard.server.common.data; @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.core.JsonProcessingException; 19 import com.fasterxml.jackson.core.JsonProcessingException;
  20 +import io.swagger.annotations.ApiModel;
  21 +import io.swagger.annotations.ApiModelProperty;
20 import lombok.Data; 22 import lombok.Data;
21 import lombok.EqualsAndHashCode; 23 import lombok.EqualsAndHashCode;
22 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
@@ -34,34 +36,51 @@ import java.io.IOException; @@ -34,34 +36,51 @@ import java.io.IOException;
34 36
35 import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper; 37 import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper;
36 38
  39 +@ApiModel
37 @Data 40 @Data
38 @EqualsAndHashCode(callSuper = true) 41 @EqualsAndHashCode(callSuper = true)
39 @Slf4j 42 @Slf4j
40 public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasOtaPackage { 43 public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasOtaPackage {
41 44
  45 + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id that owns the profile.", readOnly = true)
42 private TenantId tenantId; 46 private TenantId tenantId;
43 @NoXss 47 @NoXss
  48 + @ApiModelProperty(position = 4, value = "Unique Device Profile Name in scope of Tenant.", example = "Moisture Sensor")
44 private String name; 49 private String name;
45 @NoXss 50 @NoXss
  51 + @ApiModelProperty(position = 11, value = "Device Profile description. ")
46 private String description; 52 private String description;
  53 + @ApiModelProperty(position = 12, value = "Either URL or Base64 data of the icon. Used in the mobile application to visualize set of device profiles in the grid view. ")
47 private String image; 54 private String image;
48 private boolean isDefault; 55 private boolean isDefault;
  56 + @ApiModelProperty(position = 16, value = "Type of the profile. Always 'DEFAULT' for now. Reserved for future use.")
49 private DeviceProfileType type; 57 private DeviceProfileType type;
  58 + @ApiModelProperty(position = 14, value = "Type of the transport used to connect the device. Default transport supports HTTP, CoAP and MQTT.")
50 private DeviceTransportType transportType; 59 private DeviceTransportType transportType;
  60 + @ApiModelProperty(position = 15, value = "Provisioning strategy.")
51 private DeviceProfileProvisionType provisionType; 61 private DeviceProfileProvisionType provisionType;
  62 + @ApiModelProperty(position = 7, value = "Reference to the rule chain. " +
  63 + "If present, the specified rule chain will be used to process all messages related to device, including telemetry, attribute updates, etc. " +
  64 + "Otherwise, the root rule chain will be used to process those messages.")
52 private RuleChainId defaultRuleChainId; 65 private RuleChainId defaultRuleChainId;
  66 + @ApiModelProperty(position = 6, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to device details.")
53 private DashboardId defaultDashboardId; 67 private DashboardId defaultDashboardId;
54 @NoXss 68 @NoXss
  69 + @ApiModelProperty(position = 8, value = "Reference to the rule engine queue. " +
  70 + "If present, the specified queue will be used to store all unprocessed messages related to device, including telemetry, attribute updates, etc. " +
  71 + "Otherwise, the 'Main' queue will be used to store those messages.")
55 private String defaultQueueName; 72 private String defaultQueueName;
56 @Valid 73 @Valid
57 private transient DeviceProfileData profileData; 74 private transient DeviceProfileData profileData;
58 @JsonIgnore 75 @JsonIgnore
59 private byte[] profileDataBytes; 76 private byte[] profileDataBytes;
60 @NoXss 77 @NoXss
  78 + @ApiModelProperty(position = 13, value = "Unique provisioning key used by 'Device Provisioning' feature.")
61 private String provisionDeviceKey; 79 private String provisionDeviceKey;
62 80
  81 + @ApiModelProperty(position = 9, value = "Reference to the firmware OTA package. If present, the specified package will be used as default device firmware. ")
63 private OtaPackageId firmwareId; 82 private OtaPackageId firmwareId;
64 - 83 + @ApiModelProperty(position = 10, value = "Reference to the software OTA package. If present, the specified package will be used as default device software. ")
65 private OtaPackageId softwareId; 84 private OtaPackageId softwareId;
66 85
67 public DeviceProfile() { 86 public DeviceProfile() {
@@ -88,16 +107,32 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H @@ -88,16 +107,32 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
88 this.softwareId = deviceProfile.getSoftwareId(); 107 this.softwareId = deviceProfile.getSoftwareId();
89 } 108 }
90 109
  110 + @ApiModelProperty(position = 1, value = "JSON object with the device profile Id. " +
  111 + "Specify this field to update the device profile. " +
  112 + "Referencing non-existing device profile Id will cause error. " +
  113 + "Omit this field to create new device profile.")
  114 + @Override
  115 + public DeviceProfileId getId() {
  116 + return super.getId();
  117 + }
  118 +
  119 + @ApiModelProperty(position = 2, value = "Timestamp of the profile creation, in milliseconds", example = "1609459200000", readOnly = true)
  120 + @Override
  121 + public long getCreatedTime() {
  122 + return super.getCreatedTime();
  123 + }
  124 +
91 @Override 125 @Override
92 public String getSearchText() { 126 public String getSearchText() {
93 return getName(); 127 return getName();
94 } 128 }
95 129
96 - @Override  
97 - public String getName() {  
98 - return name; 130 + @ApiModelProperty(position = 5, value = "Used to mark the default profile. Default profile is used when the device profile is not specified during device creation.")
  131 + public boolean isDefault(){
  132 + return isDefault;
99 } 133 }
100 134
  135 + @ApiModelProperty(position = 16, value = "Complex JSON object that includes addition device profile configuration (transport, alarm rules, etc).")
101 public DeviceProfileData getProfileData() { 136 public DeviceProfileData getProfileData() {
102 if (profileData != null) { 137 if (profileData != null) {
103 return profileData; 138 return profileData;
@@ -17,6 +17,7 @@ package org.thingsboard.server.common.data; @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data;
17 17
18 import com.fasterxml.jackson.annotation.JsonCreator; 18 import com.fasterxml.jackson.annotation.JsonCreator;
19 import com.fasterxml.jackson.annotation.JsonProperty; 19 import com.fasterxml.jackson.annotation.JsonProperty;
  20 +import io.swagger.annotations.ApiModelProperty;
20 import lombok.EqualsAndHashCode; 21 import lombok.EqualsAndHashCode;
21 import lombok.ToString; 22 import lombok.ToString;
22 import lombok.Value; 23 import lombok.Value;
@@ -31,9 +32,13 @@ import java.util.UUID; @@ -31,9 +32,13 @@ import java.util.UUID;
31 @ToString(callSuper = true) 32 @ToString(callSuper = true)
32 public class DeviceProfileInfo extends EntityInfo { 33 public class DeviceProfileInfo extends EntityInfo {
33 34
  35 + @ApiModelProperty(position = 3, value = "Either URL or Base64 data of the icon. Used in the mobile application to visualize set of device profiles in the grid view. ")
34 private final String image; 36 private final String image;
  37 + @ApiModelProperty(position = 4, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to device details.")
35 private final DashboardId defaultDashboardId; 38 private final DashboardId defaultDashboardId;
  39 + @ApiModelProperty(position = 5, value = "Type of the profile. Always 'DEFAULT' for now. Reserved for future use.")
36 private final DeviceProfileType type; 40 private final DeviceProfileType type;
  41 + @ApiModelProperty(position = 6, value = "Type of the transport used to connect the device. Default transport supports HTTP, CoAP and MQTT.")
37 private final DeviceTransportType transportType; 42 private final DeviceTransportType transportType;
38 43
39 @JsonCreator 44 @JsonCreator
@@ -17,6 +17,8 @@ package org.thingsboard.server.common.data; @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data;
17 17
18 import com.fasterxml.jackson.annotation.JsonCreator; 18 import com.fasterxml.jackson.annotation.JsonCreator;
19 import com.fasterxml.jackson.annotation.JsonProperty; 19 import com.fasterxml.jackson.annotation.JsonProperty;
  20 +import io.swagger.annotations.ApiModel;
  21 +import io.swagger.annotations.ApiModelProperty;
20 import lombok.Data; 22 import lombok.Data;
21 import org.thingsboard.server.common.data.id.EntityId; 23 import org.thingsboard.server.common.data.id.EntityId;
22 import org.thingsboard.server.common.data.id.EntityIdFactory; 24 import org.thingsboard.server.common.data.id.EntityIdFactory;
@@ -24,10 +26,13 @@ import org.thingsboard.server.common.data.id.HasId; @@ -24,10 +26,13 @@ import org.thingsboard.server.common.data.id.HasId;
24 26
25 import java.util.UUID; 27 import java.util.UUID;
26 28
  29 +@ApiModel
27 @Data 30 @Data
28 public class EntityInfo implements HasId<EntityId>, HasName { 31 public class EntityInfo implements HasId<EntityId>, HasName {
29 32
  33 + @ApiModelProperty(position = 1, value = "JSON object with the entity Id. ")
30 private final EntityId id; 34 private final EntityId id;
  35 + @ApiModelProperty(position = 2, value = "Entity Name")
31 private final String name; 36 private final String name;
32 37
33 @JsonCreator 38 @JsonCreator
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.server.common.data; 16 package org.thingsboard.server.common.data;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import io.swagger.annotations.ApiModel;
  20 +import io.swagger.annotations.ApiModelProperty;
19 import lombok.Data; 21 import lombok.Data;
20 import org.thingsboard.server.common.data.id.EntityId; 22 import org.thingsboard.server.common.data.id.EntityId;
21 import org.thingsboard.server.common.data.id.EventId; 23 import org.thingsboard.server.common.data.id.EventId;
@@ -25,12 +27,18 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -25,12 +27,18 @@ import org.thingsboard.server.common.data.id.TenantId;
25 * @author Andrew Shvayka 27 * @author Andrew Shvayka
26 */ 28 */
27 @Data 29 @Data
  30 +@ApiModel
28 public class Event extends BaseData<EventId> { 31 public class Event extends BaseData<EventId> {
29 32
  33 + @ApiModelProperty(position = 1, value = "JSON object with Tenant Id.", readOnly = true)
30 private TenantId tenantId; 34 private TenantId tenantId;
  35 + @ApiModelProperty(position = 2, value = "Event type", example = "STATS")
31 private String type; 36 private String type;
  37 + @ApiModelProperty(position = 3, value = "string", example = "784f394c-42b6-435a-983c-b7beff2784f9")
32 private String uid; 38 private String uid;
  39 + @ApiModelProperty(position = 4, value = "JSON object with Entity Id for which event is created.", readOnly = true)
33 private EntityId entityId; 40 private EntityId entityId;
  41 + @ApiModelProperty(position = 5, value = "Event body.", dataType = "com.fasterxml.jackson.databind.JsonNode")
34 private transient JsonNode body; 42 private transient JsonNode body;
35 43
36 public Event() { 44 public Event() {
@@ -45,4 +53,9 @@ public class Event extends BaseData<EventId> { @@ -45,4 +53,9 @@ public class Event extends BaseData<EventId> {
45 super(event); 53 super(event);
46 } 54 }
47 55
  56 + @ApiModelProperty(position = 6, value = "Timestamp of the event creation, in milliseconds", example = "1609459200000", readOnly = true)
  57 + @Override
  58 + public long getCreatedTime() {
  59 + return super.getCreatedTime();
  60 + }
48 } 61 }
@@ -17,20 +17,28 @@ package org.thingsboard.server.common.data; @@ -17,20 +17,28 @@ package org.thingsboard.server.common.data;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.annotation.JsonProperty; 19 import com.fasterxml.jackson.annotation.JsonProperty;
  20 +import com.fasterxml.jackson.databind.JsonNode;
  21 +import io.swagger.annotations.ApiModel;
  22 +import io.swagger.annotations.ApiModelProperty;
20 import lombok.EqualsAndHashCode; 23 import lombok.EqualsAndHashCode;
21 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
22 import org.thingsboard.server.common.data.id.TenantProfileId; 25 import org.thingsboard.server.common.data.id.TenantProfileId;
23 import org.thingsboard.server.common.data.validation.NoXss; 26 import org.thingsboard.server.common.data.validation.NoXss;
24 27
  28 +@ApiModel
25 @EqualsAndHashCode(callSuper = true) 29 @EqualsAndHashCode(callSuper = true)
26 public class Tenant extends ContactBased<TenantId> implements HasTenantId { 30 public class Tenant extends ContactBased<TenantId> implements HasTenantId {
27 31
28 private static final long serialVersionUID = 8057243243859922101L; 32 private static final long serialVersionUID = 8057243243859922101L;
29 33
30 @NoXss 34 @NoXss
  35 + @ApiModelProperty(position = 3, value = "Title of the tenant", example = "Company A")
31 private String title; 36 private String title;
32 @NoXss 37 @NoXss
  38 + @ApiModelProperty(position = 5, value = "Geo region of the tenant", example = "North America")
33 private String region; 39 private String region;
  40 +
  41 + @ApiModelProperty(position = 6, required = true, value = "JSON object with Tenant Profile Id")
34 private TenantProfileId tenantProfileId; 42 private TenantProfileId tenantProfileId;
35 43
36 public Tenant() { 44 public Tenant() {
@@ -63,6 +71,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId { @@ -63,6 +71,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId {
63 } 71 }
64 72
65 @Override 73 @Override
  74 + @ApiModelProperty(position = 4, value = "Name of the tenant. Read-only, duplicated from title for backward compatibility", example = "Company A", readOnly = true)
66 @JsonProperty(access = JsonProperty.Access.READ_ONLY) 75 @JsonProperty(access = JsonProperty.Access.READ_ONLY)
67 public String getName() { 76 public String getName() {
68 return title; 77 return title;
@@ -89,6 +98,75 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId { @@ -89,6 +98,75 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId {
89 return getTitle(); 98 return getTitle();
90 } 99 }
91 100
  101 + @ApiModelProperty(position = 1, value = "JSON object with the tenant Id. " +
  102 + "Specify this field to update the tenant. " +
  103 + "Referencing non-existing tenant Id will cause error. " +
  104 + "Omit this field to create new tenant." )
  105 + @Override
  106 + public TenantId getId() {
  107 + return super.getId();
  108 + }
  109 +
  110 + @ApiModelProperty(position = 2, value = "Timestamp of the tenant creation, in milliseconds", example = "1609459200000", readOnly = true)
  111 + @Override
  112 + public long getCreatedTime() {
  113 + return super.getCreatedTime();
  114 + }
  115 +
  116 + @ApiModelProperty(position = 7, required = true, value = "Country", example = "US")
  117 + @Override
  118 + public String getCountry() {
  119 + return super.getCountry();
  120 + }
  121 +
  122 + @ApiModelProperty(position = 8, required = true, value = "State", example = "NY")
  123 + @Override
  124 + public String getState() {
  125 + return super.getState();
  126 + }
  127 +
  128 + @ApiModelProperty(position = 9, required = true, value = "City", example = "New York")
  129 + @Override
  130 + public String getCity() {
  131 + return super.getCity();
  132 + }
  133 +
  134 + @ApiModelProperty(position = 10, required = true, value = "Address Line 1", example = "42 Broadway Suite 12-400")
  135 + @Override
  136 + public String getAddress() {
  137 + return super.getAddress();
  138 + }
  139 +
  140 + @ApiModelProperty(position = 11, required = true, value = "Address Line 2", example = "")
  141 + @Override
  142 + public String getAddress2() {
  143 + return super.getAddress2();
  144 + }
  145 +
  146 + @ApiModelProperty(position = 12, required = true, value = "Zip code", example = "10004")
  147 + @Override
  148 + public String getZip() {
  149 + return super.getZip();
  150 + }
  151 +
  152 + @ApiModelProperty(position = 13, required = true, value = "Phone number", example = "+1(415)777-7777")
  153 + @Override
  154 + public String getPhone() {
  155 + return super.getPhone();
  156 + }
  157 +
  158 + @ApiModelProperty(position = 14, required = true, value = "Email", example = "example@company.com")
  159 + @Override
  160 + public String getEmail() {
  161 + return super.getEmail();
  162 + }
  163 +
  164 + @ApiModelProperty(position = 15, value = "Additional parameters of the device", dataType = "com.fasterxml.jackson.databind.JsonNode")
  165 + @Override
  166 + public JsonNode getAdditionalInfo() {
  167 + return super.getAdditionalInfo();
  168 + }
  169 +
92 @Override 170 @Override
93 public String toString() { 171 public String toString() {
94 StringBuilder builder = new StringBuilder(); 172 StringBuilder builder = new StringBuilder();
@@ -15,12 +15,15 @@ @@ -15,12 +15,15 @@
15 */ 15 */
16 package org.thingsboard.server.common.data; 16 package org.thingsboard.server.common.data;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.Data; 20 import lombok.Data;
19 import org.thingsboard.server.common.data.id.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
20 22
  23 +@ApiModel
21 @Data 24 @Data
22 public class TenantInfo extends Tenant { 25 public class TenantInfo extends Tenant {
23 - 26 + @ApiModelProperty(position = 15, value = "Tenant Profile name", example = "Default")
24 private String tenantProfileName; 27 private String tenantProfileName;
25 28
26 public TenantInfo() { 29 public TenantInfo() {
@@ -17,6 +17,8 @@ package org.thingsboard.server.common.data; @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.core.JsonProcessingException; 19 import com.fasterxml.jackson.core.JsonProcessingException;
  20 +import io.swagger.annotations.ApiModel;
  21 +import io.swagger.annotations.ApiModelProperty;
20 import lombok.Data; 22 import lombok.Data;
21 import lombok.EqualsAndHashCode; 23 import lombok.EqualsAndHashCode;
22 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
@@ -31,18 +33,27 @@ import java.util.Optional; @@ -31,18 +33,27 @@ import java.util.Optional;
31 33
32 import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper; 34 import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper;
33 35
  36 +@ApiModel
34 @Data 37 @Data
35 @EqualsAndHashCode(callSuper = true) 38 @EqualsAndHashCode(callSuper = true)
36 @Slf4j 39 @Slf4j
37 public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName { 40 public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName {
38 41
39 @NoXss 42 @NoXss
  43 + @ApiModelProperty(position = 3, value = "Name of the tenant profile", example = "High Priority Tenants")
40 private String name; 44 private String name;
41 @NoXss 45 @NoXss
  46 + @ApiModelProperty(position = 4, value = "Description of the tenant profile", example = "Any text")
42 private String description; 47 private String description;
  48 + @ApiModelProperty(position = 5, value = "Default Tenant profile to be used.", example = "true")
43 private boolean isDefault; 49 private boolean isDefault;
  50 + @ApiModelProperty(position = 6, value = "If enabled, will push all messages related to this tenant and processed by core platform services into separate queue. " +
  51 + "Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "true")
44 private boolean isolatedTbCore; 52 private boolean isolatedTbCore;
  53 + @ApiModelProperty(position = 7, value = "If enabled, will push all messages related to this tenant and processed by the rule engine into separate queue. " +
  54 + "Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "true")
45 private boolean isolatedTbRuleEngine; 55 private boolean isolatedTbRuleEngine;
  56 + @ApiModelProperty(position = 8, value = "Complex JSON object that contains profile settings: max devices, max assets, rate limits, etc.")
46 private transient TenantProfileData profileData; 57 private transient TenantProfileData profileData;
47 @JsonIgnore 58 @JsonIgnore
48 private byte[] profileDataBytes; 59 private byte[] profileDataBytes;
@@ -65,6 +76,21 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H @@ -65,6 +76,21 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
65 this.setProfileData(tenantProfile.getProfileData()); 76 this.setProfileData(tenantProfile.getProfileData());
66 } 77 }
67 78
  79 + @ApiModelProperty(position = 1, value = "JSON object with the tenant profile Id. " +
  80 + "Specify this field to update the tenant profile. " +
  81 + "Referencing non-existing tenant profile Id will cause error. " +
  82 + "Omit this field to create new tenant profile." )
  83 + @Override
  84 + public TenantProfileId getId() {
  85 + return super.getId();
  86 + }
  87 +
  88 + @ApiModelProperty(position = 2, value = "Timestamp of the tenant profile creation, in milliseconds", example = "1609459200000", readOnly = true)
  89 + @Override
  90 + public long getCreatedTime() {
  91 + return super.getCreatedTime();
  92 + }
  93 +
68 @Override 94 @Override
69 public String getSearchText() { 95 public String getSearchText() {
70 return getName(); 96 return getName();
@@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.alarm; @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.alarm;
17 17
18 import com.fasterxml.jackson.annotation.JsonProperty; 18 import com.fasterxml.jackson.annotation.JsonProperty;
19 import com.fasterxml.jackson.databind.JsonNode; 19 import com.fasterxml.jackson.databind.JsonNode;
  20 +import io.swagger.annotations.ApiModel;
  21 +import io.swagger.annotations.ApiModelProperty;
20 import lombok.AllArgsConstructor; 22 import lombok.AllArgsConstructor;
21 import lombok.Builder; 23 import lombok.Builder;
22 import lombok.Data; 24 import lombok.Data;
@@ -34,23 +36,41 @@ import java.util.List; @@ -34,23 +36,41 @@ import java.util.List;
34 /** 36 /**
35 * Created by ashvayka on 11.05.17. 37 * Created by ashvayka on 11.05.17.
36 */ 38 */
  39 +@ApiModel
37 @Data 40 @Data
38 @Builder 41 @Builder
39 @AllArgsConstructor 42 @AllArgsConstructor
40 public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, HasCustomerId { 43 public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, HasCustomerId {
41 44
  45 + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id", readOnly = true)
42 private TenantId tenantId; 46 private TenantId tenantId;
  47 +
  48 + @ApiModelProperty(position = 4, value = "JSON object with Customer Id", readOnly = true)
43 private CustomerId customerId; 49 private CustomerId customerId;
  50 +
  51 + @ApiModelProperty(position = 6, required = true, value = "representing type of the Alarm", example = "High Temperature Alarm")
44 private String type; 52 private String type;
  53 + @ApiModelProperty(position = 7, required = true, value = "JSON object with alarm originator id")
45 private EntityId originator; 54 private EntityId originator;
  55 + @ApiModelProperty(position = 8, required = true, value = "Alarm severity", example = "CRITICAL")
46 private AlarmSeverity severity; 56 private AlarmSeverity severity;
  57 + @ApiModelProperty(position = 9, required = true, value = "Alarm status", example = "CLEARED_UNACK")
47 private AlarmStatus status; 58 private AlarmStatus status;
  59 + @ApiModelProperty(position = 10, value = "Timestamp of the alarm start time, in milliseconds", example = "1634058704565")
48 private long startTs; 60 private long startTs;
  61 + @ApiModelProperty(position = 11, value = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522")
49 private long endTs; 62 private long endTs;
  63 + @ApiModelProperty(position = 12, value = "Timestamp of the alarm acknowledgement, in milliseconds", example = "1634115221948")
50 private long ackTs; 64 private long ackTs;
  65 + @ApiModelProperty(position = 13, value = "Timestamp of the alarm clearing, in milliseconds", example = "1634114528465")
51 private long clearTs; 66 private long clearTs;
  67 + @ApiModelProperty(position = 14, value = "JSON object with alarm details")
52 private transient JsonNode details; 68 private transient JsonNode details;
  69 + @ApiModelProperty(position = 15, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true")
53 private boolean propagate; 70 private boolean propagate;
  71 + @ApiModelProperty(position = 16, value = "JSON array of relation types that should be used for propagation. " +
  72 + "By default, 'propagateRelationTypes' array is empty which means that the alarm will propagate based on any relation type to parent entities. " +
  73 + "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will ignoned.")
54 private List<String> propagateRelationTypes; 74 private List<String> propagateRelationTypes;
55 75
56 public Alarm() { 76 public Alarm() {
@@ -81,7 +101,25 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha @@ -81,7 +101,25 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
81 101
82 @Override 102 @Override
83 @JsonProperty(access = JsonProperty.Access.READ_ONLY) 103 @JsonProperty(access = JsonProperty.Access.READ_ONLY)
  104 + @ApiModelProperty(position = 5, required = true, value = "representing type of the Alarm", example = "High Temperature Alarm")
84 public String getName() { 105 public String getName() {
85 return type; 106 return type;
86 } 107 }
  108 +
  109 + @ApiModelProperty(position = 1, value = "JSON object with the alarm Id. " +
  110 + "Specify this field to update the alarm. " +
  111 + "Referencing non-existing alarm Id will cause error. " +
  112 + "Omit this field to create new alarm." )
  113 + @Override
  114 + public AlarmId getId() {
  115 + return super.getId();
  116 + }
  117 +
  118 +
  119 + @ApiModelProperty(position = 2, value = "Timestamp of the alarm creation, in milliseconds", example = "1634058704567", readOnly = true)
  120 + @Override
  121 + public long getCreatedTime() {
  122 + return super.getCreatedTime();
  123 + }
  124 +
87 } 125 }
@@ -15,10 +15,15 @@ @@ -15,10 +15,15 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.alarm; 16 package org.thingsboard.server.common.data.alarm;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
  20 +
  21 +@ApiModel
18 public class AlarmInfo extends Alarm { 22 public class AlarmInfo extends Alarm {
19 23
20 private static final long serialVersionUID = 2807343093519543363L; 24 private static final long serialVersionUID = 2807343093519543363L;
21 25
  26 + @ApiModelProperty(position = 17, value = "Alarm originator name", example = "Thermostat")
22 private String originatorName; 27 private String originatorName;
23 28
24 public AlarmInfo() { 29 public AlarmInfo() {
@@ -16,24 +16,37 @@ @@ -16,24 +16,37 @@
16 package org.thingsboard.server.common.data.audit; 16 package org.thingsboard.server.common.data.audit;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import io.swagger.annotations.ApiModel;
  20 +import io.swagger.annotations.ApiModelProperty;
19 import lombok.Data; 21 import lombok.Data;
20 import lombok.EqualsAndHashCode; 22 import lombok.EqualsAndHashCode;
21 import org.thingsboard.server.common.data.BaseData; 23 import org.thingsboard.server.common.data.BaseData;
22 import org.thingsboard.server.common.data.id.*; 24 import org.thingsboard.server.common.data.id.*;
23 25
  26 +@ApiModel
24 @EqualsAndHashCode(callSuper = true) 27 @EqualsAndHashCode(callSuper = true)
25 @Data 28 @Data
26 public class AuditLog extends BaseData<AuditLogId> { 29 public class AuditLog extends BaseData<AuditLogId> {
27 30
  31 + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id", readOnly = true)
28 private TenantId tenantId; 32 private TenantId tenantId;
  33 + @ApiModelProperty(position = 4, value = "JSON object with Customer Id", readOnly = true)
29 private CustomerId customerId; 34 private CustomerId customerId;
  35 + @ApiModelProperty(position = 5, value = "JSON object with Entity id", readOnly = true)
30 private EntityId entityId; 36 private EntityId entityId;
  37 + @ApiModelProperty(position = 6, value = "Name of the logged entity", example = "Thermometer", readOnly = true)
31 private String entityName; 38 private String entityName;
  39 + @ApiModelProperty(position = 7, value = "JSON object with User id.", readOnly = true)
32 private UserId userId; 40 private UserId userId;
  41 + @ApiModelProperty(position = 8, value = "Unique user name(email) of the user that performed some action on logged entity", example = "tenant@thingsboard.org", readOnly = true)
33 private String userName; 42 private String userName;
  43 + @ApiModelProperty(position = 9, value = "String represented Action type", example = "ADDED", readOnly = true)
34 private ActionType actionType; 44 private ActionType actionType;
  45 + @ApiModelProperty(position = 10, value = "JsonNode represented action data", readOnly = true)
35 private JsonNode actionData; 46 private JsonNode actionData;
  47 + @ApiModelProperty(position = 11, value = "String represented Action status", example = "SUCCESS", allowableValues = "SUCCESS,FAILURE", readOnly = true)
36 private ActionStatus actionStatus; 48 private ActionStatus actionStatus;
  49 + @ApiModelProperty(position = 12, value = "Failure action details info. An empty string in case of action status type 'SUCCESS', otherwise includes stack trace of the caused exception.", readOnly = true)
37 private String actionFailureDetails; 50 private String actionFailureDetails;
38 51
39 public AuditLog() { 52 public AuditLog() {
@@ -57,4 +70,17 @@ public class AuditLog extends BaseData<AuditLogId> { @@ -57,4 +70,17 @@ public class AuditLog extends BaseData<AuditLogId> {
57 this.actionStatus = auditLog.getActionStatus(); 70 this.actionStatus = auditLog.getActionStatus();
58 this.actionFailureDetails = auditLog.getActionFailureDetails(); 71 this.actionFailureDetails = auditLog.getActionFailureDetails();
59 } 72 }
  73 +
  74 + @ApiModelProperty(position = 2, value = "Timestamp of the auditLog creation, in milliseconds", example = "1609459200000", readOnly = true)
  75 + @Override
  76 + public long getCreatedTime() {
  77 + return super.getCreatedTime();
  78 + }
  79 +
  80 + @ApiModelProperty(position = 1, value = "JSON object with the auditLog Id")
  81 + @Override
  82 + public AuditLogId getId() {
  83 + return super.getId();
  84 + }
  85 +
60 } 86 }
  1 +/**
  2 + * Copyright © 2016-2021 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.event;
  17 +
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
  20 +import lombok.Data;
  21 +
  22 +@ApiModel
  23 +@Data
  24 +public abstract class BaseEventFilter implements EventFilter {
  25 +
  26 + @ApiModelProperty(position = 1, value = "String value representing msg direction type (incoming to entity or outcoming from entity)", allowableValues = "IN, OUT")
  27 + protected String msgDirectionType;
  28 + @ApiModelProperty(position = 2, value = "String value representing the server name, identifier or ip address where the platform is running", example = "ip-172-31-24-152")
  29 + protected String server;
  30 + @ApiModelProperty(position = 3, value = "The case insensitive 'contains' filter based on data (key and value) for the message.", example = "humidity")
  31 + protected String dataSearch;
  32 + @ApiModelProperty(position = 4, value = "The case insensitive 'contains' filter based on metadata (key and value) for the message.", example = "deviceName")
  33 + protected String metadataSearch;
  34 + @ApiModelProperty(position = 5, value = "String value representing the entity type", allowableValues = "DEVICE")
  35 + protected String entityName;
  36 + @ApiModelProperty(position = 6, value = "String value representing the type of message routing", example = "Success")
  37 + protected String relationType;
  38 + @ApiModelProperty(position = 7, value = "String value representing the entity id in the event body (originator of the message)", example = "de9d54a0-2b7a-11ec-a3cc-23386423d98f")
  39 + protected String entityId;
  40 + @ApiModelProperty(position = 8, value = "String value representing the message type", example = "POST_TELEMETRY_REQUEST")
  41 + protected String msgType;
  42 + @ApiModelProperty(position = 9, value = "Boolean value to filter the errors", allowableValues = "false, true")
  43 + protected boolean isError;
  44 + @ApiModelProperty(position = 10, value = "The case insensitive 'contains' filter based on error message", example = "not present in the DB")
  45 + protected String errorStr;
  46 + @ApiModelProperty(position = 11, value = "String value representing the method name when the error happened", example = "onClusterEventMsg")
  47 + protected String method;
  48 + @ApiModelProperty(position = 12, value = "The minimum number of successfully processed messages", example = "25")
  49 + protected Integer messagesProcessed;
  50 + @ApiModelProperty(position = 13, value = "The minimum number of errors occurred during messages processing", example = "30")
  51 + protected Integer errorsOccurred;
  52 + @ApiModelProperty(position = 14, value = "String value representing the lifecycle event type", example = "STARTED")
  53 + protected String event;
  54 + @ApiModelProperty(position = 15, value = "String value representing status of the lifecycle event", allowableValues = "Success, Failure")
  55 + protected String status;
  56 +
  57 +}
@@ -15,22 +15,11 @@ @@ -15,22 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.event; 16 package org.thingsboard.server.common.data.event;
17 17
18 -import lombok.Data; 18 +import io.swagger.annotations.ApiModel;
19 import org.thingsboard.server.common.data.StringUtils; 19 import org.thingsboard.server.common.data.StringUtils;
20 20
21 -@Data  
22 -public abstract class DebugEvent implements EventFilter {  
23 -  
24 - private String msgDirectionType;  
25 - private String server;  
26 - private String dataSearch;  
27 - private String metadataSearch;  
28 - private String entityName;  
29 - private String relationType;  
30 - private String entityId;  
31 - private String msgType;  
32 - private boolean isError;  
33 - private String error; 21 +@ApiModel
  22 +public abstract class DebugEvent extends BaseEventFilter implements EventFilter {
34 23
35 public void setIsError(boolean isError) { 24 public void setIsError(boolean isError) {
36 this.isError = isError; 25 this.isError = isError;
@@ -39,7 +28,7 @@ public abstract class DebugEvent implements EventFilter { @@ -39,7 +28,7 @@ public abstract class DebugEvent implements EventFilter {
39 @Override 28 @Override
40 public boolean hasFilterForJsonBody() { 29 public boolean hasFilterForJsonBody() {
41 return !StringUtils.isEmpty(msgDirectionType) || !StringUtils.isEmpty(server) || !StringUtils.isEmpty(dataSearch) || !StringUtils.isEmpty(metadataSearch) 30 return !StringUtils.isEmpty(msgDirectionType) || !StringUtils.isEmpty(server) || !StringUtils.isEmpty(dataSearch) || !StringUtils.isEmpty(metadataSearch)
42 - || !StringUtils.isEmpty(entityName) || !StringUtils.isEmpty(relationType) || !StringUtils.isEmpty(entityId) || !StringUtils.isEmpty(msgType) || !StringUtils.isEmpty(error) || isError; 31 + || !StringUtils.isEmpty(entityName) || !StringUtils.isEmpty(relationType) || !StringUtils.isEmpty(entityId) || !StringUtils.isEmpty(msgType) || !StringUtils.isEmpty(errorStr) || isError;
43 } 32 }
44 33
45 } 34 }
@@ -15,6 +15,9 @@ @@ -15,6 +15,9 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.event; 16 package org.thingsboard.server.common.data.event;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +
  20 +@ApiModel
18 public class DebugRuleChainEventFilter extends DebugEvent { 21 public class DebugRuleChainEventFilter extends DebugEvent {
19 @Override 22 @Override
20 public EventType getEventType() { 23 public EventType getEventType() {
@@ -15,6 +15,9 @@ @@ -15,6 +15,9 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.event; 16 package org.thingsboard.server.common.data.event;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +
  20 +@ApiModel
18 public class DebugRuleNodeEventFilter extends DebugEvent { 21 public class DebugRuleNodeEventFilter extends DebugEvent {
19 @Override 22 @Override
20 public EventType getEventType() { 23 public EventType getEventType() {
@@ -15,14 +15,11 @@ @@ -15,14 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.event; 16 package org.thingsboard.server.common.data.event;
17 17
18 -import lombok.Data; 18 +import io.swagger.annotations.ApiModel;
19 import org.thingsboard.server.common.data.StringUtils; 19 import org.thingsboard.server.common.data.StringUtils;
20 20
21 -@Data  
22 -public class ErrorEventFilter implements EventFilter {  
23 - private String server;  
24 - private String method;  
25 - private String error; 21 +@ApiModel
  22 +public class ErrorEventFilter extends BaseEventFilter implements EventFilter {
26 23
27 @Override 24 @Override
28 public EventType getEventType() { 25 public EventType getEventType() {
@@ -31,6 +28,6 @@ public class ErrorEventFilter implements EventFilter { @@ -31,6 +28,6 @@ public class ErrorEventFilter implements EventFilter {
31 28
32 @Override 29 @Override
33 public boolean hasFilterForJsonBody() { 30 public boolean hasFilterForJsonBody() {
34 - return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(method) || !StringUtils.isEmpty(error); 31 + return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(method) || !StringUtils.isEmpty(errorStr);
35 } 32 }
36 } 33 }
@@ -15,11 +15,12 @@ @@ -15,11 +15,12 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.event; 16 package org.thingsboard.server.common.data.event;
17 17
18 -import com.fasterxml.jackson.annotation.JsonIgnore;  
19 -import com.fasterxml.jackson.annotation.JsonIgnoreProperties;  
20 import com.fasterxml.jackson.annotation.JsonSubTypes; 18 import com.fasterxml.jackson.annotation.JsonSubTypes;
21 import com.fasterxml.jackson.annotation.JsonTypeInfo; 19 import com.fasterxml.jackson.annotation.JsonTypeInfo;
  20 +import io.swagger.annotations.ApiModel;
  21 +import io.swagger.annotations.ApiModelProperty;
22 22
  23 +@ApiModel
23 @JsonTypeInfo( 24 @JsonTypeInfo(
24 use = JsonTypeInfo.Id.NAME, 25 use = JsonTypeInfo.Id.NAME,
25 include = JsonTypeInfo.As.PROPERTY, 26 include = JsonTypeInfo.As.PROPERTY,
@@ -32,7 +33,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -32,7 +33,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
32 @JsonSubTypes.Type(value = StatisticsEventFilter.class, name = "STATS") 33 @JsonSubTypes.Type(value = StatisticsEventFilter.class, name = "STATS")
33 }) 34 })
34 public interface EventFilter { 35 public interface EventFilter {
35 - @JsonIgnore 36 +
  37 + @ApiModelProperty(position = 1, required = true, value = "String value representing the event type", example = "STATS")
36 EventType getEventType(); 38 EventType getEventType();
37 39
38 boolean hasFilterForJsonBody(); 40 boolean hasFilterForJsonBody();
@@ -15,15 +15,11 @@ @@ -15,15 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.event; 16 package org.thingsboard.server.common.data.event;
17 17
18 -import lombok.Data; 18 +import io.swagger.annotations.ApiModel;
19 import org.thingsboard.server.common.data.StringUtils; 19 import org.thingsboard.server.common.data.StringUtils;
20 20
21 -@Data  
22 -public class LifeCycleEventFilter implements EventFilter {  
23 - private String server;  
24 - private String event;  
25 - private String status;  
26 - private String error; 21 +@ApiModel
  22 +public class LifeCycleEventFilter extends BaseEventFilter implements EventFilter {
27 23
28 @Override 24 @Override
29 public EventType getEventType() { 25 public EventType getEventType() {
@@ -32,6 +28,6 @@ public class LifeCycleEventFilter implements EventFilter { @@ -32,6 +28,6 @@ public class LifeCycleEventFilter implements EventFilter {
32 28
33 @Override 29 @Override
34 public boolean hasFilterForJsonBody() { 30 public boolean hasFilterForJsonBody() {
35 - return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(event) || !StringUtils.isEmpty(status) || !StringUtils.isEmpty(error); 31 + return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(event) || !StringUtils.isEmpty(status) || !StringUtils.isEmpty(errorStr);
36 } 32 }
37 } 33 }
@@ -15,14 +15,11 @@ @@ -15,14 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.event; 16 package org.thingsboard.server.common.data.event;
17 17
18 -import lombok.Data; 18 +import io.swagger.annotations.ApiModel;
19 import org.thingsboard.server.common.data.StringUtils; 19 import org.thingsboard.server.common.data.StringUtils;
20 20
21 -@Data  
22 -public class StatisticsEventFilter implements EventFilter {  
23 - private String server;  
24 - private Integer messagesProcessed;  
25 - private Integer errorsOccurred; 21 +@ApiModel
  22 +public class StatisticsEventFilter extends BaseEventFilter implements EventFilter {
26 23
27 @Override 24 @Override
28 public EventType getEventType() { 25 public EventType getEventType() {
@@ -18,6 +18,8 @@ package org.thingsboard.server.common.data.id; @@ -18,6 +18,8 @@ package org.thingsboard.server.common.data.id;
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 19 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
20 import com.fasterxml.jackson.databind.annotation.JsonSerialize; 20 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
  21 +import io.swagger.annotations.ApiModel;
  22 +import io.swagger.annotations.ApiModelProperty;
21 import org.thingsboard.server.common.data.EntityType; 23 import org.thingsboard.server.common.data.EntityType;
22 24
23 import java.io.Serializable; 25 import java.io.Serializable;
@@ -29,12 +31,15 @@ import java.util.UUID; @@ -29,12 +31,15 @@ import java.util.UUID;
29 31
30 @JsonDeserialize(using = EntityIdDeserializer.class) 32 @JsonDeserialize(using = EntityIdDeserializer.class)
31 @JsonSerialize(using = EntityIdSerializer.class) 33 @JsonSerialize(using = EntityIdSerializer.class)
  34 +@ApiModel
32 public interface EntityId extends HasUUID, Serializable { //NOSONAR, the constant is closely related to EntityId 35 public interface EntityId extends HasUUID, Serializable { //NOSONAR, the constant is closely related to EntityId
33 36
34 UUID NULL_UUID = UUID.fromString("13814000-1dd2-11b2-8080-808080808080"); 37 UUID NULL_UUID = UUID.fromString("13814000-1dd2-11b2-8080-808080808080");
35 38
  39 + @ApiModelProperty(position = 1, required = true, value = "string", example = "784f394c-42b6-435a-983c-b7beff2784f9")
36 UUID getId(); 40 UUID getId();
37 41
  42 + @ApiModelProperty(position = 2, required = true, value = "string", example = "DEVICE")
38 EntityType getEntityType(); 43 EntityType getEntityType();
39 44
40 @JsonIgnore 45 @JsonIgnore
@@ -15,19 +15,36 @@ @@ -15,19 +15,36 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
18 -import lombok.*; 18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
  20 +import lombok.Builder;
  21 +import lombok.Data;
  22 +import lombok.EqualsAndHashCode;
  23 +import lombok.ToString;
19 24
20 @Builder(toBuilder = true) 25 @Builder(toBuilder = true)
21 @EqualsAndHashCode 26 @EqualsAndHashCode
22 @Data 27 @Data
23 @ToString 28 @ToString
  29 +@ApiModel
24 public class OAuth2BasicMapperConfig { 30 public class OAuth2BasicMapperConfig {
  31 + @ApiModelProperty(value = "Email attribute key of OAuth2 principal attributes. " +
  32 + "Must be specified for BASIC mapper type and cannot be specified for GITHUB type")
25 private final String emailAttributeKey; 33 private final String emailAttributeKey;
  34 + @ApiModelProperty(value = "First name attribute key")
26 private final String firstNameAttributeKey; 35 private final String firstNameAttributeKey;
  36 + @ApiModelProperty(value = "Last name attribute key")
27 private final String lastNameAttributeKey; 37 private final String lastNameAttributeKey;
  38 + @ApiModelProperty(value = "Tenant naming strategy. For DOMAIN type, domain for tenant name will be taken from the email (substring before '@')", required = true)
28 private final TenantNameStrategyType tenantNameStrategy; 39 private final TenantNameStrategyType tenantNameStrategy;
  40 + @ApiModelProperty(value = "Tenant name pattern for CUSTOM naming strategy. " +
  41 + "OAuth2 attributes in the pattern can be used by enclosing attribute key in '%{' and '}'", example = "%{email}")
29 private final String tenantNamePattern; 42 private final String tenantNamePattern;
  43 + @ApiModelProperty(value = "Customer name pattern. When creating a user on the first OAuth2 log in, if specified, " +
  44 + "customer name will be used to create or find existing customer in the platform and assign customerId to the user")
30 private final String customerNamePattern; 45 private final String customerNamePattern;
  46 + @ApiModelProperty(value = "Name of the tenant's dashboard to set as default dashboard for newly created user")
31 private final String defaultDashboardName; 47 private final String defaultDashboardName;
  48 + @ApiModelProperty(value = "Whether default dashboard should be open in full screen")
32 private final boolean alwaysFullScreen; 49 private final boolean alwaysFullScreen;
33 } 50 }
@@ -15,19 +15,26 @@ @@ -15,19 +15,26 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
  20 +import lombok.AllArgsConstructor;
18 import lombok.Data; 21 import lombok.Data;
19 import lombok.EqualsAndHashCode; 22 import lombok.EqualsAndHashCode;
20 import lombok.NoArgsConstructor; 23 import lombok.NoArgsConstructor;
21 -import lombok.AllArgsConstructor;  
22 24
23 @EqualsAndHashCode 25 @EqualsAndHashCode
24 @Data 26 @Data
25 @NoArgsConstructor 27 @NoArgsConstructor
26 @AllArgsConstructor 28 @AllArgsConstructor
  29 +@ApiModel
27 public class OAuth2ClientInfo { 30 public class OAuth2ClientInfo {
28 31
  32 + @ApiModelProperty(value = "OAuth2 client name", example = "GitHub")
29 private String name; 33 private String name;
  34 + @ApiModelProperty(value = "Name of the icon, displayed on OAuth2 log in button", example = "github-logo")
30 private String icon; 35 private String icon;
  36 + @ApiModelProperty(value = "URI for OAuth2 log in. On HTTP GET request to this URI, it redirects to the OAuth2 provider page",
  37 + example = "/oauth2/authorization/8352f191-2b4d-11ec-9ed1-cbf57c026ecc")
31 private String url; 38 private String url;
32 39
33 public OAuth2ClientInfo(OAuth2ClientInfo oauth2ClientInfo) { 40 public OAuth2ClientInfo(OAuth2ClientInfo oauth2ClientInfo) {
@@ -35,4 +42,5 @@ public class OAuth2ClientInfo { @@ -35,4 +42,5 @@ public class OAuth2ClientInfo {
35 this.icon = oauth2ClientInfo.getIcon(); 42 this.icon = oauth2ClientInfo.getIcon();
36 this.url = oauth2ClientInfo.getUrl(); 43 this.url = oauth2ClientInfo.getUrl();
37 } 44 }
  45 +
38 } 46 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.Data; 20 import lombok.Data;
19 import lombok.EqualsAndHashCode; 21 import lombok.EqualsAndHashCode;
20 import lombok.NoArgsConstructor; 22 import lombok.NoArgsConstructor;
@@ -29,20 +31,34 @@ import java.util.List; @@ -29,20 +31,34 @@ import java.util.List;
29 @Data 31 @Data
30 @ToString 32 @ToString
31 @NoArgsConstructor 33 @NoArgsConstructor
  34 +@ApiModel
32 public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName { 35 public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName {
33 36
  37 + @ApiModelProperty(value = "OAuth2 provider identifier (e.g. its name)", required = true)
34 private String providerId; 38 private String providerId;
  39 + @ApiModelProperty(value = "Default config for mapping OAuth2 log in response to platform entities")
35 private OAuth2MapperConfig mapperConfig; 40 private OAuth2MapperConfig mapperConfig;
  41 + @ApiModelProperty(value = "Default authorization URI of the OAuth2 provider")
36 private String authorizationUri; 42 private String authorizationUri;
  43 + @ApiModelProperty(value = "Default access token URI of the OAuth2 provider")
37 private String accessTokenUri; 44 private String accessTokenUri;
  45 + @ApiModelProperty(value = "Default OAuth scopes that will be requested from OAuth2 platform")
38 private List<String> scope; 46 private List<String> scope;
  47 + @ApiModelProperty(value = "Default user info URI of the OAuth2 provider")
39 private String userInfoUri; 48 private String userInfoUri;
  49 + @ApiModelProperty(value = "Default name of the username attribute in OAuth2 provider log in response")
40 private String userNameAttributeName; 50 private String userNameAttributeName;
  51 + @ApiModelProperty(value = "Default JSON Web Key URI of the OAuth2 provider")
41 private String jwkSetUri; 52 private String jwkSetUri;
  53 + @ApiModelProperty(value = "Default client authentication method to use: 'BASIC' or 'POST'")
42 private String clientAuthenticationMethod; 54 private String clientAuthenticationMethod;
  55 + @ApiModelProperty(value = "Comment for OAuth2 provider")
43 private String comment; 56 private String comment;
  57 + @ApiModelProperty(value = "Default log in button icon for OAuth2 provider")
44 private String loginButtonIcon; 58 private String loginButtonIcon;
  59 + @ApiModelProperty(value = "Default OAuth2 provider label")
45 private String loginButtonLabel; 60 private String loginButtonLabel;
  61 + @ApiModelProperty(value = "Help link for OAuth2 provider")
46 private String helpLink; 62 private String helpLink;
47 63
48 public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { 64 public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) {
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.AllArgsConstructor; 20 import lombok.AllArgsConstructor;
19 import lombok.Builder; 21 import lombok.Builder;
20 import lombok.Data; 22 import lombok.Data;
@@ -28,7 +30,10 @@ import lombok.ToString; @@ -28,7 +30,10 @@ import lombok.ToString;
28 @NoArgsConstructor 30 @NoArgsConstructor
29 @AllArgsConstructor 31 @AllArgsConstructor
30 @Builder 32 @Builder
  33 +@ApiModel
31 public class OAuth2DomainInfo { 34 public class OAuth2DomainInfo {
  35 + @ApiModelProperty(value = "Domain scheme. Mixed scheme means than both HTTP and HTTPS are going to be used", required = true)
32 private SchemeType scheme; 36 private SchemeType scheme;
  37 + @ApiModelProperty(value = "Domain name. Cannot be empty", required = true)
33 private String name; 38 private String name;
34 } 39 }
@@ -15,7 +15,14 @@ @@ -15,7 +15,14 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
18 -import lombok.*; 18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
  20 +import lombok.AllArgsConstructor;
  21 +import lombok.Builder;
  22 +import lombok.Data;
  23 +import lombok.EqualsAndHashCode;
  24 +import lombok.NoArgsConstructor;
  25 +import lombok.ToString;
19 26
20 import java.util.List; 27 import java.util.List;
21 28
@@ -25,7 +32,10 @@ import java.util.List; @@ -25,7 +32,10 @@ import java.util.List;
25 @Builder(toBuilder = true) 32 @Builder(toBuilder = true)
26 @NoArgsConstructor 33 @NoArgsConstructor
27 @AllArgsConstructor 34 @AllArgsConstructor
  35 +@ApiModel
28 public class OAuth2Info { 36 public class OAuth2Info {
  37 + @ApiModelProperty("Whether OAuth2 settings are enabled or not")
29 private boolean enabled; 38 private boolean enabled;
  39 + @ApiModelProperty(value = "List of configured OAuth2 clients. Cannot contain null values", required = true)
30 private List<OAuth2ParamsInfo> oauth2ParamsInfos; 40 private List<OAuth2ParamsInfo> oauth2ParamsInfos;
31 } 41 }
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
  18 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.Builder; 19 import lombok.Builder;
19 import lombok.Data; 20 import lombok.Data;
20 import lombok.EqualsAndHashCode; 21 import lombok.EqualsAndHashCode;
@@ -25,9 +26,14 @@ import lombok.ToString; @@ -25,9 +26,14 @@ import lombok.ToString;
25 @Data 26 @Data
26 @ToString 27 @ToString
27 public class OAuth2MapperConfig { 28 public class OAuth2MapperConfig {
  29 + @ApiModelProperty(value = "Whether user should be created if not yet present on the platform after successful authentication")
28 private boolean allowUserCreation; 30 private boolean allowUserCreation;
  31 + @ApiModelProperty(value = "Whether user credentials should be activated when user is created after successful authentication")
29 private boolean activateUser; 32 private boolean activateUser;
  33 + @ApiModelProperty(value = "Type of OAuth2 mapper. Depending on this param, different mapper config fields must be specified", required = true)
30 private MapperType type; 34 private MapperType type;
  35 + @ApiModelProperty(value = "Mapper config for BASIC and GITHUB mapper types")
31 private OAuth2BasicMapperConfig basic; 36 private OAuth2BasicMapperConfig basic;
  37 + @ApiModelProperty(value = "Mapper config for CUSTOM mapper type")
32 private OAuth2CustomMapperConfig custom; 38 private OAuth2CustomMapperConfig custom;
33 } 39 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.AllArgsConstructor; 20 import lombok.AllArgsConstructor;
19 import lombok.Builder; 21 import lombok.Builder;
20 import lombok.Data; 22 import lombok.Data;
@@ -28,7 +30,10 @@ import lombok.ToString; @@ -28,7 +30,10 @@ import lombok.ToString;
28 @NoArgsConstructor 30 @NoArgsConstructor
29 @AllArgsConstructor 31 @AllArgsConstructor
30 @Builder 32 @Builder
  33 +@ApiModel
31 public class OAuth2MobileInfo { 34 public class OAuth2MobileInfo {
  35 + @ApiModelProperty(value = "Application package name. Cannot be empty", required = true)
32 private String pkgName; 36 private String pkgName;
  37 + @ApiModelProperty(value = "Application secret. The length must be at least 16 characters", required = true)
33 private String appSecret; 38 private String appSecret;
34 } 39 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.AllArgsConstructor; 20 import lombok.AllArgsConstructor;
19 import lombok.Builder; 21 import lombok.Builder;
20 import lombok.Data; 22 import lombok.Data;
@@ -30,10 +32,16 @@ import java.util.List; @@ -30,10 +32,16 @@ import java.util.List;
30 @Builder(toBuilder = true) 32 @Builder(toBuilder = true)
31 @NoArgsConstructor 33 @NoArgsConstructor
32 @AllArgsConstructor 34 @AllArgsConstructor
  35 +@ApiModel
33 public class OAuth2ParamsInfo { 36 public class OAuth2ParamsInfo {
34 37
  38 + @ApiModelProperty(value = "List of configured domains where OAuth2 platform will redirect a user after successful " +
  39 + "authentication. Cannot be empty. There have to be only one domain with specific name with scheme type 'MIXED'. " +
  40 + "Configured domains with the same name must have different scheme types", required = true)
35 private List<OAuth2DomainInfo> domainInfos; 41 private List<OAuth2DomainInfo> domainInfos;
  42 + @ApiModelProperty(value = "Mobile applications settings. Application package name must be unique within the list", required = true)
36 private List<OAuth2MobileInfo> mobileInfos; 43 private List<OAuth2MobileInfo> mobileInfos;
  44 + @ApiModelProperty(value = "List of OAuth2 provider settings. Cannot be empty", required = true)
37 private List<OAuth2RegistrationInfo> clientRegistrations; 45 private List<OAuth2RegistrationInfo> clientRegistrations;
38 46
39 } 47 }
@@ -16,7 +16,14 @@ @@ -16,7 +16,14 @@
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 -import lombok.*; 19 +import io.swagger.annotations.ApiModel;
  20 +import io.swagger.annotations.ApiModelProperty;
  21 +import lombok.AllArgsConstructor;
  22 +import lombok.Builder;
  23 +import lombok.Data;
  24 +import lombok.EqualsAndHashCode;
  25 +import lombok.NoArgsConstructor;
  26 +import lombok.ToString;
20 27
21 import java.util.List; 28 import java.util.List;
22 29
@@ -26,19 +33,34 @@ import java.util.List; @@ -26,19 +33,34 @@ import java.util.List;
26 @NoArgsConstructor 33 @NoArgsConstructor
27 @AllArgsConstructor 34 @AllArgsConstructor
28 @Builder 35 @Builder
  36 +@ApiModel
29 public class OAuth2RegistrationInfo { 37 public class OAuth2RegistrationInfo {
  38 + @ApiModelProperty(value = "Config for mapping OAuth2 log in response to platform entities", required = true)
30 private OAuth2MapperConfig mapperConfig; 39 private OAuth2MapperConfig mapperConfig;
  40 + @ApiModelProperty(value = "OAuth2 client ID. Cannot be empty", required = true)
31 private String clientId; 41 private String clientId;
  42 + @ApiModelProperty(value = "OAuth2 client secret. Cannot be empty", required = true)
32 private String clientSecret; 43 private String clientSecret;
  44 + @ApiModelProperty(value = "Authorization URI of the OAuth2 provider. Cannot be empty", required = true)
33 private String authorizationUri; 45 private String authorizationUri;
  46 + @ApiModelProperty(value = "Access token URI of the OAuth2 provider. Cannot be empty", required = true)
34 private String accessTokenUri; 47 private String accessTokenUri;
  48 + @ApiModelProperty(value = "OAuth scopes that will be requested from OAuth2 platform. Cannot be empty", required = true)
35 private List<String> scope; 49 private List<String> scope;
  50 + @ApiModelProperty(value = "User info URI of the OAuth2 provider")
36 private String userInfoUri; 51 private String userInfoUri;
  52 + @ApiModelProperty(value = "Name of the username attribute in OAuth2 provider response. Cannot be empty")
37 private String userNameAttributeName; 53 private String userNameAttributeName;
  54 + @ApiModelProperty(value = "JSON Web Key URI of the OAuth2 provider")
38 private String jwkSetUri; 55 private String jwkSetUri;
  56 + @ApiModelProperty(value = "Client authentication method to use: 'BASIC' or 'POST'. Cannot be empty", required = true)
39 private String clientAuthenticationMethod; 57 private String clientAuthenticationMethod;
  58 + @ApiModelProperty(value = "OAuth2 provider label. Cannot be empty", required = true)
40 private String loginButtonLabel; 59 private String loginButtonLabel;
  60 + @ApiModelProperty(value = "Log in button icon for OAuth2 provider")
41 private String loginButtonIcon; 61 private String loginButtonIcon;
  62 + @ApiModelProperty(value = "List of platforms for which usage of the OAuth2 client is allowed (empty for all allowed)")
42 private List<PlatformType> platforms; 63 private List<PlatformType> platforms;
  64 + @ApiModelProperty(value = "Additional info of OAuth2 client (e.g. providerName)", required = true)
43 private JsonNode additionalInfo; 65 private JsonNode additionalInfo;
44 } 66 }
@@ -15,11 +15,13 @@ @@ -15,11 +15,13 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.query; 16 package org.thingsboard.server.common.data.query;
17 17
  18 +import io.swagger.annotations.ApiModel;
18 import lombok.Getter; 19 import lombok.Getter;
19 20
20 import java.util.Collections; 21 import java.util.Collections;
21 import java.util.List; 22 import java.util.List;
22 23
  24 +@ApiModel
23 public class EntityCountQuery { 25 public class EntityCountQuery {
24 26
25 @Getter 27 @Getter
@@ -15,10 +15,12 @@ @@ -15,10 +15,12 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.query; 16 package org.thingsboard.server.common.data.query;
17 17
  18 +import io.swagger.annotations.ApiModel;
18 import lombok.Data; 19 import lombok.Data;
19 20
20 import java.io.Serializable; 21 import java.io.Serializable;
21 22
  23 +@ApiModel
22 @Data 24 @Data
23 public class EntityKey implements Serializable { 25 public class EntityKey implements Serializable {
24 private final EntityKeyType type; 26 private final EntityKeyType type;
@@ -15,10 +15,12 @@ @@ -15,10 +15,12 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.query; 16 package org.thingsboard.server.common.data.query;
17 17
  18 +import io.swagger.annotations.ApiModel;
18 import lombok.Data; 19 import lombok.Data;
19 20
20 import java.io.Serializable; 21 import java.io.Serializable;
21 22
  23 +@ApiModel
22 @Data 24 @Data
23 public class KeyFilter implements Serializable { 25 public class KeyFilter implements Serializable {
24 26
@@ -16,18 +16,17 @@ @@ -16,18 +16,17 @@
16 package org.thingsboard.server.common.data.relation; 16 package org.thingsboard.server.common.data.relation;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 -import com.fasterxml.jackson.core.JsonProcessingException;  
20 import com.fasterxml.jackson.databind.JsonNode; 19 import com.fasterxml.jackson.databind.JsonNode;
21 -import com.fasterxml.jackson.databind.ObjectMapper; 20 +import io.swagger.annotations.ApiModel;
  21 +import io.swagger.annotations.ApiModelProperty;
22 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
23 import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; 23 import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
24 import org.thingsboard.server.common.data.id.EntityId; 24 import org.thingsboard.server.common.data.id.EntityId;
25 25
26 -import java.io.ByteArrayInputStream;  
27 -import java.io.IOException;  
28 import java.io.Serializable; 26 import java.io.Serializable;
29 27
30 @Slf4j 28 @Slf4j
  29 +@ApiModel
31 public class EntityRelation implements Serializable { 30 public class EntityRelation implements Serializable {
32 31
33 private static final long serialVersionUID = 2807343040519543363L; 32 private static final long serialVersionUID = 2807343040519543363L;
@@ -72,6 +71,7 @@ public class EntityRelation implements Serializable { @@ -72,6 +71,7 @@ public class EntityRelation implements Serializable {
72 this.additionalInfo = entityRelation.getAdditionalInfo(); 71 this.additionalInfo = entityRelation.getAdditionalInfo();
73 } 72 }
74 73
  74 + @ApiModelProperty(position = 1, value = "JSON object with [from] Entity Id.", readOnly = true)
75 public EntityId getFrom() { 75 public EntityId getFrom() {
76 return from; 76 return from;
77 } 77 }
@@ -80,6 +80,7 @@ public class EntityRelation implements Serializable { @@ -80,6 +80,7 @@ public class EntityRelation implements Serializable {
80 this.from = from; 80 this.from = from;
81 } 81 }
82 82
  83 + @ApiModelProperty(position = 2, value = "JSON object with [to] Entity Id.", readOnly = true)
83 public EntityId getTo() { 84 public EntityId getTo() {
84 return to; 85 return to;
85 } 86 }
@@ -88,6 +89,7 @@ public class EntityRelation implements Serializable { @@ -88,6 +89,7 @@ public class EntityRelation implements Serializable {
88 this.to = to; 89 this.to = to;
89 } 90 }
90 91
  92 + @ApiModelProperty(position = 3, value = "String value of relation type.", example = "Contains")
91 public String getType() { 93 public String getType() {
92 return type; 94 return type;
93 } 95 }
@@ -96,6 +98,7 @@ public class EntityRelation implements Serializable { @@ -96,6 +98,7 @@ public class EntityRelation implements Serializable {
96 this.type = type; 98 this.type = type;
97 } 99 }
98 100
  101 + @ApiModelProperty(position = 4, value = "Represents the type group of the relation.", example = "COMMON")
99 public RelationTypeGroup getTypeGroup() { 102 public RelationTypeGroup getTypeGroup() {
100 return typeGroup; 103 return typeGroup;
101 } 104 }
@@ -104,6 +107,7 @@ public class EntityRelation implements Serializable { @@ -104,6 +107,7 @@ public class EntityRelation implements Serializable {
104 this.typeGroup = typeGroup; 107 this.typeGroup = typeGroup;
105 } 108 }
106 109
  110 + @ApiModelProperty(position = 5, value = "Additional parameters of the relation", dataType = "com.fasterxml.jackson.databind.JsonNode")
107 public JsonNode getAdditionalInfo() { 111 public JsonNode getAdditionalInfo() {
108 return SearchTextBasedWithAdditionalInfo.getJson(() -> additionalInfo, () -> additionalInfoBytes); 112 return SearchTextBasedWithAdditionalInfo.getJson(() -> additionalInfo, () -> additionalInfoBytes);
109 } 113 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.relation; 16 package org.thingsboard.server.common.data.relation;
17 17
  18 +import io.swagger.annotations.ApiModelProperty;
  19 +
18 public class EntityRelationInfo extends EntityRelation { 20 public class EntityRelationInfo extends EntityRelation {
19 21
20 private static final long serialVersionUID = 2807343097519543363L; 22 private static final long serialVersionUID = 2807343097519543363L;
@@ -30,6 +32,7 @@ public class EntityRelationInfo extends EntityRelation { @@ -30,6 +32,7 @@ public class EntityRelationInfo extends EntityRelation {
30 super(entityRelation); 32 super(entityRelation);
31 } 33 }
32 34
  35 + @ApiModelProperty(position = 6, value = "Name of the entity for [from] direction.", readOnly = true, example = "A4B72CCDFF33")
33 public String getFromName() { 36 public String getFromName() {
34 return fromName; 37 return fromName;
35 } 38 }
@@ -38,6 +41,7 @@ public class EntityRelationInfo extends EntityRelation { @@ -38,6 +41,7 @@ public class EntityRelationInfo extends EntityRelation {
38 this.fromName = fromName; 41 this.fromName = fromName;
39 } 42 }
40 43
  44 + @ApiModelProperty(position = 7, value = "Name of the entity for [to] direction.", readOnly = true, example = "A4B72CCDFF35")
41 public String getToName() { 45 public String getToName() {
42 return toName; 46 return toName;
43 } 47 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.relation; 16 package org.thingsboard.server.common.data.relation;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.Data; 20 import lombok.Data;
19 21
20 import java.util.List; 22 import java.util.List;
@@ -23,9 +25,12 @@ import java.util.List; @@ -23,9 +25,12 @@ import java.util.List;
23 * Created by ashvayka on 02.05.17. 25 * Created by ashvayka on 02.05.17.
24 */ 26 */
25 @Data 27 @Data
  28 +@ApiModel
26 public class EntityRelationsQuery { 29 public class EntityRelationsQuery {
27 30
  31 + @ApiModelProperty(position = 2, value = "Main search parameters.")
28 private RelationsSearchParameters parameters; 32 private RelationsSearchParameters parameters;
  33 + @ApiModelProperty(position = 1, value = "Main filters.")
29 private List<RelationEntityTypeFilter> filters; 34 private List<RelationEntityTypeFilter> filters;
30 35
31 } 36 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.relation; 16 package org.thingsboard.server.common.data.relation;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.AllArgsConstructor; 20 import lombok.AllArgsConstructor;
19 import lombok.Data; 21 import lombok.Data;
20 import org.thingsboard.server.common.data.EntityType; 22 import org.thingsboard.server.common.data.EntityType;
@@ -26,9 +28,12 @@ import java.util.List; @@ -26,9 +28,12 @@ import java.util.List;
26 */ 28 */
27 @Data 29 @Data
28 @AllArgsConstructor 30 @AllArgsConstructor
  31 +@ApiModel
29 public class RelationEntityTypeFilter { 32 public class RelationEntityTypeFilter {
30 33
  34 + @ApiModelProperty(position = 1, value = "Type of the relation between root entity and other entity (e.g. 'Contains' or 'Manages').", example = "Contains")
31 private String relationType; 35 private String relationType;
32 36
  37 + @ApiModelProperty(position = 2, value = "Array of entity types to filter the related entities (e.g. 'DEVICE', 'ASSET').")
33 private List<EntityType> entityTypes; 38 private List<EntityType> entityTypes;
34 } 39 }
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.relation; 16 package org.thingsboard.server.common.data.relation;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
18 import io.swagger.annotations.ApiModel; 19 import io.swagger.annotations.ApiModel;
19 import io.swagger.annotations.ApiModelProperty; 20 import io.swagger.annotations.ApiModelProperty;
20 import lombok.AllArgsConstructor; 21 import lombok.AllArgsConstructor;
@@ -33,7 +34,7 @@ import java.util.UUID; @@ -33,7 +34,7 @@ import java.util.UUID;
33 @AllArgsConstructor 34 @AllArgsConstructor
34 public class RelationsSearchParameters { 35 public class RelationsSearchParameters {
35 36
36 - @ApiModelProperty(position = 1, value = "Root entity id to start search from.") 37 + @ApiModelProperty(position = 1, value = "Root entity id to start search from.", example = "784f394c-42b6-435a-983c-b7beff2784f9")
37 private UUID rootId; 38 private UUID rootId;
38 @ApiModelProperty(position = 2, value = "Type of the root entity.") 39 @ApiModelProperty(position = 2, value = "Type of the root entity.")
39 private EntityType rootType; 40 private EntityType rootType;
@@ -59,6 +60,7 @@ public class RelationsSearchParameters { @@ -59,6 +60,7 @@ public class RelationsSearchParameters {
59 this.fetchLastLevelOnly = fetchLastLevelOnly; 60 this.fetchLastLevelOnly = fetchLastLevelOnly;
60 } 61 }
61 62
  63 + @JsonIgnore
62 public EntityId getEntityId() { 64 public EntityId getEntityId() {
63 return EntityIdFactory.getByTypeAndUuid(rootType, rootId); 65 return EntityIdFactory.getByTypeAndUuid(rootType, rootId);
64 } 66 }
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.server.common.data.rpc; 16 package org.thingsboard.server.common.data.rpc;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import io.swagger.annotations.ApiModel;
  20 +import io.swagger.annotations.ApiModelProperty;
19 import lombok.Data; 21 import lombok.Data;
20 import lombok.EqualsAndHashCode; 22 import lombok.EqualsAndHashCode;
21 import org.thingsboard.server.common.data.BaseData; 23 import org.thingsboard.server.common.data.BaseData;
@@ -24,15 +26,24 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -24,15 +26,24 @@ import org.thingsboard.server.common.data.id.DeviceId;
24 import org.thingsboard.server.common.data.id.RpcId; 26 import org.thingsboard.server.common.data.id.RpcId;
25 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
26 28
  29 +@ApiModel
27 @Data 30 @Data
28 @EqualsAndHashCode(callSuper = true) 31 @EqualsAndHashCode(callSuper = true)
29 public class Rpc extends BaseData<RpcId> implements HasTenantId { 32 public class Rpc extends BaseData<RpcId> implements HasTenantId {
  33 +
  34 + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", readOnly = true)
30 private TenantId tenantId; 35 private TenantId tenantId;
  36 + @ApiModelProperty(position = 4, value = "JSON object with Device Id.", readOnly = true)
31 private DeviceId deviceId; 37 private DeviceId deviceId;
  38 + @ApiModelProperty(position = 5, value = "Expiration time of the request.", readOnly = true)
32 private long expirationTime; 39 private long expirationTime;
  40 + @ApiModelProperty(position = 6, value = "The request body that will be used to send message to device.", readOnly = true)
33 private JsonNode request; 41 private JsonNode request;
  42 + @ApiModelProperty(position = 7, value = "The response from the device.", readOnly = true)
34 private JsonNode response; 43 private JsonNode response;
  44 + @ApiModelProperty(position = 8, value = "The current status of the RPC call.", readOnly = true)
35 private RpcStatus status; 45 private RpcStatus status;
  46 + @ApiModelProperty(position = 9, value = "Additional info used in the rule engine to process the updates to the RPC state.", readOnly = true)
36 private JsonNode additionalInfo; 47 private JsonNode additionalInfo;
37 48
38 public Rpc() { 49 public Rpc() {
@@ -53,4 +64,17 @@ public class Rpc extends BaseData<RpcId> implements HasTenantId { @@ -53,4 +64,17 @@ public class Rpc extends BaseData<RpcId> implements HasTenantId {
53 this.status = rpc.getStatus(); 64 this.status = rpc.getStatus();
54 this.additionalInfo = rpc.getAdditionalInfo(); 65 this.additionalInfo = rpc.getAdditionalInfo();
55 } 66 }
  67 +
  68 + @ApiModelProperty(position = 1, value = "JSON object with the rpc Id. Referencing non-existing rpc Id will cause error.")
  69 + @Override
  70 + public RpcId getId() {
  71 + return super.getId();
  72 + }
  73 +
  74 + @ApiModelProperty(position = 2, value = "Timestamp of the rpc creation, in milliseconds", example = "1609459200000", readOnly = true)
  75 + @Override
  76 + public long getCreatedTime() {
  77 + return super.getCreatedTime();
  78 + }
  79 +
56 } 80 }
@@ -15,17 +15,21 @@ @@ -15,17 +15,21 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.rule; 16 package org.thingsboard.server.common.data.rule;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.Data; 20 import lombok.Data;
19 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
20 22
21 import java.io.Serializable; 23 import java.io.Serializable;
22 24
  25 +@ApiModel
23 @Data 26 @Data
24 @Slf4j 27 @Slf4j
25 public class DefaultRuleChainCreateRequest implements Serializable { 28 public class DefaultRuleChainCreateRequest implements Serializable {
26 29
27 private static final long serialVersionUID = 5600333716030561537L; 30 private static final long serialVersionUID = 5600333716030561537L;
28 31
  32 + @ApiModelProperty(position = 1, required = true, value = "Name of the new rule chain", example = "Root Rule Chain")
29 private String name; 33 private String name;
30 34
31 } 35 }
@@ -15,14 +15,20 @@ @@ -15,14 +15,20 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.rule; 16 package org.thingsboard.server.common.data.rule;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.Data; 20 import lombok.Data;
19 21
20 /** 22 /**
21 * Created by ashvayka on 21.03.18. 23 * Created by ashvayka on 21.03.18.
22 */ 24 */
  25 +@ApiModel
23 @Data 26 @Data
24 public class NodeConnectionInfo { 27 public class NodeConnectionInfo {
  28 + @ApiModelProperty(position = 1, required = true, value = "Index of rule node in the 'nodes' array of the RuleChainMetaData. Indicates the 'from' part of the connection.")
25 private int fromIndex; 29 private int fromIndex;
  30 + @ApiModelProperty(position = 2, required = true, value = "Index of rule node in the 'nodes' array of the RuleChainMetaData. Indicates the 'to' part of the connection.")
26 private int toIndex; 31 private int toIndex;
  32 + @ApiModelProperty(position = 3, required = true, value = "Type of the relation. Typically indicated the result of processing by the 'from' rule node. For example, 'Success' or 'Failure'")
27 private String type; 33 private String type;
28 } 34 }
@@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.rule; @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.rule;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.databind.JsonNode; 19 import com.fasterxml.jackson.databind.JsonNode;
  20 +import io.swagger.annotations.ApiModel;
  21 +import io.swagger.annotations.ApiModelProperty;
20 import lombok.Data; 22 import lombok.Data;
21 import lombok.EqualsAndHashCode; 23 import lombok.EqualsAndHashCode;
22 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
@@ -28,6 +30,7 @@ import org.thingsboard.server.common.data.id.RuleNodeId; @@ -28,6 +30,7 @@ import org.thingsboard.server.common.data.id.RuleNodeId;
28 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.validation.NoXss; 31 import org.thingsboard.server.common.data.validation.NoXss;
30 32
  33 +@ApiModel
31 @Data 34 @Data
32 @EqualsAndHashCode(callSuper = true) 35 @EqualsAndHashCode(callSuper = true)
33 @Slf4j 36 @Slf4j
@@ -35,13 +38,20 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im @@ -35,13 +38,20 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
35 38
36 private static final long serialVersionUID = -5656679015121935465L; 39 private static final long serialVersionUID = -5656679015121935465L;
37 40
  41 + @ApiModelProperty(position = 3, required = true, value = "JSON object with Tenant Id.", readOnly = true)
38 private TenantId tenantId; 42 private TenantId tenantId;
39 @NoXss 43 @NoXss
  44 + @ApiModelProperty(position = 4, required = true, value = "Rule Chain name", example = "Humidity data processing")
40 private String name; 45 private String name;
  46 + @ApiModelProperty(position = 5, value = "Rule Chain type. 'EDGE' rule chains are processing messages on the edge devices only.", example = "A4B72CCDFF33")
41 private RuleChainType type; 47 private RuleChainType type;
  48 + @ApiModelProperty(position = 6, value = "JSON object with Rule Chain Id. Pointer to the first rule node that should receive all messages pushed to this rule chain.")
42 private RuleNodeId firstRuleNodeId; 49 private RuleNodeId firstRuleNodeId;
  50 + @ApiModelProperty(position = 7, value = "Indicates root rule chain. The root rule chain process messages from all devices and entities by default. User may configure default rule chain per device profile.")
43 private boolean root; 51 private boolean root;
  52 + @ApiModelProperty(position = 8, value = "Reserved for future usage.")
44 private boolean debugMode; 53 private boolean debugMode;
  54 + @ApiModelProperty(position = 9, value = "Reserved for future usage. The actual list of rule nodes and their relations is stored in the database separately.")
45 private transient JsonNode configuration; 55 private transient JsonNode configuration;
46 56
47 @JsonIgnore 57 @JsonIgnore
@@ -75,6 +85,21 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im @@ -75,6 +85,21 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
75 return name; 85 return name;
76 } 86 }
77 87
  88 + @ApiModelProperty(position = 1, value = "JSON object with the Rule Chain Id. " +
  89 + "Specify this field to update the Rule Chain. " +
  90 + "Referencing non-existing Rule Chain Id will cause error. " +
  91 + "Omit this field to create new rule chain." )
  92 + @Override
  93 + public RuleChainId getId() {
  94 + return super.getId();
  95 + }
  96 +
  97 + @ApiModelProperty(position = 2, value = "Timestamp of the rule chain creation, in milliseconds", example = "1609459200000", readOnly = true)
  98 + @Override
  99 + public long getCreatedTime() {
  100 + return super.getCreatedTime();
  101 + }
  102 +
78 public JsonNode getConfiguration() { 103 public JsonNode getConfiguration() {
79 return SearchTextBasedWithAdditionalInfo.getJson(() -> configuration, () -> configurationBytes); 104 return SearchTextBasedWithAdditionalInfo.getJson(() -> configuration, () -> configurationBytes);
80 } 105 }
@@ -82,4 +107,5 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im @@ -82,4 +107,5 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
82 public void setConfiguration(JsonNode data) { 107 public void setConfiguration(JsonNode data) {
83 setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); 108 setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes);
84 } 109 }
  110 +
85 } 111 }
@@ -16,16 +16,23 @@ @@ -16,16 +16,23 @@
16 package org.thingsboard.server.common.data.rule; 16 package org.thingsboard.server.common.data.rule;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import io.swagger.annotations.ApiModel;
  20 +import io.swagger.annotations.ApiModelProperty;
19 import lombok.Data; 21 import lombok.Data;
20 import org.thingsboard.server.common.data.id.RuleChainId; 22 import org.thingsboard.server.common.data.id.RuleChainId;
21 23
22 /** 24 /**
23 * Created by ashvayka on 21.03.18. 25 * Created by ashvayka on 21.03.18.
24 */ 26 */
  27 +@ApiModel
25 @Data 28 @Data
26 public class RuleChainConnectionInfo { 29 public class RuleChainConnectionInfo {
  30 + @ApiModelProperty(position = 1, required = true, value = "Index of rule node in the 'nodes' array of the RuleChainMetaData. Indicates the 'from' part of the connection.")
27 private int fromIndex; 31 private int fromIndex;
  32 + @ApiModelProperty(position = 2, required = true, value = "JSON object with the Rule Chain Id.")
28 private RuleChainId targetRuleChainId; 33 private RuleChainId targetRuleChainId;
  34 + @ApiModelProperty(position = 3, required = true, value = "JSON object with the additional information about the connection.")
29 private JsonNode additionalInfo; 35 private JsonNode additionalInfo;
  36 + @ApiModelProperty(position = 4, required = true, value = "Type of the relation. Typically indicated the result of processing by the 'from' rule node. For example, 'Success' or 'Failure'")
30 private String type; 37 private String type;
31 } 38 }
@@ -15,13 +15,18 @@ @@ -15,13 +15,18 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.rule; 16 package org.thingsboard.server.common.data.rule;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.Data; 20 import lombok.Data;
19 21
20 import java.util.List; 22 import java.util.List;
21 23
  24 +@ApiModel
22 @Data 25 @Data
23 public class RuleChainData { 26 public class RuleChainData {
24 27
  28 + @ApiModelProperty(position = 1, required = true, value = "List of the Rule Chain objects.", readOnly = true)
25 List<RuleChain> ruleChains; 29 List<RuleChain> ruleChains;
  30 + @ApiModelProperty(position = 2, required = true, value = "List of the Rule Chain metadata objects.", readOnly = true)
26 List<RuleChainMetaData> metadata; 31 List<RuleChainMetaData> metadata;
27 } 32 }
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.server.common.data.rule; 16 package org.thingsboard.server.common.data.rule;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import io.swagger.annotations.ApiModel;
  20 +import io.swagger.annotations.ApiModelProperty;
19 import lombok.Data; 21 import lombok.Data;
20 import org.thingsboard.server.common.data.id.RuleChainId; 22 import org.thingsboard.server.common.data.id.RuleChainId;
21 23
@@ -25,17 +27,23 @@ import java.util.List; @@ -25,17 +27,23 @@ import java.util.List;
25 /** 27 /**
26 * Created by igor on 3/13/18. 28 * Created by igor on 3/13/18.
27 */ 29 */
  30 +@ApiModel
28 @Data 31 @Data
29 public class RuleChainMetaData { 32 public class RuleChainMetaData {
30 33
  34 + @ApiModelProperty(position = 1, required = true, value = "JSON object with Rule Chain Id.", readOnly = true)
31 private RuleChainId ruleChainId; 35 private RuleChainId ruleChainId;
32 36
  37 + @ApiModelProperty(position = 2, required = true, value = "Index of the first rule node in the 'nodes' list")
33 private Integer firstNodeIndex; 38 private Integer firstNodeIndex;
34 39
  40 + @ApiModelProperty(position = 3, required = true, value = "List of rule node JSON objects")
35 private List<RuleNode> nodes; 41 private List<RuleNode> nodes;
36 42
  43 + @ApiModelProperty(position = 4, required = true, value = "List of JSON objects that represent connections between rule nodes")
37 private List<NodeConnectionInfo> connections; 44 private List<NodeConnectionInfo> connections;
38 45
  46 + @ApiModelProperty(position = 5, required = true, value = "List of JSON objects that represent connections between rule nodes and other rule chains.")
39 private List<RuleChainConnectionInfo> ruleChainConnections; 47 private List<RuleChainConnectionInfo> ruleChainConnections;
40 48
41 public void addConnectionInfo(int fromIndex, int toIndex, String type) { 49 public void addConnectionInfo(int fromIndex, int toIndex, String type) {
@@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.rule; @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.rule;
17 17
18 import com.fasterxml.jackson.annotation.JsonIgnore; 18 import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.databind.JsonNode; 19 import com.fasterxml.jackson.databind.JsonNode;
  20 +import io.swagger.annotations.ApiModel;
  21 +import io.swagger.annotations.ApiModelProperty;
20 import lombok.Data; 22 import lombok.Data;
21 import lombok.EqualsAndHashCode; 23 import lombok.EqualsAndHashCode;
22 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
@@ -25,6 +27,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; @@ -25,6 +27,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
25 import org.thingsboard.server.common.data.id.RuleChainId; 27 import org.thingsboard.server.common.data.id.RuleChainId;
26 import org.thingsboard.server.common.data.id.RuleNodeId; 28 import org.thingsboard.server.common.data.id.RuleNodeId;
27 29
  30 +@ApiModel
28 @Data 31 @Data
29 @EqualsAndHashCode(callSuper = true) 32 @EqualsAndHashCode(callSuper = true)
30 @Slf4j 33 @Slf4j
@@ -32,10 +35,15 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl @@ -32,10 +35,15 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl
32 35
33 private static final long serialVersionUID = -5656679015121235465L; 36 private static final long serialVersionUID = -5656679015121235465L;
34 37
  38 + @ApiModelProperty(position = 3, value = "JSON object with the Rule Chain Id. ", readOnly = true)
35 private RuleChainId ruleChainId; 39 private RuleChainId ruleChainId;
  40 + @ApiModelProperty(position = 4, value = "Full Java Class Name of the rule node implementation. ", example = "com.mycompany.iot.rule.engine.ProcessingNode")
36 private String type; 41 private String type;
  42 + @ApiModelProperty(position = 5, value = "User defined name of the rule node. Used on UI and for logging. ", example = "Process sensor reading")
37 private String name; 43 private String name;
  44 + @ApiModelProperty(position = 6, value = "Enable/disable debug. ", example = "false")
38 private boolean debugMode; 45 private boolean debugMode;
  46 + @ApiModelProperty(position = 7, value = "JSON with the rule node configuration. Structure depends on the rule node implementation.", dataType = "com.fasterxml.jackson.databind.JsonNode")
39 private transient JsonNode configuration; 47 private transient JsonNode configuration;
40 @JsonIgnore 48 @JsonIgnore
41 private byte[] configurationBytes; 49 private byte[] configurationBytes;
@@ -75,4 +83,25 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl @@ -75,4 +83,25 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl
75 setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); 83 setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes);
76 } 84 }
77 85
  86 + @ApiModelProperty(position = 1, value = "JSON object with the Rule Node Id. " +
  87 + "Specify this field to update the Rule Node. " +
  88 + "Referencing non-existing Rule Node Id will cause error. " +
  89 + "Omit this field to create new rule node." )
  90 + @Override
  91 + public RuleNodeId getId() {
  92 + return super.getId();
  93 + }
  94 +
  95 + @ApiModelProperty(position = 2, value = "Timestamp of the rule node creation, in milliseconds", example = "1609459200000", readOnly = true)
  96 + @Override
  97 + public long getCreatedTime() {
  98 + return super.getCreatedTime();
  99 + }
  100 +
  101 + @ApiModelProperty(position = 8, value = "Additional parameters of the rule node. Contains 'layoutX' and 'layoutY' properties for visualization.", dataType = "com.fasterxml.jackson.databind.JsonNode")
  102 + @Override
  103 + public JsonNode getAdditionalInfo() {
  104 + return super.getAdditionalInfo();
  105 + }
  106 +
78 } 107 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.tenant.profile; 16 package org.thingsboard.server.common.data.tenant.profile;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.AllArgsConstructor; 20 import lombok.AllArgsConstructor;
19 import lombok.Builder; 21 import lombok.Builder;
20 import lombok.Data; 22 import lombok.Data;
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
19 import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 19 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
20 import com.fasterxml.jackson.annotation.JsonSubTypes; 20 import com.fasterxml.jackson.annotation.JsonSubTypes;
21 import com.fasterxml.jackson.annotation.JsonTypeInfo; 21 import com.fasterxml.jackson.annotation.JsonTypeInfo;
  22 +import io.swagger.annotations.ApiModel;
22 import org.thingsboard.server.common.data.ApiUsageRecordKey; 23 import org.thingsboard.server.common.data.ApiUsageRecordKey;
23 import org.thingsboard.server.common.data.TenantProfileType; 24 import org.thingsboard.server.common.data.TenantProfileType;
24 25
@@ -15,11 +15,15 @@ @@ -15,11 +15,15 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.tenant.profile; 16 package org.thingsboard.server.common.data.tenant.profile;
17 17
  18 +import io.swagger.annotations.ApiModel;
  19 +import io.swagger.annotations.ApiModelProperty;
18 import lombok.Data; 20 import lombok.Data;
19 21
  22 +@ApiModel
20 @Data 23 @Data
21 public class TenantProfileData { 24 public class TenantProfileData {
22 25
  26 + @ApiModelProperty(position = 1, value = "Complex JSON object that contains profile settings: max devices, max assets, rate limits, etc.")
23 private TenantProfileConfiguration configuration; 27 private TenantProfileConfiguration configuration;
24 28
25 } 29 }
@@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
15 */ 15 */
16 package org.thingsboard.server.dao.model.sql; 16 package org.thingsboard.server.dao.model.sql;
17 17
18 -import com.datastax.oss.driver.api.core.uuid.Uuids;  
19 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
20 import lombok.Data; 19 import lombok.Data;
21 import lombok.EqualsAndHashCode; 20 import lombok.EqualsAndHashCode;
@@ -40,15 +39,14 @@ import javax.persistence.Table; @@ -40,15 +39,14 @@ import javax.persistence.Table;
40 import java.util.UUID; 39 import java.util.UUID;
41 40
42 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ACTION_PROPERTY; 41 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ACTION_PROPERTY;
  42 +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_BODY_PROPERTY;
43 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_COLUMN_FAMILY_NAME; 43 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_COLUMN_FAMILY_NAME;
44 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_EDGE_ID_PROPERTY; 44 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_EDGE_ID_PROPERTY;
45 -import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_BODY_PROPERTY;  
46 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ENTITY_ID_PROPERTY; 45 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ENTITY_ID_PROPERTY;
47 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TENANT_ID_PROPERTY; 46 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TENANT_ID_PROPERTY;
48 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TYPE_PROPERTY; 47 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TYPE_PROPERTY;
49 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_UID_PROPERTY; 48 import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_UID_PROPERTY;
50 import static org.thingsboard.server.dao.model.ModelConstants.EPOCH_DIFF; 49 import static org.thingsboard.server.dao.model.ModelConstants.EPOCH_DIFF;
51 -import static org.thingsboard.server.dao.model.ModelConstants.EVENT_UID_PROPERTY;  
52 import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; 50 import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN;
53 51
54 @Data 52 @Data
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.dao.oauth2; 16 package org.thingsboard.server.dao.oauth2;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.commons.collections.CollectionUtils;
19 import org.springframework.beans.factory.annotation.Autowired; 20 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
21 import org.springframework.util.StringUtils; 22 import org.springframework.util.StringUtils;
@@ -224,7 +225,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se @@ -224,7 +225,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se
224 if (StringUtils.isEmpty(clientRegistration.getAccessTokenUri())) { 225 if (StringUtils.isEmpty(clientRegistration.getAccessTokenUri())) {
225 throw new DataValidationException("Token uri should be specified!"); 226 throw new DataValidationException("Token uri should be specified!");
226 } 227 }
227 - if (StringUtils.isEmpty(clientRegistration.getScope())) { 228 + if (CollectionUtils.isEmpty(clientRegistration.getScope())) {
228 throw new DataValidationException("Scope should be specified!"); 229 throw new DataValidationException("Scope should be specified!");
229 } 230 }
230 if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) { 231 if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) {
@@ -552,6 +552,9 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @@ -552,6 +552,9 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
552 if (!edge.getTenantId().equals(ruleChain.getTenantId())) { 552 if (!edge.getTenantId().equals(ruleChain.getTenantId())) {
553 throw new DataValidationException("Can't assign ruleChain to edge from different tenant!"); 553 throw new DataValidationException("Can't assign ruleChain to edge from different tenant!");
554 } 554 }
  555 + if (!RuleChainType.EDGE.equals(ruleChain.getType())) {
  556 + throw new DataValidationException("Can't assign non EDGE ruleChain to edge!");
  557 + }
555 try { 558 try {
556 createRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE)); 559 createRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
557 } catch (Exception e) { 560 } catch (Exception e) {
@@ -31,10 +31,12 @@ public interface EdgeEventRepository extends PagingAndSortingRepository<EdgeEven @@ -31,10 +31,12 @@ public interface EdgeEventRepository extends PagingAndSortingRepository<EdgeEven
31 "e.tenantId = :tenantId " + 31 "e.tenantId = :tenantId " +
32 "AND e.edgeId = :edgeId " + 32 "AND e.edgeId = :edgeId " +
33 "AND (:startTime IS NULL OR e.createdTime >= :startTime) " + 33 "AND (:startTime IS NULL OR e.createdTime >= :startTime) " +
34 - "AND (:endTime IS NULL OR e.createdTime <= :endTime) " 34 + "AND (:endTime IS NULL OR e.createdTime <= :endTime) " +
  35 + "AND LOWER(e.edgeEventType) LIKE LOWER(CONCAT(:textSearch, '%'))"
35 ) 36 )
36 Page<EdgeEventEntity> findEdgeEventsByTenantIdAndEdgeId(@Param("tenantId") UUID tenantId, 37 Page<EdgeEventEntity> findEdgeEventsByTenantIdAndEdgeId(@Param("tenantId") UUID tenantId,
37 @Param("edgeId") UUID edgeId, 38 @Param("edgeId") UUID edgeId,
  39 + @Param("textSearch") String textSearch,
38 @Param("startTime") Long startTime, 40 @Param("startTime") Long startTime,
39 @Param("endTime") Long endTime, 41 @Param("endTime") Long endTime,
40 Pageable pageable); 42 Pageable pageable);
@@ -44,10 +46,12 @@ public interface EdgeEventRepository extends PagingAndSortingRepository<EdgeEven @@ -44,10 +46,12 @@ public interface EdgeEventRepository extends PagingAndSortingRepository<EdgeEven
44 "AND e.edgeId = :edgeId " + 46 "AND e.edgeId = :edgeId " +
45 "AND (:startTime IS NULL OR e.createdTime >= :startTime) " + 47 "AND (:startTime IS NULL OR e.createdTime >= :startTime) " +
46 "AND (:endTime IS NULL OR e.createdTime <= :endTime) " + 48 "AND (:endTime IS NULL OR e.createdTime <= :endTime) " +
47 - "AND e.edgeEventAction <> 'TIMESERIES_UPDATED'" 49 + "AND e.edgeEventAction <> 'TIMESERIES_UPDATED' " +
  50 + "AND LOWER(e.edgeEventType) LIKE LOWER(CONCAT(:textSearch, '%'))"
48 ) 51 )
49 Page<EdgeEventEntity> findEdgeEventsByTenantIdAndEdgeIdWithoutTimeseriesUpdated(@Param("tenantId") UUID tenantId, 52 Page<EdgeEventEntity> findEdgeEventsByTenantIdAndEdgeIdWithoutTimeseriesUpdated(@Param("tenantId") UUID tenantId,
50 @Param("edgeId") UUID edgeId, 53 @Param("edgeId") UUID edgeId,
  54 + @Param("textSearch") String textSearch,
51 @Param("startTime") Long startTime, 55 @Param("startTime") Long startTime,
52 @Param("endTime") Long endTime, 56 @Param("endTime") Long endTime,
53 Pageable pageable); 57 Pageable pageable);
@@ -39,6 +39,7 @@ import java.sql.Connection; @@ -39,6 +39,7 @@ import java.sql.Connection;
39 import java.sql.PreparedStatement; 39 import java.sql.PreparedStatement;
40 import java.sql.ResultSet; 40 import java.sql.ResultSet;
41 import java.sql.SQLException; 41 import java.sql.SQLException;
  42 +import java.util.Objects;
42 import java.util.Optional; 43 import java.util.Optional;
43 import java.util.UUID; 44 import java.util.UUID;
44 import java.util.concurrent.ConcurrentHashMap; 45 import java.util.concurrent.ConcurrentHashMap;
@@ -107,6 +108,7 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit @@ -107,6 +108,7 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit
107 .findEdgeEventsByTenantIdAndEdgeId( 108 .findEdgeEventsByTenantIdAndEdgeId(
108 tenantId, 109 tenantId,
109 edgeId.getId(), 110 edgeId.getId(),
  111 + Objects.toString(pageLink.getTextSearch(), ""),
110 pageLink.getStartTime(), 112 pageLink.getStartTime(),
111 pageLink.getEndTime(), 113 pageLink.getEndTime(),
112 DaoUtil.toPageable(pageLink))); 114 DaoUtil.toPageable(pageLink)));
@@ -116,6 +118,7 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit @@ -116,6 +118,7 @@ public class JpaBaseEdgeEventDao extends JpaAbstractSearchTextDao<EdgeEventEntit
116 .findEdgeEventsByTenantIdAndEdgeIdWithoutTimeseriesUpdated( 118 .findEdgeEventsByTenantIdAndEdgeIdWithoutTimeseriesUpdated(
117 tenantId, 119 tenantId,
118 edgeId.getId(), 120 edgeId.getId(),
  121 + Objects.toString(pageLink.getTextSearch(), ""),
119 pageLink.getStartTime(), 122 pageLink.getStartTime(),
120 pageLink.getEndTime(), 123 pageLink.getEndTime(),
121 DaoUtil.toPageable(pageLink))); 124 DaoUtil.toPageable(pageLink)));
@@ -39,10 +39,6 @@ import org.thingsboard.server.dao.event.EventDao; @@ -39,10 +39,6 @@ import org.thingsboard.server.dao.event.EventDao;
39 import org.thingsboard.server.dao.model.sql.EventEntity; 39 import org.thingsboard.server.dao.model.sql.EventEntity;
40 import org.thingsboard.server.dao.sql.JpaAbstractDao; 40 import org.thingsboard.server.dao.sql.JpaAbstractDao;
41 41
42 -import java.sql.Connection;  
43 -import java.sql.PreparedStatement;  
44 -import java.sql.ResultSet;  
45 -import java.sql.SQLException;  
46 import java.util.List; 42 import java.util.List;
47 import java.util.Objects; 43 import java.util.Objects;
48 import java.util.Optional; 44 import java.util.Optional;
@@ -196,7 +192,7 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen @@ -196,7 +192,7 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen
196 eventFilter.getEntityId(), 192 eventFilter.getEntityId(),
197 eventFilter.getMsgType(), 193 eventFilter.getMsgType(),
198 eventFilter.isError(), 194 eventFilter.isError(),
199 - eventFilter.getError(), 195 + eventFilter.getErrorStr(),
200 eventFilter.getDataSearch(), 196 eventFilter.getDataSearch(),
201 eventFilter.getMetadataSearch(), 197 eventFilter.getMetadataSearch(),
202 DaoUtil.toPageable(pageLink))); 198 DaoUtil.toPageable(pageLink)));
@@ -212,7 +208,7 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen @@ -212,7 +208,7 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen
212 notNull(pageLink.getEndTime()), 208 notNull(pageLink.getEndTime()),
213 eventFilter.getServer(), 209 eventFilter.getServer(),
214 eventFilter.getMethod(), 210 eventFilter.getMethod(),
215 - eventFilter.getError(), 211 + eventFilter.getErrorStr(),
216 DaoUtil.toPageable(pageLink)) 212 DaoUtil.toPageable(pageLink))
217 ); 213 );
218 } 214 }
@@ -231,7 +227,7 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen @@ -231,7 +227,7 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen
231 eventFilter.getEvent(), 227 eventFilter.getEvent(),
232 statusFilterEnabled, 228 statusFilterEnabled,
233 statusFilter, 229 statusFilter,
234 - eventFilter.getError(), 230 + eventFilter.getErrorStr(),
235 DaoUtil.toPageable(pageLink)) 231 DaoUtil.toPageable(pageLink))
236 ); 232 );
237 } 233 }
@@ -44,10 +44,10 @@ @@ -44,10 +44,10 @@
44 {{ 'event.has-error' | translate }} 44 {{ 'event.has-error' | translate }}
45 </tb-checkbox> 45 </tb-checkbox>
46 </ng-template> 46 </ng-template>
47 - <ng-template [ngSwitchCase]="'error'"> 47 + <ng-template [ngSwitchCase]="'errorStr'">
48 <mat-form-field fxHide [fxShow]="showErrorMsgFields()"> 48 <mat-form-field fxHide [fxShow]="showErrorMsgFields()">
49 <mat-label>{{ column.title | translate}}</mat-label> 49 <mat-label>{{ column.title | translate}}</mat-label>
50 - <input matInput type="text" name="errorSearchText" formControlName="error"> 50 + <input matInput type="text" name="errorSearchText" formControlName="errorStr">
51 </mat-form-field> 51 </mat-form-field>
52 </ng-template> 52 </ng-template>
53 <ng-container *ngSwitchDefault> 53 <ng-container *ngSwitchDefault>
@@ -292,14 +292,14 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { @@ -292,14 +292,14 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
292 case EventType.ERROR: 292 case EventType.ERROR:
293 this.filterColumns.push( 293 this.filterColumns.push(
294 {key: 'method', title: 'event.method'}, 294 {key: 'method', title: 'event.method'},
295 - {key: 'error', title: 'event.error'} 295 + {key: 'errorStr', title: 'event.error'}
296 ); 296 );
297 break; 297 break;
298 case EventType.LC_EVENT: 298 case EventType.LC_EVENT:
299 this.filterColumns.push( 299 this.filterColumns.push(
300 {key: 'event', title: 'event.event'}, 300 {key: 'event', title: 'event.event'},
301 {key: 'status', title: 'event.status'}, 301 {key: 'status', title: 'event.status'},
302 - {key: 'error', title: 'event.error'} 302 + {key: 'errorStr', title: 'event.error'}
303 ); 303 );
304 break; 304 break;
305 case EventType.STATS: 305 case EventType.STATS:
@@ -319,7 +319,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { @@ -319,7 +319,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
319 {key: 'dataSearch', title: 'event.data'}, 319 {key: 'dataSearch', title: 'event.data'},
320 {key: 'metadataSearch', title: 'event.metadata'}, 320 {key: 'metadataSearch', title: 'event.metadata'},
321 {key: 'isError', title: 'event.error'}, 321 {key: 'isError', title: 'event.error'},
322 - {key: 'error', title: 'event.error'} 322 + {key: 'errorStr', title: 'event.error'}
323 ); 323 );
324 break; 324 break;
325 } 325 }
@@ -91,13 +91,13 @@ export interface BaseFilterEventBody { @@ -91,13 +91,13 @@ export interface BaseFilterEventBody {
91 91
92 export interface ErrorFilterEventBody extends BaseFilterEventBody { 92 export interface ErrorFilterEventBody extends BaseFilterEventBody {
93 method?: string; 93 method?: string;
94 - error?: string; 94 + errorStr?: string;
95 } 95 }
96 96
97 export interface LcFilterEventEventBody extends BaseFilterEventBody { 97 export interface LcFilterEventEventBody extends BaseFilterEventBody {
98 event?: string; 98 event?: string;
99 status?: string; 99 status?: string;
100 - error?: string; 100 + errorStr?: string;
101 } 101 }
102 102
103 export interface StatsFilterEventBody extends BaseFilterEventBody { 103 export interface StatsFilterEventBody extends BaseFilterEventBody {
@@ -115,7 +115,7 @@ export interface DebugFilterRuleNodeEventBody extends BaseFilterEventBody { @@ -115,7 +115,7 @@ export interface DebugFilterRuleNodeEventBody extends BaseFilterEventBody {
115 dataSearch?: string; 115 dataSearch?: string;
116 metadataSearch?: string; 116 metadataSearch?: string;
117 isError?: boolean; 117 isError?: boolean;
118 - error?: string; 118 + errorStr?: string;
119 } 119 }
120 120
121 export type FilterEventBody = ErrorFilterEventBody & LcFilterEventEventBody & StatsFilterEventBody & DebugFilterRuleNodeEventBody; 121 export type FilterEventBody = ErrorFilterEventBody & LcFilterEventEventBody & StatsFilterEventBody & DebugFilterRuleNodeEventBody;