/* * 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.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; 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.json.GroupConditionsInfo; import org.hawkular.alerts.api.json.GroupMemberInfo; import org.hawkular.alerts.api.json.UnorphanMemberInfo; import org.hawkular.alerts.api.model.condition.Condition; import org.hawkular.alerts.api.model.dampening.Dampening; import org.hawkular.alerts.api.model.paging.Page; import org.hawkular.alerts.api.model.paging.Pager; import org.hawkular.alerts.api.model.trigger.FullTrigger; import org.hawkular.alerts.api.model.trigger.Mode; import org.hawkular.alerts.api.model.trigger.Trigger; import org.hawkular.alerts.api.services.DefinitionsService; import org.hawkular.alerts.api.services.TriggersCriteria; 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 triggers * * @author Jay Shaughnessy * @author Lucas Ponce */ @Path("/triggers") @Api(value = "/triggers", description = "Triggers Definitions Handling") public class TriggersHandler { private static final Logger log = Logger.getLogger(TriggersHandler.class); private static final Map<String, Set<String>> queryParamValidationMap = new HashMap<>(); static { ResponseUtil.populateQueryParamsMap(TriggersHandler.class, queryParamValidationMap); } @HeaderParam(TENANT_HEADER_NAME) String tenantId; @EJB DefinitionsService definitions; public TriggersHandler() { log.debug("Creating instance."); } @GET @Path("/") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get triggers with optional filtering.", notes = "If not criteria defined, it fetches all triggers stored in the system.", response = Trigger.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched list of triggers."), @ApiResponse(code = 400, message = "Bad request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) @QueryParamValidation(name = "findTriggers") public Response findTriggers( @ApiParam(required = false, value = "Filter out triggers for unspecified triggerIds. ", allowableValues = "Comma separated list of trigger IDs.") @QueryParam("triggerIds") final String triggerIds, @ApiParam(required = false, value = "Filter out triggers 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 = "Return only thin triggers. Currently Ignored.") @QueryParam("thin") final Boolean thin, @Context final UriInfo uri) { try { ResponseUtil.checkForUnknownQueryParams(uri, queryParamValidationMap.get("findTriggers")); Pager pager = RequestUtil.extractPaging(uri); TriggersCriteria criteria = buildCriteria(triggerIds, tags, thin); Page<Trigger> triggerPage = definitions.getTriggers(tenantId, criteria, pager); log.debugf("Triggers: %s", triggerPage); if (isEmpty(triggerPage)) { return ResponseUtil.ok(triggerPage); } return ResponseUtil.paginatedOk(triggerPage, uri); } catch (Exception e) { return ResponseUtil.onException(e, log); } } private TriggersCriteria buildCriteria(String triggerIds, String tags, Boolean thin) { TriggersCriteria criteria = new TriggersCriteria(); if (!isEmpty(triggerIds)) { criteria.setTriggerIds(Arrays.asList(triggerIds.split(","))); } if (!isEmpty(tags)) { String[] tagTokens = tags.split(","); Map<String, String> tagsMap = new HashMap<>(tagTokens.length); for (String tagToken : tagTokens) { String[] fields = tagToken.split("\\|"); if (fields.length == 2) { tagsMap.put(fields[0], fields[1]); } else { if (log.isDebugEnabled()) { log.debugf("Invalid Tag Criteria: %s", Arrays.toString(fields)); } throw new IllegalArgumentException( "Invalid Tag Criteria " + Arrays.toString(fields) ); } } criteria.setTags(tagsMap); } if (null != thin) { criteria.setThin(thin.booleanValue()); } return criteria; } @GET @Path("/groups/{groupId}/members") @Produces(APPLICATION_JSON) @ApiOperation(value = "Find all group member trigger definitions.", notes = "Pagination is not yet implemented.", response = Trigger.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched list of triggers."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response findGroupMembers( @ApiParam(value = "Group TriggerId.", required = true) @PathParam("groupId") final String groupId, @ApiParam(value = "include Orphan members? No if omitted.", required = false) @QueryParam("includeOrphans") final boolean includeOrphans) { try { Collection<Trigger> members = definitions.getMemberTriggers(tenantId, groupId, includeOrphans); log.debugf("Member Triggers: %s", members); return ResponseUtil.ok(members); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Create a new trigger.", notes = "Return created trigger.", response = Trigger.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Trigger created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response createTrigger( @ApiParam(value = "Trigger definition to be created.", name = "trigger", required = true) final Trigger trigger) { try { if (null != trigger) { if (isEmpty(trigger.getId())) { trigger.setId(Trigger.generateId()); } else if (definitions.getTrigger(tenantId, trigger.getId()) != null) { return ResponseUtil.badRequest("Trigger with ID [" + trigger.getId() + "] exists."); } if (!checkTags(trigger)) { return ResponseUtil.badRequest("Tags " + trigger.getTags() + " must be non empty."); } definitions.addTrigger(tenantId, trigger); log.debugf("Trigger: %s", trigger); return ResponseUtil.ok(trigger); } return ResponseUtil.badRequest("Trigger is null"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/trigger") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Create a new full trigger (trigger, dampenings and conditions).", notes = "Return created full trigger.", response = FullTrigger.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, FullTrigger created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response createFullTrigger( @ApiParam(value = "FullTrigger (trigger, dampenings, conditions) to be created.", name = "fullTrigger", required = true) final FullTrigger fullTrigger) { if (fullTrigger == null || fullTrigger.getTrigger() == null) { return ResponseUtil.badRequest("Trigger is empty "); } try { Trigger trigger = fullTrigger.getTrigger(); trigger.setTenantId(tenantId); if (isEmpty(trigger.getId())) { trigger.setId(Trigger.generateId()); } else if (definitions.getTrigger(tenantId, trigger.getId()) != null) { return ResponseUtil.badRequest("Trigger with ID [" + trigger.getId() + "] exists."); } if (!checkTags(trigger)) { return ResponseUtil.badRequest("Tags " + trigger.getTags() + " must be non empty."); } definitions.addTrigger(tenantId, trigger); log.debugf("Trigger: %s", trigger); for (Dampening dampening : fullTrigger.getDampenings()) { dampening.setTenantId(tenantId); dampening.setTriggerId(trigger.getId()); boolean exist = (definitions.getDampening(tenantId, dampening.getDampeningId()) != null); if (exist) { definitions.removeDampening(tenantId, dampening.getDampeningId()); } definitions.addDampening(tenantId, dampening); log.debugf("Dampening: %s", dampening); } fullTrigger.getConditions().stream().forEach(c -> { c.setTenantId(tenantId); c.setTriggerId(trigger.getId()); }); List<Condition> firingConditions = fullTrigger.getConditions().stream() .filter(c -> c.getTriggerMode() == Mode.FIRING) .collect(Collectors.toList()); if (!isEmpty(firingConditions)) { definitions.setConditions(tenantId, trigger.getId(), Mode.FIRING, firingConditions); log.debugf("Conditions: %s", firingConditions); } List<Condition> autoResolveConditions = fullTrigger.getConditions().stream() .filter(c -> c.getTriggerMode() == Mode.AUTORESOLVE) .collect(Collectors.toList()); if (!isEmpty(autoResolveConditions)) { definitions.setConditions(tenantId, trigger.getId(), Mode.AUTORESOLVE, autoResolveConditions); log.debugf("Conditions: %s", autoResolveConditions); } return ResponseUtil.ok(fullTrigger); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/groups") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Create a new group trigger.", notes = "Returns created group trigger.", response = Trigger.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Group Trigger Created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response createGroupTrigger( @ApiParam(value = "Trigger definition to be created.", name = "groupTrigger", required = true) final Trigger groupTrigger) { try { if (null != groupTrigger) { if (isEmpty(groupTrigger.getId())) { groupTrigger.setId(Trigger.generateId()); } else if (definitions.getTrigger(tenantId, groupTrigger.getId()) != null) { return ResponseUtil.badRequest("Trigger with ID [" + groupTrigger.getId() + "] exists."); } if (!checkTags(groupTrigger)) { return ResponseUtil.badRequest("Tags " + groupTrigger.getTags() + " must be non empty."); } definitions.addGroupTrigger(tenantId, groupTrigger); log.debugf("Group Trigger: %s", groupTrigger); return ResponseUtil.ok(groupTrigger); } return ResponseUtil.badRequest("Trigger is null"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/groups/members") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Create a new member trigger for a parent trigger.", notes = "Returns Member Trigger created if operation finished correctly.", response = Trigger.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Member Trigger Created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Group trigger not found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response createGroupMember( @ApiParam(value = "Group member trigger to be created.", name = "groupMember", required = true) final GroupMemberInfo groupMember) { try { if (null == groupMember) { return ResponseUtil.badRequest("MemberTrigger is null"); } String groupId = groupMember.getGroupId(); if (isEmpty(groupId)) { return ResponseUtil.badRequest("MemberTrigger groupId is null"); } if (!checkTags(groupMember)) { return ResponseUtil.badRequest("Tags " + groupMember.getMemberTags() + " must be non empty."); } Trigger child = definitions.addMemberTrigger(tenantId, groupId, groupMember.getMemberId(), groupMember.getMemberName(), groupMember.getMemberDescription(), groupMember.getMemberContext(), groupMember.getMemberTags(), groupMember.getDataIdMap()); log.debugf("Child Trigger: %s", child); return ResponseUtil.ok(child); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/{triggerId}") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get an existing trigger definition.", response = Trigger.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Trigger found."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Trigger not found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response getTrigger( @ApiParam(value = "Trigger definition id to be retrieved.", required = true) @PathParam("triggerId") final String triggerId) { try { Trigger found = definitions.getTrigger(tenantId, triggerId); if (found != null) { log.debugf("Trigger: %s", found); return ResponseUtil.ok(found); } return ResponseUtil.notFound("triggerId: " + triggerId + " not found"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/trigger/{triggerId}") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get an existing full trigger definition (trigger, dampenings and conditions).", response = FullTrigger.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, FullTrigger found."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Trigger not found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response getFullTrigger( @ApiParam(value = "Full Trigger definition id to be retrieved.", required = true) @PathParam("triggerId") final String triggerId) { try { Trigger found = definitions.getTrigger(tenantId, triggerId); if (found != null) { log.debugf("Trigger: %s", found); List<Dampening> dampenings = new ArrayList<>(definitions.getTriggerDampenings(tenantId, found.getId(), null)); List<Condition> conditions = new ArrayList<>(definitions.getTriggerConditions(tenantId, found.getId(), null)); FullTrigger fullTrigger = new FullTrigger(found, dampenings, conditions); return ResponseUtil.ok(fullTrigger); } return ResponseUtil.notFound("triggerId: " + triggerId + " not found"); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/{triggerId}") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Update an existing trigger definition.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Trigger updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Trigger doesn't exist.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response updateTrigger( @ApiParam(value = "Trigger definition id to be updated.", required = true) @PathParam("triggerId") final String triggerId, @ApiParam(value = "Updated trigger definition.", name = "trigger", required = true) final Trigger trigger) { try { if (trigger != null && !isEmpty(triggerId)) { trigger.setId(triggerId); } if (!checkTags(trigger)) { return ResponseUtil.badRequest("Tags " + trigger.getTags() + " must be non empty."); } definitions.updateTrigger(tenantId, trigger); log.debugf("Trigger: %s", trigger); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/groups/{groupId}") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Update an existing group trigger definition and its member definitions.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Group Trigger updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Trigger doesn't exist.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response updateGroupTrigger( @ApiParam(value = "Group Trigger id to be updated.", required = true) @PathParam("groupId") final String groupId, @ApiParam(value = "Updated group trigger definition.", name = "groupTrigger", required = true) final Trigger groupTrigger) { try { if (groupTrigger != null && !isEmpty(groupId)) { groupTrigger.setId(groupId); } if (!checkTags(groupTrigger)) { return ResponseUtil.badRequest("Tags " + groupTrigger.getTags() + " must be non empty."); } definitions.updateGroupTrigger(tenantId, groupTrigger); log.debugf("Trigger: %s", groupTrigger); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/groups/members/{memberId}/orphan") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Make a non-orphan member trigger into an orphan.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Trigger updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Trigger doesn't exist/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response orphanMemberTrigger( @ApiParam(value = "Member Trigger id to be made an orphan.", required = true) @PathParam("memberId") final String memberId) { try { Trigger child = definitions.orphanMemberTrigger(tenantId, memberId); log.debugf("Orphan Member Trigger: %s", child); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/groups/members/{memberId}/unorphan") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Make a non-orphan member trigger into an orphan.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Trigger updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Trigger doesn't exist.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response unorphanMemberTrigger( @ApiParam(value = "Member Trigger id to be made an orphan.", required = true) @PathParam("memberId") final String memberId, @ApiParam(required = true, name = "memberTrigger", value = "Only context and dataIdMap are used when changing back to a non-orphan.") final UnorphanMemberInfo unorphanMemberInfo) { try { if (null == unorphanMemberInfo) { return ResponseUtil.badRequest("MemberTrigger is null"); } if (!checkTags(unorphanMemberInfo)) { return ResponseUtil.badRequest("Tags " + unorphanMemberInfo.getMemberTags() + " must be non empty."); } Trigger child = definitions.unorphanMemberTrigger(tenantId, memberId, unorphanMemberInfo.getMemberContext(), unorphanMemberInfo.getMemberTags(), unorphanMemberInfo.getDataIdMap()); log.debugf("Member Trigger: %s", child); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @DELETE @Path("/{triggerId}") @ApiOperation(value = "Delete an existing standard or group member trigger definition.", notes = "This can not be used to delete a group trigger definition.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Trigger deleted."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Trigger not found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response deleteTrigger( @ApiParam(value = "Trigger definition id to be deleted.", required = true) @PathParam("triggerId") final String triggerId) { try { definitions.removeTrigger(tenantId, triggerId); log.debugf("TriggerId: %s", triggerId); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @DELETE @Path("/groups/{groupId}") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Delete a group trigger.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Group Trigger Removed."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Group Trigger not found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response deleteGroupTrigger( @ApiParam(required = true, value = "Group Trigger id.") @PathParam("groupId") final String groupId, @ApiParam(required = true, value = "Convert the non-orphan member triggers to standard triggers.") @QueryParam("keepNonOrphans") final boolean keepNonOrphans, @ApiParam(required = true, value = "Convert the orphan member triggers to standard triggers.") @QueryParam("keepOrphans") final boolean keepOrphans) { try { definitions.removeGroupTrigger(tenantId, groupId, keepNonOrphans, keepOrphans); log.debugf("Remove Group Trigger: %s/%s", tenantId, groupId); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/{triggerId}/dampenings") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get all Dampenings for a Trigger (1 Dampening per mode).", response = Dampening.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched list of dampenings."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response getTriggerDampenings( @ApiParam(value = "Trigger definition id to be retrieved.", required = true) @PathParam("triggerId") final String triggerId) { try { Collection<Dampening> dampenings = definitions.getTriggerDampenings(tenantId, triggerId, null); log.debugf("Dampenings: %s", dampenings); return ResponseUtil.ok(dampenings); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/{triggerId}/dampenings/mode/{triggerMode}") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get dampening using triggerId and triggerMode.", response = Dampening.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched list of dampenings."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response getTriggerModeDampenings( @ApiParam(value = "Trigger definition id to be retrieved.", required = true) @PathParam("triggerId") final String triggerId, @ApiParam(value = "Trigger mode", required = true) @PathParam("triggerMode") final Mode triggerMode) { try { Collection<Dampening> dampenings = definitions.getTriggerDampenings(tenantId, triggerId, triggerMode); log.debugf("Dampenings: %s", dampenings); return ResponseUtil.ok(dampenings); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/{triggerId}/dampenings/{dampeningId}") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get an existing dampening.", response = Dampening.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Dampening Found."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No Dampening Found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response getDampening( @ApiParam(value = "Trigger definition id to be retrieved.", required = true) @PathParam("triggerId") final String triggerId, @ApiParam(value = "Dampening id", required = true) @PathParam("dampeningId") final String dampeningId) { try { Dampening found = definitions.getDampening(tenantId, dampeningId); log.debugf("Dampening: %s", found); if (found == null) { return ResponseUtil.notFound("No dampening found for triggerId: " + triggerId + " and dampeningId:" + dampeningId); } return ResponseUtil.ok(found); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/{triggerId}/dampenings") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Create a new dampening.", notes = "Return Dampening created.", response = Dampening.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Dampening created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response createDampening( @ApiParam(value = "Trigger definition id attached to dampening.", required = true) @PathParam("triggerId") final String triggerId, @ApiParam(value = "Dampening definition to be created.", required = true) final Dampening dampening) { try { dampening.setTenantId(tenantId); dampening.setTriggerId(triggerId); boolean exists = (definitions.getDampening(tenantId, dampening.getDampeningId()) != null); if (!exists) { // make sure we have the best chance of clean data.. Dampening d = getCleanDampening(dampening); definitions.addDampening(tenantId, d); log.debugf("Dampening: %s", d); return ResponseUtil.ok(d); } return ResponseUtil.badRequest("Existing dampening for dampeningId: " + dampening.getDampeningId()); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @POST @Path("/groups/{groupId}/dampenings") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Create a new group dampening.", notes = " Return group Dampening created.", response = Dampening.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Dampening created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response createGroupDampening( @ApiParam(value = "Group Trigger definition id attached to dampening.", required = true) @PathParam("groupId") final String groupId, @ApiParam(value = "Dampening definition to be created.", required = true) final Dampening dampening) { try { dampening.setTriggerId(groupId); boolean exists = (definitions.getDampening(tenantId, dampening.getDampeningId()) != null); if (!exists) { // make sure we have the best chance of clean data.. Dampening d = getCleanDampening(dampening); definitions.addGroupDampening(tenantId, d); log.debugf("Dampening: %s", d); return ResponseUtil.ok(d); } return ResponseUtil.badRequest("Existing dampening for dampeningId: " + dampening.getDampeningId()); } catch (Exception e) { return ResponseUtil.onException(e, log); } } private Dampening getCleanDampening(Dampening dampening) throws Exception { switch (dampening.getType()) { case STRICT: return Dampening.forStrict(dampening.getTenantId(), dampening.getTriggerId(), dampening.getTriggerMode(), dampening.getEvalTrueSetting()); case STRICT_TIME: return Dampening.forStrictTime(dampening.getTenantId(), dampening.getTriggerId(), dampening.getTriggerMode(), dampening.getEvalTimeSetting()); case STRICT_TIMEOUT: return Dampening.forStrictTimeout(dampening.getTenantId(), dampening.getTriggerId(), dampening.getTriggerMode(), dampening.getEvalTimeSetting()); case RELAXED_COUNT: return Dampening.forRelaxedCount(dampening.getTenantId(), dampening.getTriggerId(), dampening.getTriggerMode(), dampening.getEvalTrueSetting(), dampening.getEvalTotalSetting()); case RELAXED_TIME: return Dampening.forRelaxedTime(dampening.getTenantId(), dampening.getTriggerId(), dampening.getTriggerMode(), dampening.getEvalTrueSetting(), dampening.getEvalTimeSetting()); default: throw new Exception("Unhandled Dampening Type: " + dampening.toString()); } } @PUT @Path("/{triggerId}/dampenings/{dampeningId}") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Update an existing dampening definition.", notes = "Note that the trigger mode can not be changed. + \n" + "Return Dampening updated.", response = Dampening.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Dampening Updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No Dampening Found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error", response = ApiError.class) }) public Response updateDampening( @ApiParam(value = "Trigger definition id to be retrieved.", required = true) @PathParam("triggerId") final String triggerId, @ApiParam(value = "Dampening id.", required = true) @PathParam("dampeningId") final String dampeningId, @ApiParam(value = "Updated dampening definition", required = true) final Dampening dampening) { try { boolean exists = (definitions.getDampening(tenantId, dampeningId) != null); if (exists) { // make sure we have the best chance of clean data.. dampening.setTriggerId(triggerId); Dampening d = getCleanDampening(dampening); definitions.updateDampening(tenantId, d); log.debugf("Dampening: %s", d); return ResponseUtil.ok(d); } return ResponseUtil.notFound("No dampening found for dampeningId: " + dampeningId); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/groups/{groupId}/dampenings/{dampeningId}") @Consumes(APPLICATION_JSON) @ApiOperation(value = "Update an existing group dampening definition.", notes = "Note that the trigger mode can not be changed. + \n" + "Return Dampening updated.", response = Dampening.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Dampening Updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No Dampening Found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error", response = ApiError.class) }) public Response updateGroupDampening( @ApiParam(value = "Trigger definition id to be retrieved.", required = true) @PathParam("groupId") final String groupId, @ApiParam(value = "Dampening id.", required = true) @PathParam("dampeningId") final String dampeningId, @ApiParam(value = "Updated dampening definition.", required = true) final Dampening dampening) { try { boolean exists = (definitions.getDampening(tenantId, dampeningId) != null); if (exists) { // make sure we have the best chance of clean data.. dampening.setTriggerId(groupId); Dampening d = getCleanDampening(dampening); definitions.updateGroupDampening(tenantId, d); log.debugf("Group Dampening: %s", d); return ResponseUtil.ok(d); } return ResponseUtil.notFound("No dampening found for dampeningId: " + dampeningId); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @DELETE @Path("/{triggerId}/dampenings/{dampeningId}") @ApiOperation(value = "Delete an existing dampening definition.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Dampening deleted."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No Dampening found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error", response = ApiError.class) }) public Response deleteDampening( @ApiParam(value = "Trigger definition id to be deleted.", required = true) @PathParam("triggerId") final String triggerId, @ApiParam(value = "Dampening id for dampening definition to be deleted.", required = true) @PathParam("dampeningId") final String dampeningId) { try { boolean exists = (definitions.getDampening(tenantId, dampeningId) != null); if (exists) { definitions.removeDampening(tenantId, dampeningId); log.debugf("DampeningId: %s", dampeningId); return ResponseUtil.ok(); } return ResponseUtil.notFound("Dampening not found for dampeningId: " + dampeningId); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @DELETE @Path("/groups/{groupId}/dampenings/{dampeningId}") @ApiOperation(value = "Delete an existing group dampening definition.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Dampening deleted."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No Dampening found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response deleteGroupDampening( @ApiParam(value = "Trigger definition id to be retrieved.", required = true) @PathParam("groupId") final String groupId, @ApiParam(value = "Dampening id for dampening definition to be deleted.", required = true) @PathParam("dampeningId") final String dampeningId) { try { boolean exists = (definitions.getDampening(tenantId, dampeningId) != null); if (exists) { definitions.removeGroupDampening(tenantId, dampeningId); log.debugf("Group DampeningId: %s", dampeningId); return ResponseUtil.ok(); } return ResponseUtil.notFound("Dampening not found for dampeningId: " + dampeningId); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @GET @Path("/{triggerId}/conditions") @Produces(APPLICATION_JSON) @ApiOperation(value = "Get all conditions for a specific trigger.", response = Condition.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully fetched list of conditions."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response getTriggerConditions( @ApiParam(value = "Trigger definition id to be retrieved.", required = true) @PathParam("triggerId") final String triggerId) { try { Collection<Condition> conditions = definitions.getTriggerConditions(tenantId, triggerId, null); log.debugf("Conditions: %s", conditions); return ResponseUtil.ok(conditions); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/{triggerId}/conditions") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Set the conditions for the trigger. ", notes = "This sets the conditions for all trigger modes, " + "replacing existing conditions for all trigger modes. Returns the new conditions.", response = Condition.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Condition Set created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No trigger found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response setConditions( @ApiParam(value = "The relevant Trigger.", required = true) @PathParam("triggerId") final String triggerId, @ApiParam(value = "Collection of Conditions to set.", required = true) final Collection<Condition> conditions) { try { if (conditions == null) { return ResponseUtil.badRequest("Conditions must be non null"); } Collection<Condition> updatedConditions = new HashSet<>(); conditions.stream().forEach(c -> c.setTriggerId(triggerId)); Collection<Condition> firingConditions = conditions.stream() .filter(c -> c.getTriggerMode() == null || c.getTriggerMode().equals(Mode.FIRING)) .collect(Collectors.toList()); updatedConditions.addAll(definitions.setConditions(tenantId, triggerId, Mode.FIRING, firingConditions)); Collection<Condition> autoResolveConditions = conditions.stream() .filter(c -> c.getTriggerMode().equals(Mode.AUTORESOLVE)) .collect(Collectors.toList()); updatedConditions.addAll(definitions.setConditions(tenantId, triggerId, Mode.AUTORESOLVE, autoResolveConditions)); log.debugf("Conditions: %s", updatedConditions); return ResponseUtil.ok(updatedConditions); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/{triggerId}/conditions/{triggerMode}") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Set the conditions for the trigger. ", notes = "This replaces any existing conditions. Returns the new conditions.", response = Condition.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Condition Set created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No trigger found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response setConditions( @ApiParam(value = "The relevant Trigger.", required = true) @PathParam("triggerId") final String triggerId, @ApiParam(value = "The trigger mode.", required = true, allowableValues = "FIRING or AUTORESOLVE (not case sensitive)") @PathParam("triggerMode") final String triggerMode, @ApiParam(value = "Collection of Conditions to set.", required = true) final Collection<Condition> conditions) { try { Mode mode = Mode.valueOf(triggerMode.toUpperCase()); if (!isEmpty(conditions)) { for (Condition condition : conditions) { condition.setTriggerId(triggerId); if (condition.getTriggerMode() == null || !condition.getTriggerMode().equals(mode)) { return ResponseUtil.badRequest("Condition: " + condition + " has a different triggerMode [" + triggerMode + "]"); } } } Collection<Condition> updatedConditions = definitions.setConditions(tenantId, triggerId, mode, conditions); log.debugf("Conditions: %s", updatedConditions); return ResponseUtil.ok(updatedConditions); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/groups/{groupId}/conditions") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Set the conditions for the group trigger.", notes = "This replaces any existing conditions on the group and member conditions " + "for all trigger modes. + \n" + "Return the new group conditions.", response = Condition.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Group Condition Set created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No trigger found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error", response = ApiError.class) }) public Response setGroupConditions( @ApiParam(value = "The relevant Group Trigger.", required = true) @PathParam("groupId") final String groupId, @ApiParam(value = "Collection of Conditions to set and Map with tokens per dataId on members.") final GroupConditionsInfo groupConditionsInfo) { try { Collection<Condition> updatedConditions = new HashSet<>(); if (groupConditionsInfo == null) { return ResponseUtil.badRequest("GroupConditionsInfo must be non null."); } if (groupConditionsInfo.getConditions() == null) { groupConditionsInfo.setConditions(Collections.emptyList()); } for (Condition condition : groupConditionsInfo.getConditions()) { if (condition == null) { return ResponseUtil.badRequest("GroupConditionsInfo must have non null conditions: " + groupConditionsInfo); } condition.setTriggerId(groupId); } Collection<Condition> firingConditions = groupConditionsInfo.getConditions().stream() .filter(c -> c.getTriggerMode() == null || c.getTriggerMode().equals(Mode.FIRING)) .collect(Collectors.toList()); updatedConditions.addAll(definitions.setGroupConditions(tenantId, groupId, Mode.FIRING, firingConditions, groupConditionsInfo.getDataIdMemberMap())); Collection<Condition> autoResolveConditions = groupConditionsInfo.getConditions().stream() .filter(c -> c.getTriggerMode().equals(Mode.AUTORESOLVE)) .collect(Collectors.toList()); updatedConditions.addAll(definitions.setGroupConditions(tenantId, groupId, Mode.AUTORESOLVE, autoResolveConditions, groupConditionsInfo.getDataIdMemberMap())); log.debugf("Conditions: %s", updatedConditions); return ResponseUtil.ok(updatedConditions); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/groups/{groupId}/conditions/{triggerMode}") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Set the conditions for the group trigger.", notes = "This replaces any existing conditions on the group and member conditions. + \n" + "Return the new group conditions.", response = Condition.class, responseContainer = "List") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Group Condition Set created."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "No trigger found.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error", response = ApiError.class) }) public Response setGroupConditions( @ApiParam(value = "The relevant Group Trigger.", required = true) @PathParam("groupId") final String groupId, @ApiParam(value = "The trigger mode.", required = true, allowableValues = "FIRING or AUTORESOLVE (not case sensitive)") @PathParam("triggerMode") final String triggerMode, @ApiParam(value = "Collection of Conditions to set and Map with tokens per dataId on members.") final GroupConditionsInfo groupConditionsInfo) { try { Mode mode = Mode.valueOf(triggerMode.toUpperCase()); for (Condition condition : groupConditionsInfo.getConditions()) { if (condition == null) { return ResponseUtil.badRequest("GroupConditionsInfo must have non null conditions: " + groupConditionsInfo); } condition.setTriggerId(groupId); if (condition.getTriggerMode() == null || !condition.getTriggerMode().equals(mode)) { return ResponseUtil.badRequest("Condition: " + condition + " has a different triggerMode [" + triggerMode + "]"); } } Collection<Condition> conditions = definitions.setGroupConditions(tenantId, groupId, mode, groupConditionsInfo.getConditions(), groupConditionsInfo.getDataIdMemberMap()); log.debugf("Conditions: %s", conditions); return ResponseUtil.ok(conditions); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/groups/enabled") @ApiOperation(value = "Update group triggers and their member triggers to be enabled or disabled.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Group Triggers updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Group Trigger doesn't exist.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response setGroupTriggersEnabled( @ApiParam(required = true, value = "List of group trigger ids to enable or disable", allowableValues = "Comma separated list of group triggerIds to be enabled or disabled.") @QueryParam("triggerIds") final String triggerIds, @ApiParam(required = true, value = "Set enabled or disabled.") @QueryParam("enabled") final Boolean enabled) { try { if (isEmpty(triggerIds)) { return ResponseUtil.badRequest("GroupTriggerIds must be non empty."); } if (null == enabled) { return ResponseUtil.badRequest("Enabled must be non-empty."); } definitions.updateGroupTriggerEnablement(tenantId, triggerIds, enabled); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } @PUT @Path("/enabled") @ApiOperation(value = "Update triggers to be enabled or disabled.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Success, Triggers updated."), @ApiResponse(code = 400, message = "Bad Request/Invalid Parameters.", response = ApiError.class), @ApiResponse(code = 404, message = "Trigger doesn't exist.", response = ApiError.class), @ApiResponse(code = 500, message = "Internal server error.", response = ApiError.class) }) public Response setTriggersEnabled( @ApiParam(required = true, value = "List of trigger ids to enable or disable", allowableValues = "Comma separated list of triggerIds to be enabled or disabled.") @QueryParam("triggerIds") final String triggerIds, @ApiParam(required = true, value = "Set enabled or disabled.") @QueryParam("enabled") final Boolean enabled) { try { if (isEmpty(triggerIds)) { return ResponseUtil.badRequest("TriggerIds must be non empty."); } if (null == enabled) { return ResponseUtil.badRequest("Enabled must be non-empty."); } definitions.updateTriggerEnablement(tenantId, triggerIds, enabled); return ResponseUtil.ok(); } catch (Exception e) { return ResponseUtil.onException(e, log); } } private boolean checkTags(Trigger trigger) { return CommonUtil.checkTags(trigger.getTags()); } private boolean checkTags(GroupMemberInfo groupMemberInfo) { return CommonUtil.checkTags(groupMemberInfo.getMemberTags()); } private boolean checkTags(UnorphanMemberInfo unorphanMemberInfo) { return CommonUtil.checkTags(unorphanMemberInfo.getMemberTags()); } }