/* * 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.HawkularAlertsApp.TENANT_HEADER_NAME; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; 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.action.Action; import org.hawkular.alerts.api.model.action.ActionDefinition; import org.hawkular.alerts.api.model.paging.Page; import org.hawkular.alerts.api.model.paging.Pager; import org.hawkular.alerts.api.services.ActionsCriteria; import org.hawkular.alerts.api.services.ActionsService; import org.hawkular.alerts.api.services.DefinitionsService; 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 Actions * * @author Lucas Ponce */ @Path("/actions") @Api(value = "/actions", description = "Actions Handling") public class ActionsHandler { private final Logger log = Logger.getLogger(ActionsHandler.class); private static final Map<String, Set<String>> queryParamValidationMap = new HashMap<>(); static { ResponseUtil.populateQueryParamsMap(ActionsHandler.class, queryParamValidationMap); } @HeaderParam(TENANT_HEADER_NAME) String tenantId; @EJB DefinitionsService definitions; @EJB ActionsService actions; public ActionsHandler() { log.debug("Creating instance."); } @GET @Path("/") @Produces(APPLICATION_JSON) @ApiOperation(value = "Find all action ids grouped by plugin.", notes = "Return a map[string, array of string]] where key is the plugin id and value " + "a collection of actionIds.", response = Collection.class, responseContainer = "Map") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched map of action ids grouped by plugin."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response findActionIds() { try { Map<String, Set<String>> actions = definitions.getActionDefinitionIds(tenantId); log.debugf("Actions: %s", actions); return ResponseUtil.ok(actions); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/plugin/{actionPlugin}") @Produces(APPLICATION_JSON) @ApiOperation(value = "Find all action ids of an specific action plugin.", response = String.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched list of action ids."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response findActionIdsByPlugin(@ApiParam(value = "Action plugin to filter query for action ids.", required = true) @PathParam("actionPlugin") final String actionPlugin) { try { Collection<String> actions = definitions.getActionDefinitionIds(tenantId, actionPlugin); log.debugf("Actions: %s", actions); return ResponseUtil.ok(actions); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/{actionPlugin}/{actionId}") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get an existing action definition.", response = ActionDefinition.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Action found."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No Action found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response getActionDefinition(@ApiParam(value = "Action plugin.", required = true) @PathParam("actionPlugin") final String actionPlugin, @ApiParam(value = "Action id to be retrieved.", required = true) @PathParam("actionId") final String actionId) { try { ActionDefinition actionDefinition = definitions.getActionDefinition(tenantId, actionPlugin, actionId); log.debugf("ActionDefinition: %s", actionDefinition); if (actionDefinition == null) { return ResponseUtil.notFound("Not action found for actionPlugin: " + actionPlugin + " and actionId: " + actionId); } return ResponseUtil.ok(actionDefinition); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Create a new ActionDefinition.", notes = "Returns created ActionDefinition", response = ActionDefinition.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, ActionDefinition Created."), @ApiResponse(code = 400, message = "Existing ActionDefinition/Invalid Parameters", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response createActionDefinition( @ApiParam(value = "ActionDefinition to be created.", name = "actionDefinition", required = true) final ActionDefinition actionDefinition) { if (actionDefinition == null) { return ResponseUtil.badRequest("actionDefinition must be not null"); } if (isEmpty(actionDefinition.getActionPlugin())) { return ResponseUtil.badRequest("actionPlugin must be not null"); } if (isEmpty(actionDefinition.getActionId())) { return ResponseUtil.badRequest("actionId must be not null"); } if (isEmpty(actionDefinition.getProperties())) { return ResponseUtil.badRequest("properties must be not null"); } try { if (definitions.getActionDefinition(tenantId, actionDefinition.getActionPlugin(), actionDefinition.getActionId()) != null) { return ResponseUtil.badRequest("Existing ActionDefinition: " + actionDefinition); } definitions.addActionDefinition(tenantId, actionDefinition); log.debugf("ActionDefinition: %s", actionDefinition); return ResponseUtil.ok(actionDefinition); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Update an existing ActionDefinition.", notes = "Returns updated ActionDefinition.", response = ActionDefinition.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, ActionDefinition Updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "ActionDefinition not found for update.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response updateActionDefinition( @ApiParam(value = "ActionDefinition to be updated.", name = "actionDefinition", required = true) final ActionDefinition actionDefinition) { if (actionDefinition == null) { return ResponseUtil.badRequest("actionDefinition must be not null"); } if (isEmpty(actionDefinition.getActionPlugin())) { return ResponseUtil.badRequest("actionPlugin must be not null"); } if (isEmpty(actionDefinition.getActionId())) { return ResponseUtil.badRequest("actionId must be not null"); } try { actionDefinition.setTenantId(tenantId); if (definitions.getActionDefinition(tenantId, actionDefinition.getActionPlugin(), actionDefinition.getActionId()) != null) { definitions.updateActionDefinition(tenantId, actionDefinition); log.debugf("ActionDefinition: %s", actionDefinition); return ResponseUtil.ok(actionDefinition); } return ResponseUtil.notFound("ActionDefinition: " + actionDefinition + " not found for update"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @DELETE @Path("/{actionPlugin}/{actionId}") @ApiOperation(value = "Delete an existing ActionDefinition.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, ActionDefinition Deleted."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "ActionDefinition not found for delete.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response deleteActionDefinition( @ApiParam(value = "Action plugin.", required = true) @PathParam("actionPlugin") final String actionPlugin, @ApiParam(value = "Action id to be deleted.", required = true) @PathParam("actionId") final String actionId) { try { if (definitions.getActionDefinition(tenantId, actionPlugin, actionId) != null) { definitions.removeActionDefinition(tenantId, actionPlugin, actionId); log.debugf("ActionPlugin: %s, ActionId: %s", actionPlugin, actionId); return ResponseUtil.ok(); } return ResponseUtil .notFound("ActionPlugin: " + actionPlugin + " ActionId: " + actionId + " not found for delete"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/history") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get actions from history with optional filtering.", notes = "If not criteria defined, it fetches all actions stored in the system.", response = Action.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched list of actions."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) @QueryParamValidation(name = "findActionsHistory") public Response findActionsHistory( @ApiParam(required = false, value = "Filter out actions created before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startTime") final Long startTime, @ApiParam(required = false, value = "Filter out actions created after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endTime") final Long endTime, @ApiParam(required = false, value = "Filter out actions for unspecified actionPlugin.", allowableValues = "Comma separated list of plugin names.") @QueryParam("actionPlugins") final String actionPlugins, @ApiParam(required = false, value = "Filter out actions for unspecified actionId.", allowableValues = "Comma separated list of actions IDs.") @QueryParam("actionIds") final String actionIds, @ApiParam(required = false, value = "Filter out actions for unspecified alertIds.", allowableValues = "Comma separated list of alert IDs") @QueryParam("alertIds") final String alertIds, @ApiParam(required = false, value = "Filter out alerts for unspecified result.", allowableValues = "Comma separated list of action results.") @QueryParam("results") final String results, @ApiParam(required = false, value = "Return only thin actions, do not include full alert, only alertId.") @QueryParam("thin") final Boolean thin, @Context final UriInfo uri) { try { ResponseUtil.checkForUnknownQueryParams(uri, queryParamValidationMap.get("findActionsHistory")); Pager pager = RequestUtil.extractPaging(uri); ActionsCriteria criteria = buildCriteria(startTime, endTime, actionPlugins, actionIds, alertIds, results, thin); Page<Action> actionPage = actions.getActions(tenantId, criteria, pager); log.debugf("Actions: %s", actionPage); if (isEmpty(actionPage)) { return ResponseUtil.ok(actionPage); } return ResponseUtil.paginatedOk(actionPage, uri); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/history/delete") @Produces(APPLICATION_JSON) @ApiOperation(value = "Delete actions from history with optional filtering.", notes = "WARNING: If not criteria defined, it deletes all actions history stored in the system.", response = ApiDeleted.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Actions deleted."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) @QueryParamValidation(name = "deleteActionsHistory") public Response deleteActionsHistory( @ApiParam(required = false, value = "Filter out actions created before this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("startTime") final Long startTime, @ApiParam(required = false, value = "Filter out action created after this time.", allowableValues = "Timestamp in millisecond since epoch.") @QueryParam("endTime") final Long endTime, @ApiParam(required = false, value = "Filter out actions for unspecified actionPlugin.", allowableValues = "Comma separated list of plugin names.") @QueryParam("actionPlugins") final String actionPlugins, @ApiParam(required = false, value = "Filter out actions for unspecified actionId.", allowableValues = "Comma separated list of action IDs.") @QueryParam("actionIds") final String actionIds, @ApiParam(required = false, value = "Filter out actions for unspecified alertIds. ", allowableValues = "Comma separated list of alert IDs.") @QueryParam("alertIds") final String alertIds, @ApiParam(required = false, value = "Filter out alerts for unspecified result. ", allowableValues = "Comma separated list of action results.") @QueryParam("results") final String results, @Context final UriInfo uri) { try { ResponseUtil.checkForUnknownQueryParams(uri, queryParamValidationMap.get("deleteActionsHistory")); ActionsCriteria criteria = buildCriteria(startTime, endTime, actionPlugins, actionIds, alertIds, results, false); int numDeleted = actions.deleteActions(tenantId, criteria); log.debugf("Actions deleted: %d", numDeleted); return ResponseUtil.ok(new ApiDeleted(numDeleted)); } catch (Exception e) { return ResponseUtil.onException(e, log); } } private ActionsCriteria buildCriteria(Long startTime, Long endTime, String actionPlugins, String actionIds, String alertIds, String results, Boolean thin) { ActionsCriteria criteria = new ActionsCriteria(); criteria.setStartTime(startTime); criteria.setEndTime(endTime); if (!isEmpty(actionPlugins)) { criteria.setActionPlugins(Arrays.asList(actionPlugins.split(","))); } if (!isEmpty(actionIds)) { criteria.setActionIds(Arrays.asList(actionIds.split(","))); } if (!isEmpty(alertIds)) { criteria.setAlertIds(Arrays.asList(alertIds.split(","))); } if (!isEmpty(results)) { criteria.setResults(Arrays.asList(results.split(","))); } if (thin != null) { criteria.setThin(thin); } else { criteria.setThin(false); } return criteria; } }