/* * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hawkular.alerts.rest; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.hawkular.alerts.rest.CommonUtil.isEmpty; import static org.hawkular.alerts.rest.CommonUtil.parseTagQuery; import static org.hawkular.alerts.rest.CommonUtil.parseTags; import static org.hawkular.alerts.rest.HawkularAlertsApp.TENANT_HEADER_NAME; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.ejb.EJB; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.hawkular.alerts.api.model.data.Data; import org.hawkular.alerts.api.model.event.Alert; import org.hawkular.alerts.api.model.paging.Page; import org.hawkular.alerts.api.model.paging.Pager; import org.hawkular.alerts.api.services.AlertsCriteria; import org.hawkular.alerts.api.services.AlertsService; import org.hawkular.alerts.engine.service.AlertsEngine; import org.hawkular.alerts.rest.ResponseUtil.ApiDeleted; import org.hawkular.alerts.rest.ResponseUtil.ApiError; import org.jboss.logging.Logger; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; /** * REST endpoint for alerts * * @author Jay Shaughnessy * @author Lucas Ponce */ @Path("/") @Api(value = "/*", description = "Alerts Handling") // '/*' is a trick to manipulate root endpoint in apidoc.groovy public class AlertsHandler { private final Logger log = Logger.getLogger(AlertsHandler.class); private static final Map<String, Set<String>> queryParamValidationMap = new HashMap<>(); static { ResponseUtil.populateQueryParamsMap(AlertsHandler.class, queryParamValidationMap); } @HeaderParam(TENANT_HEADER_NAME) String tenantId; @EJB AlertsService alertsService; @EJB AlertsEngine alertsEngine; @EJB StreamWatcher streamWatcher; public AlertsHandler() { log.debug("Creating instance."); } @GET @Path("/") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get alerts with optional filtering", notes = "If not criteria defined, it fetches all alerts available in the system. + \n" + "Tags Query language (BNF): + \n" + "[source] \n" + "---- \n" + "<tag_query> ::= ( <expression> | \"(\" <object> \")\" " + "| <object> <logical_operator> <object> ) \n" + "<expression> ::= ( <tag_name> | <not> <tag_name> " + "| <tag_name> <boolean_operator> <tag_value> | " + "<tag_name> <array_operator> <array> ) \n" + "<not> ::= [ \"NOT\" | \"not\" ] \n" + "<logical_operator> ::= [ \"AND\" | \"OR\" | \"and\" | \"or\" ] \n" + "<boolean_operator> ::= [ \"=\" | \"!=\" ] \n" + "<array_operator> ::= [ \"IN\" | \"NOT IN\" | \"in\" | \"not in\" ] \n" + "<array> ::= ( \"[\" \"]\" | \"[\" ( \",\" <tag_value> )* ) \n" + "<tag_name> ::= <identifier> \n" + "<tag_value> ::= ( \"'\" <regexp> \"'\" | <simple_value> ) \n" + "; \n" + "; <identifier> and <simple_value> follow pattern [a-zA-Z_0-9][\\-a-zA-Z_0-9]* \n" + "; <regexp> follows any valid Java Regular Expression format \n" + "---- \n", response = Alert.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched list of alerts."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) @QueryParamValidation(name = "findAlerts") public Response findAlerts( @ApiParam(required = false, value = "Filter out alerts created before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startTime") final Long startTime, @ApiParam(required = false, value = "Filter out alerts created after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endTime") final Long endTime, @ApiParam(required = false, value = "Filter out alerts for unspecified alertIds.", allowableValues = "Comma separated list of alert IDs.") @QueryParam("alertIds") final String alertIds, @ApiParam(required = false, value = "Filter out alerts for unspecified triggers. ", allowableValues = "Comma separated list of trigger IDs.") @QueryParam("triggerIds") final String triggerIds, @ApiParam(required = false, value = "Filter out alerts for unspecified lifecycle status.", allowableValues = "Comma separated list of [OPEN, ACKNOWLEDGED, RESOLVED]") @QueryParam("statuses") final String statuses, @ApiParam(required = false, value = "Filter out alerts for unspecified severity. ", allowableValues = "Comma separated list of [LOW, MEDIUM, HIGH, CRITICAL]") @QueryParam("severities") final String severities, @ApiParam(required = false, value = "[DEPRECATED] Filter out alerts for unspecified tags.", allowableValues = "Comma separated list of tags, each tag of format 'name\\|value'. + \n" + "Specify '*' for value to match all values.") @QueryParam("tags") final String tags, @ApiParam(required = false, value = "Filter out alerts for unspecified tags.", allowableValues = "A tag query expression.") @QueryParam("tagQuery") final String tagQuery, @ApiParam(required = false, value = "Filter out alerts resolved before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startResolvedTime") final Long startResolvedTime, @ApiParam(required = false, value = "Filter out alerts resolved after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endResolvedTime") final Long endResolvedTime, @ApiParam(required = false, value = "Filter out alerts acknowledged before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startAckTime") final Long startAckTime, @ApiParam(required = false, value = "Filter out alerts acknowledged after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endAckTime") final Long endAckTime, @ApiParam(required = false, value = "Filter out alerts with some lifecycle state before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startStatusTime") final Long startStatusTime, @ApiParam(required = false, value = "Filter out alerts with some lifecycle after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endStatusTime") final Long endStatusTime, @ApiParam(required = false, value = "Return only thin alerts, do not include: evalSets, resolvedEvalSets.") @QueryParam("thin") final Boolean thin, @Context final UriInfo uri) { try { ResponseUtil.checkForUnknownQueryParams(uri, queryParamValidationMap.get("findAlerts")); Pager pager = RequestUtil.extractPaging(uri); /* We maintain old tags criteria as deprecated (it can be removed in a next major version). If present, the tags criteria has precedence over tagQuery parameter. */ String unifiedTagQuery; if (!isEmpty(tags)) { unifiedTagQuery = parseTagQuery(parseTags(tags)); } else { unifiedTagQuery = tagQuery; } AlertsCriteria criteria = new AlertsCriteria(startTime, endTime, alertIds, triggerIds, statuses, severities, unifiedTagQuery, startResolvedTime, endResolvedTime, startAckTime, endAckTime, startStatusTime, endStatusTime, thin); Page<Alert> alertPage = alertsService.getAlerts(tenantId, criteria, pager); log.debugf("Alerts: %s", alertPage); if (isEmpty(alertPage)) { return ResponseUtil.ok(alertPage); } return ResponseUtil.paginatedOk(alertPage, uri); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/watch") @Produces(APPLICATION_JSON) @ApiOperation(value = "Watch alerts with optional filtering", notes = "Return a stream of alerts ordered by the current lifecycle stime. + \n" + "Changes on lifecycle alert are monitored and sent by the watcher. + \n" + " + \n" + "If not criteria defined, it fetches all alerts available in the system. + \n" + " + \n" + "Time criterias are used only for the initial query. + \n" + "After initial query, time criterias are discarded, watching alerts by current lifecycle stime. + \n" + "Non time criterias are active. + \n" + " + \n" + "If not criteria defined, it fetches all alerts available in the system. + \n" + "Tags Query language (BNF): + \n" + "[source] \n" + "---- \n" + "<tag_query> ::= ( <expression> | \"(\" <object> \")\" " + "| <object> <logical_operator> <object> ) \n" + "<expression> ::= ( <tag_name> | <not> <tag_name> " + "| <tag_name> <boolean_operator> <tag_value> | " + "<tag_name> <array_operator> <array> ) \n" + "<not> ::= [ \"NOT\" | \"not\" ] \n" + "<logical_operator> ::= [ \"AND\" | \"OR\" | \"and\" | \"or\" ] \n" + "<boolean_operator> ::= [ \"=\" | \"!=\" ] \n" + "<array_operator> ::= [ \"IN\" | \"NOT IN\" | \"in\" | \"not in\" ] \n" + "<array> ::= ( \"[\" \"]\" | \"[\" ( \",\" <tag_value> )* ) \n" + "<tag_name> ::= <identifier> \n" + "<tag_value> ::= ( \"'\" <regexp> \"'\" | <simple_value> ) \n" + "; \n" + "; <identifier> and <simple_value> follow pattern [a-zA-Z_0-9][\\-a-zA-Z_0-9]* \n" + "; <regexp> follows any valid Java Regular Expression format \n" + "---- \n", response = Alert.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Stream of alerts.", response = Alert.class), @ApiResponse(code = 200, message = "Errors will close the stream. Description is sent before stream is closed.", response = ResponseUtil.ApiError.class) }) @QueryParamValidation(name = "watchAlerts") public Response watchAlerts( @ApiParam(required = false, value = "Filter out alerts created before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startTime") final Long startTime, @ApiParam(required = false, value = "Filter out alerts created after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endTime") final Long endTime, @ApiParam(required = false, value = "Filter out alerts for unspecified alertIds.", allowableValues = "Comma separated list of alert IDs.") @QueryParam("alertIds") final String alertIds, @ApiParam(required = false, value = "Filter out alerts for unspecified triggers. ", allowableValues = "Comma separated list of trigger IDs.") @QueryParam("triggerIds") final String triggerIds, @ApiParam(required = false, value = "Filter out alerts for unspecified lifecycle status.", allowableValues = "Comma separated list of [OPEN, ACKNOWLEDGED, RESOLVED]") @QueryParam("statuses") final String statuses, @ApiParam(required = false, value = "Filter out alerts for unspecified severity. ", allowableValues = "Comma separated list of [LOW, MEDIUM, HIGH, CRITICAL]") @QueryParam("severities") final String severities, @ApiParam(required = false, value = "[DEPRECATED] Filter out alerts for unspecified tags.", allowableValues = "Comma separated list of tags, each tag of format 'name\\|value'. + \n" + "Specify '*' for value to match all values.") @QueryParam("tags") final String tags, @ApiParam(required = false, value = "Filter out alerts for unspecified tags.", allowableValues = "A tag query expression.") @QueryParam("tagQuery") final String tagQuery, @ApiParam(required = false, value = "Filter out alerts resolved before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startResolvedTime") final Long startResolvedTime, @ApiParam(required = false, value = "Filter out alerts resolved after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endResolvedTime") final Long endResolvedTime, @ApiParam(required = false, value = "Filter out alerts acknowledged before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startAckTime") final Long startAckTime, @ApiParam(required = false, value = "Filter out alerts acknowledged after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endAckTime") final Long endAckTime, @ApiParam(required = false, value = "Filter out alerts with some lifecycle state before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startStatusTime") final Long startStatusTime, @ApiParam(required = false, value = "Filter out alerts with some lifecycle after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endStatusTime") final Long endStatusTime, @ApiParam(required = false, value = "Define interval when watcher notifications will be sent.", allowableValues = "Interval in seconds") @QueryParam("watchInterval") final Long watchInterval, @ApiParam(required = false, value = "Return only thin alerts, do not include: evalSets, resolvedEvalSets.") @QueryParam("thin") final Boolean thin, @Context final UriInfo uri) { try { ResponseUtil.checkForUnknownQueryParams(uri, queryParamValidationMap.get("watchAlerts")); String unifiedTagQuery; if (!isEmpty(tags)) { unifiedTagQuery = parseTagQuery(parseTags(tags)); } else { unifiedTagQuery = tagQuery; } AlertsCriteria criteria = new AlertsCriteria(startTime, endTime, alertIds, triggerIds, statuses, severities, unifiedTagQuery, startResolvedTime, endResolvedTime, startAckTime, endAckTime, startStatusTime, endStatusTime, thin); return Response.ok(streamWatcher.watchAlerts(Collections.singleton(tenantId), criteria, watchInterval)) .build(); } catch (Exception e) { log.debug(e.getMessage(), e); if (e instanceof IllegalArgumentException || (e.getCause() != null && e.getCause() instanceof IllegalArgumentException)) { return ResponseUtil.badRequest("Bad arguments: " + e.getMessage()); } return ResponseUtil.internalError(e); } } @PUT @Path("/ack/{alertId}") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Set one alert Acknowledged.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alert Acknowledged invoked successfully."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response ackAlert( @ApiParam(required = true, value = "The alertId to Ack.", allowableValues = "An existing alertId.") @PathParam("alertId") final String alertId, @ApiParam(required = false, value = "User acknowledging the alerts.") @QueryParam("ackBy") final String ackBy, @ApiParam(required = false, value = "Additional notes associated with the acknowledgement.") @QueryParam("ackNotes") final String ackNotes) { try { if (!isEmpty(alertId)) { alertsService.ackAlerts(tenantId, Arrays.asList(alertId), ackBy, ackNotes); log.debugf("AlertId: %s", alertId); return ResponseUtil.ok(); } return ResponseUtil.badRequest("AlertId required for ack"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/note/{alertId}") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Add a note into an existing Alert.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alert note added successfully."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response addAlertNote( @ApiParam(required = true, value = "The alertId to add the note.", allowableValues = "An existing alertId.") @PathParam("alertId") final String alertId, @ApiParam(required = false, value = "Author of the note.") @QueryParam("user") final String user, @ApiParam(required = false, value = "Text of the note.") @QueryParam("text") final String text) { try { if (!isEmpty(alertId)) { alertsService.addNote(tenantId, alertId, user, text); log.debugf("AlertId: %s", alertId); return ResponseUtil.ok(); } return ResponseUtil.badRequest("AlertId required for adding notes"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/tags") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Add tags to existing Alerts.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alerts tagged successfully."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response addTags( @ApiParam(required = true, value = "List of alerts to tag.", allowableValues = "Comma separated list of alert IDs.") @QueryParam("alertIds") final String alertIds, @ApiParam(required = true, value = "List of tags to add.", allowableValues = "Comma separated list of tags. + \n" + "Each tag of format 'name\\|value'.") @QueryParam("tags") final String tags) { try { if (!isEmpty(alertIds) && !isEmpty(tags)) { List<String> alertIdList = Arrays.asList(alertIds.split(",")); Map<String, String> tagsMap = parseTags(tags); alertsService.addAlertTags(tenantId, alertIdList, tagsMap); log.debugf("Tagged alertIds:%s, %s", alertIdList, tagsMap); return ResponseUtil.ok(); } return ResponseUtil.badRequest("AlertIds and Tags required for adding tags"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @DELETE @Path("/tags") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Remove tags from existing Alerts.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alerts untagged successfully."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response deleteTags( @ApiParam(required = true, value = "List of alerts to untag.", allowableValues = "Comma separated list of alert IDs.") @QueryParam("alertIds") final String alertIds, @ApiParam(required = true, value = "List of tag names to remove.", allowableValues = "Comma separated list of tags names.") @QueryParam("tagNames") final String tagNames) { try { if (!isEmpty(alertIds) && !isEmpty(tagNames)) { Collection<String> ids = Arrays.asList(alertIds.split(",")); Collection<String> tags = Arrays.asList(tagNames.split(",")); alertsService.removeAlertTags(tenantId, ids, tags); log.debugf("Untagged alertIds:%s, %s", ids, tags); return ResponseUtil.ok(); } return ResponseUtil.badRequest("AlertIds and Tags required for removing tags"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/ack") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Set one or more alerts Acknowledged.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alerts Acknowledged invoked successfully."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response ackAlerts( @ApiParam(required = true, value = "List of alerts to Ack.", allowableValues = "Comma separated list of alert IDs.") @QueryParam("alertIds") final String alertIds, @ApiParam(required = false, value = "User acknowledging the alerts.") @QueryParam("ackBy") final String ackBy, @ApiParam(required = false, value = "Additional notes associated with the acknowledgement.") @QueryParam("ackNotes") final String ackNotes) { try { if (!isEmpty(alertIds)) { alertsService.ackAlerts(tenantId, Arrays.asList(alertIds.split(",")), ackBy, ackNotes); log.debugf("Acked alertIds: %s", alertIds); return ResponseUtil.ok(); } return ResponseUtil.badRequest("AlertIds required for ack"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @DELETE @Path("/{alertId}") @ApiOperation(value = "Delete an existing Alert.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alert deleted."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Alert not found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response deleteAlert( @ApiParam(required = true, value = "Alert id to be deleted.") @PathParam("alertId") final String alertId) { try { AlertsCriteria criteria = new AlertsCriteria(); criteria.setAlertId(alertId); int numDeleted = alertsService.deleteAlerts(tenantId, criteria); if (1 == numDeleted) { log.debugf("AlertId: %s", alertId); return ResponseUtil.ok(); } return ResponseUtil.notFound("Alert " + alertId + " doesn't exist for delete"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/delete") @Produces(APPLICATION_JSON) @ApiOperation(value = "Delete alerts with optional filtering.", notes = "Return number of alerts deleted. + \n" + "WARNING: If not criteria defined, it deletes all alerts stored in the system. + \n" + "Tags Query language (BNF): + \n" + "[source] \n" + "---- \n" + "<tag_query> ::= ( <expression> | \"(\" <object> \")\" " + "| <object> <logical_operator> <object> ) \n" + "<expression> ::= ( <tag_name> | <not> <tag_name> " + "| <tag_name> <boolean_operator> <tag_value> | " + "<tag_name> <array_operator> <array> ) \n" + "<not> ::= [ \"NOT\" | \"not\" ] \n" + "<logical_operator> ::= [ \"AND\" | \"OR\" | \"and\" | \"or\" ] \n" + "<boolean_operator> ::= [ \"=\" | \"!=\" ] \n" + "<array_operator> ::= [ \"IN\" | \"NOT IN\" | \"in\" | \"not in\" ] \n" + "<array> ::= ( \"[\" \"]\" | \"[\" ( \",\" <tag_value> )* ) \n" + "<tag_name> ::= <identifier> \n" + "<tag_value> ::= ( \"'\" <regexp> \"'\" | <simple_value> ) \n" + "; \n" + "; <identifier> and <simple_value> follow pattern [a-zA-Z_0-9][\\-a-zA-Z_0-9]* \n" + "; <regexp> follows any valid Java Regular Expression format \n" + "---- \n", response = ApiDeleted.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alerts deleted."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) @QueryParamValidation(name = "deleteAlerts") public Response deleteAlerts( @ApiParam(required = false, value = "Filter out alerts created before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startTime") final Long startTime, @ApiParam(required = false, value = "Filter out alerts created after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endTime") final Long endTime, @ApiParam(required = false, value = "Filter out alerts for unspecified alertIds.", allowableValues = "Comma separated list of alert IDs.") @QueryParam("alertIds") final String alertIds, @ApiParam(required = false, value = "Filter out alerts for unspecified triggers.", allowableValues = "Comma separated list of trigger IDs.") @QueryParam("triggerIds") final String triggerIds, @ApiParam(required = false, value = "Filter out alerts for unspecified lifecycle status.", allowableValues = "Comma separated list of [OPEN, ACKNOWLEDGED, RESOLVED]") @QueryParam("statuses") final String statuses, @ApiParam(required = false, value = "Filter out alerts for unspecified severity.", allowableValues = "Comma separated list of [LOW, MEDIUM, HIGH, CRITICAL]") @QueryParam("severities") final String severities, @ApiParam(required = false, value = "[DEPRECATED] Filter out alerts for unspecified tags.", allowableValues = "Comma separated list of tags, each tag of format 'name\\|value'. + \n" + "Specify '*' for value to match all values.") @QueryParam("tags") final String tags, @ApiParam(required = false, value = "Filter out alerts for unspecified tags.", allowableValues = "A tag query expression.") @QueryParam("tagQuery") final String tagQuery, @ApiParam(required = false, value = "Filter out alerts resolved before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startResolvedTime") final Long startResolvedTime, @ApiParam(required = false, value = "Filter out alerts resolved after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endResolvedTime") final Long endResolvedTime, @ApiParam(required = false, value = "Filter out alerts acknowledged before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startAckTime") final Long startAckTime, @ApiParam(required = false, value = "Filter out alerts acknowledged after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endAckTime") final Long endAckTime, @ApiParam(required = false, value = "Filter out alerts with some lifecycle state before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startStatusTime") final Long startStatusTime, @ApiParam(required = false, value = "Filter out alerts with some lifecycle after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endStatusTime") final Long endStatusTime, @Context final UriInfo uri ) { try { ResponseUtil.checkForUnknownQueryParams(uri, queryParamValidationMap.get("deleteAlerts")); /* We maintain old tags criteria as deprecated (it can be removed in a next major version). If present, the tags criteria has precedence over tagQuery parameter. */ String unifiedTagQuery; if (!isEmpty(tags)) { unifiedTagQuery = parseTagQuery(parseTags(tags)); } else { unifiedTagQuery = tagQuery; } AlertsCriteria criteria = new AlertsCriteria(startTime, endTime, alertIds, triggerIds, statuses, severities, unifiedTagQuery, startResolvedTime, endResolvedTime, startAckTime, endAckTime, startStatusTime, endStatusTime, null); int numDeleted = alertsService.deleteAlerts(tenantId, criteria); log.debugf("Alerts deleted: %d", numDeleted); return ResponseUtil.ok(new ApiDeleted(numDeleted)); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/alert/{alertId}") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get an existing Alert.", response = Alert.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alert found."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Alert not found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error", response = ApiError.class) }) public Response getAlert( @ApiParam(value = "Id of alert to be retrieved", required = true) @PathParam("alertId") final String alertId, @ApiParam(required = false, value = "Return only a thin alert, do not include: evalSets, resolvedEvalSets.") @QueryParam("thin") final Boolean thin) { try { Alert found = alertsService.getAlert(tenantId, alertId, ((null == thin) ? false : thin.booleanValue())); if (found != null) { log.debugf("Alert: %s", found); return ResponseUtil.ok(found); } return ResponseUtil.notFound("alertId: " + alertId + " not found"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/resolve/{alertId}") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Set one alert Resolved.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alerts Resolution invoked successfully."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response resolveAlert( @ApiParam(required = true, value = "The alertId to set resolved.") @PathParam("alertId") final String alertId, @ApiParam(required = false, value = "User resolving the alerts.") @QueryParam("resolvedBy") final String resolvedBy, @ApiParam(required = false, value = "Additional notes associated with the resolution.") @QueryParam("resolvedNotes") final String resolvedNotes) { try { if (!isEmpty(alertId)) { alertsService.resolveAlerts(tenantId, Arrays.asList(alertId), resolvedBy, resolvedNotes, null); log.debugf("AlertId: %s", alertId); return ResponseUtil.ok(); } return ResponseUtil.badRequest("AlertsId required for resolve"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/resolve") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Set one or more alerts resolved.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Alerts Resolution invoked successfully."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Alert not found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response resolveAlerts( @ApiParam(required = true, value = "List of alertIds to set resolved.", allowableValues = "Comma separated list of alert IDs.") @QueryParam("alertIds") final String alertIds, @ApiParam(required = false, value = "User resolving the alerts.") @QueryParam("resolvedBy") final String resolvedBy, @ApiParam(required = false, value = "Additional notes associated with the resolution.") @QueryParam("resolvedNotes") final String resolvedNotes) { try { if (!isEmpty(alertIds)) { alertsService.resolveAlerts(tenantId, Arrays.asList(alertIds.split(",")), resolvedBy, resolvedNotes, null); log.debugf("AlertsIds: %s", alertIds); return ResponseUtil.ok(); } return ResponseUtil.badRequest("AlertsIds required for resolve"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/data") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Send data for alert processing/condition evaluation.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, data added."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response sendData( @ApiParam(required = true, name = "datums", value = "Data to be processed by alerting.") final Collection<Data> datums) { try { if (isEmpty(datums)) { return ResponseUtil.badRequest("Data is empty"); } for (Data d : datums) { d.setTenantId(tenantId); } alertsService.sendData(datums); log.debugf("Datums: %s", datums); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } }