Commit 2fea8570b7a1ca2a53ec87ccbe1b53b3f9b79cf0

Authored by Andrii Shvaika
2 parents 2ba9b463 3fda67be

Merge branch 'feature/swagger-alarm-controller' of https://github.com/ShvaykaD/t…

…hingsboard into feature/swagger
... ... @@ -15,8 +15,11 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
  18 +import io.swagger.annotations.ApiOperation;
  19 +import io.swagger.annotations.ApiParam;
18 20 import org.apache.commons.lang3.StringUtils;
19 21 import org.springframework.http.HttpStatus;
  22 +import org.springframework.http.MediaType;
20 23 import org.springframework.security.access.prepost.PreAuthorize;
21 24 import org.springframework.web.bind.annotation.PathVariable;
22 25 import org.springframework.web.bind.annotation.RequestBody;
... ... @@ -55,11 +58,25 @@ import java.util.List;
55 58 public class AlarmController extends BaseController {
56 59
57 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 alarm is owned by the same tenant. " +
  62 + "If the user has the authority of 'Customer User', the server checks that the 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(milliseconds) of the search time range over the alarm object field: 'createdTime'.";
  69 + private static final String ALARM_QUERY_END_TIME_DESCRIPTION = "The end timestamp(milliseconds) of the search time range over the alarm object 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 76 @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET)
61 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 80 checkParameter(ALARM_ID, strAlarmId);
64 81 try {
65 82 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
... ... @@ -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 93 @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET)
74 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 97 checkParameter(ALARM_ID, strAlarmId);
77 98 try {
78 99 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
... ... @@ -82,10 +103,14 @@ 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. Platform generates random Alarm Id during alarm creation. " +
  108 + "The Alarm Id will be present in the response. Specify the Alarm Id when you would like to update the Alarm. " +
  109 + "Referencing non-existing Alarm Id will cause an error.", produces = MediaType.APPLICATION_JSON_VALUE)
  110 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
86 111 @RequestMapping(value = "/alarm", method = RequestMethod.POST)
87 112 @ResponseBody
88   - public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
  113 + public Alarm saveAlarm(@ApiParam(value = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException {
89 114 try {
90 115 alarm.setTenantId(getCurrentUser().getTenantId());
91 116
... ... @@ -106,10 +131,12 @@ public class AlarmController extends BaseController {
106 131 }
107 132 }
108 133
109   - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  134 + @ApiOperation(value = "Delete Alarm (deleteAlarm)",
  135 + notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error.", produces = MediaType.APPLICATION_JSON_VALUE)
  136 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
110 137 @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE)
111 138 @ResponseBody
112   - public Boolean deleteAlarm(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
  139 + public Boolean deleteAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
113 140 checkParameter(ALARM_ID, strAlarmId);
114 141 try {
115 142 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
... ... @@ -124,15 +151,17 @@ public class AlarmController extends BaseController {
124 151 sendAlarmDeleteNotificationMsg(getTenantId(), alarmId, relatedEdgeIds, alarm);
125 152
126 153 return alarmService.deleteAlarm(getTenantId(), alarmId);
127   - } catch (Exception e) {
  154 + } catch (Exception e) {
128 155 throw handleException(e);
129 156 }
130 157 }
131 158
132   - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  159 + @ApiOperation(value = "Acknowledge Alarm (ackAlarm)",
  160 + notes = "Acknowledge the Alarm. Referencing non-existing Alarm Id will cause an error.", produces = MediaType.APPLICATION_JSON_VALUE)
  161 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
133 162 @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST)
134 163 @ResponseStatus(value = HttpStatus.OK)
135   - public void ackAlarm(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
  164 + public void ackAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
136 165 checkParameter(ALARM_ID, strAlarmId);
137 166 try {
138 167 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
... ... @@ -149,10 +178,12 @@ public class AlarmController extends BaseController {
149 178 }
150 179 }
151 180
152   - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  181 + @ApiOperation(value = "Clear Alarm (clearAlarm)",
  182 + notes = "Clear the Alarm. Referencing non-existing Alarm Id will cause an error.", produces = MediaType.APPLICATION_JSON_VALUE)
  183 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
153 184 @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST)
154 185 @ResponseStatus(value = HttpStatus.OK)
155   - public void clearAlarm(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
  186 + public void clearAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
156 187 checkParameter(ALARM_ID, strAlarmId);
157 188 try {
158 189 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
... ... @@ -169,21 +200,36 @@ public class AlarmController extends BaseController {
169 200 }
170 201 }
171 202
  203 + @ApiOperation(value = "Get Alarms (getAlarms)",
  204 + notes = "Returns a page of alarms for the selected entity. Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " +
  205 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
172 206 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
173 207 @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
174 208 @ResponseBody
175 209 public PageData<AlarmInfo> getAlarms(
  210 + @ApiParam(value = ENTITY_TYPE_DESCRIPTION)
176 211 @PathVariable("entityType") String strEntityType,
  212 + @ApiParam(value = ENTITY_ID_DESCRIPTION)
177 213 @PathVariable("entityId") String strEntityId,
  214 + @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
178 215 @RequestParam(required = false) String searchStatus,
  216 + @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
179 217 @RequestParam(required = false) String status,
  218 + @ApiParam(value = PAGE_SIZE_DESCRIPTION)
180 219 @RequestParam int pageSize,
  220 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
181 221 @RequestParam int page,
  222 + @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
182 223 @RequestParam(required = false) String textSearch,
  224 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = SORT_PROPERTY_ALLOWABLE_VALUES)
183 225 @RequestParam(required = false) String sortProperty,
  226 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
184 227 @RequestParam(required = false) String sortOrder,
  228 + @ApiParam(value = ALARM_QUERY_START_TIME_DESCRIPTION)
185 229 @RequestParam(required = false) Long startTime,
  230 + @ApiParam(value = ALARM_QUERY_END_TIME_DESCRIPTION)
186 231 @RequestParam(required = false) Long endTime,
  232 + @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION)
187 233 @RequestParam(required = false) Boolean fetchOriginator
188 234 ) throws ThingsboardException {
189 235 checkParameter("EntityId", strEntityId);
... ... @@ -204,20 +250,35 @@ public class AlarmController extends BaseController {
204 250 throw handleException(e);
205 251 }
206 252 }
207   -
208   - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  253 + @ApiOperation(value = "Get All Alarms (getAllAlarms)",
  254 + notes = "Returns a page of alarms that belongs to the current user owner. " +
  255 + "If the user has the authority of 'Tenant Administrator', the server returns alarms that belongs to the tenant of current user. " +
  256 + "If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " +
  257 + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " +
  258 + PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
  259 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
209 260 @RequestMapping(value = "/alarms", method = RequestMethod.GET)
210 261 @ResponseBody
211 262 public PageData<AlarmInfo> getAllAlarms(
  263 + @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
212 264 @RequestParam(required = false) String searchStatus,
  265 + @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
213 266 @RequestParam(required = false) String status,
  267 + @ApiParam(value = PAGE_SIZE_DESCRIPTION)
214 268 @RequestParam int pageSize,
  269 + @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
215 270 @RequestParam int page,
  271 + @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
216 272 @RequestParam(required = false) String textSearch,
  273 + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = SORT_PROPERTY_ALLOWABLE_VALUES)
217 274 @RequestParam(required = false) String sortProperty,
  275 + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
218 276 @RequestParam(required = false) String sortOrder,
  277 + @ApiParam(value = ALARM_QUERY_START_TIME_DESCRIPTION)
219 278 @RequestParam(required = false) Long startTime,
  279 + @ApiParam(value = ALARM_QUERY_END_TIME_DESCRIPTION)
220 280 @RequestParam(required = false) Long endTime,
  281 + @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION)
221 282 @RequestParam(required = false) Boolean fetchOriginator
222 283 ) throws ThingsboardException {
223 284 AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
... ... @@ -239,13 +300,19 @@ public class AlarmController extends BaseController {
239 300 }
240 301 }
241 302
242   - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  303 + @ApiOperation(value = "Get Highest Alarm Severity (getHighestAlarmSeverity)",
  304 + notes = "Returns highest AlarmSeverity object for the selected entity.", produces = MediaType.APPLICATION_JSON_VALUE)
  305 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
243 306 @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET)
244 307 @ResponseBody
245 308 public AlarmSeverity getHighestAlarmSeverity(
  309 + @ApiParam(value = ENTITY_TYPE_DESCRIPTION)
246 310 @PathVariable("entityType") String strEntityType,
  311 + @ApiParam(value = ENTITY_ID_DESCRIPTION)
247 312 @PathVariable("entityId") String strEntityId,
  313 + @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
248 314 @RequestParam(required = false) String searchStatus,
  315 + @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
249 316 @RequestParam(required = false) String status
250 317 ) throws ThingsboardException {
251 318 checkParameter("EntityId", strEntityId);
... ...
... ... @@ -168,7 +168,7 @@ public abstract class BaseController {
168 168 public static final String EDGE_ID_PARAM_DESCRIPTION = "A string value representing the edge id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
169 169 public static final String CUSTOMER_ID_PARAM_DESCRIPTION = "A string value representing the customer id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
170 170 public static final String ASSET_ID_PARAM_DESCRIPTION = "A string value representing the asset id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
171   -
  171 + public static final String ALARM_ID_PARAM_DESCRIPTION = "A string value representing the alarm id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
172 172
173 173 protected final String PAGE_SIZE_DESCRIPTION = "Maximum amount of entities in a one page";
174 174 protected final String PAGE_NUMBER_DESCRIPTION = "Sequence number of page starting from 0";
... ... @@ -188,11 +188,15 @@ public abstract class BaseController {
188 188 protected final String SORT_ORDER_ALLOWABLE_VALUES = "ASC, DESC";
189 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 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 + protected final String ALARM_INFO_DESCRIPTION = "Alarm Info is an extension of the default Alarm object that contains information about alarm originator name.";
191 192
192 193
193 194 protected final String DEVICE_NAME_DESCRIPTION = "A string value representing the Device name.";
194 195 protected final String ASSET_NAME_DESCRIPTION = "A string value representing the Asset name.";
195 196
  197 + protected static final String ENTITY_TYPE_DESCRIPTION = "A string value representing the entity type. For example, 'DEVICE'";
  198 + protected static final String ENTITY_ID_DESCRIPTION = "A string value representing the entity id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
  199 +
196 200 public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
197 201 protected static final String DEFAULT_DASHBOARD = "defaultDashboardId";
198 202 protected static final String HOME_DASHBOARD = "homeDashboardId";
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.alarm;
17 17
18 18 import com.fasterxml.jackson.annotation.JsonProperty;
19 19 import com.fasterxml.jackson.databind.JsonNode;
  20 +import io.swagger.annotations.ApiModelProperty;
20 21 import lombok.AllArgsConstructor;
21 22 import lombok.Builder;
22 23 import lombok.Data;
... ... @@ -39,18 +40,35 @@ import java.util.List;
39 40 @AllArgsConstructor
40 41 public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, HasCustomerId {
41 42
  43 + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id", readOnly = true)
42 44 private TenantId tenantId;
  45 +
  46 + @ApiModelProperty(position = 4, value = "JSON object with Customer Id", readOnly = true)
43 47 private CustomerId customerId;
  48 +
  49 + @ApiModelProperty(position = 6, required = true, value = "representing type of the Alarm", example = "High Temperature Alarm")
44 50 private String type;
  51 + @ApiModelProperty(position = 7, required = true, value = "JSON object with alarm originator id")
45 52 private EntityId originator;
  53 + @ApiModelProperty(position = 8, required = true, value = "Alarm severity", example = "CRITICAL")
46 54 private AlarmSeverity severity;
  55 + @ApiModelProperty(position = 9, required = true, value = "Alarm status", example = "CLEARED_UNACK")
47 56 private AlarmStatus status;
  57 + @ApiModelProperty(position = 10, value = "Timestamp of the alarm start time, in milliseconds", example = "1634058704565")
48 58 private long startTs;
  59 + @ApiModelProperty(position = 11, value = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522")
49 60 private long endTs;
  61 + @ApiModelProperty(position = 12, value = "Timestamp of the alarm acknowledgement, in milliseconds", example = "1634115221948")
50 62 private long ackTs;
  63 + @ApiModelProperty(position = 13, value = "Timestamp of the alarm clearing, in milliseconds", example = "1634114528465")
51 64 private long clearTs;
  65 + @ApiModelProperty(position = 14, value = "JSON object with alarm details")
52 66 private transient JsonNode details;
  67 + @ApiModelProperty(position = 15, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true")
53 68 private boolean propagate;
  69 + @ApiModelProperty(position = 16, value = "JSON array of relation types that should be used for propagation. " +
  70 + "By default, 'propagateRelationTypes' array is empty which means that the alarm will propagate based on any relation type to parent entities. " +
  71 + "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will ignoned.")
54 72 private List<String> propagateRelationTypes;
55 73
56 74 public Alarm() {
... ... @@ -81,7 +99,25 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
81 99
82 100 @Override
83 101 @JsonProperty(access = JsonProperty.Access.READ_ONLY)
  102 + @ApiModelProperty(position = 5, required = true, value = "representing type of the Alarm", example = "High Temperature Alarm")
84 103 public String getName() {
85 104 return type;
86 105 }
  106 +
  107 + @ApiModelProperty(position = 1, value = "JSON object with the alarm Id. " +
  108 + "Specify this field to update the alarm. " +
  109 + "Referencing non-existing alarm Id will cause error. " +
  110 + "Omit this field to create new alarm." )
  111 + @Override
  112 + public AlarmId getId() {
  113 + return super.getId();
  114 + }
  115 +
  116 +
  117 + @ApiModelProperty(position = 2, value = "Timestamp of the alarm creation, in milliseconds", example = "1634058704567", readOnly = true)
  118 + @Override
  119 + public long getCreatedTime() {
  120 + return super.getCreatedTime();
  121 + }
  122 +
87 123 }
... ...
... ... @@ -15,10 +15,13 @@
15 15 */
16 16 package org.thingsboard.server.common.data.alarm;
17 17
  18 +import io.swagger.annotations.ApiModelProperty;
  19 +
18 20 public class AlarmInfo extends Alarm {
19 21
20 22 private static final long serialVersionUID = 2807343093519543363L;
21 23
  24 + @ApiModelProperty(position = 17, value = "Alarm originator name", example = "Thermostat")
22 25 private String originatorName;
23 26
24 27 public AlarmInfo() {
... ...