package com.intrbiz.bergamot.ui.api;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.log4j.Logger;
import com.intrbiz.accounting.Accounting;
import com.intrbiz.balsa.engine.route.Router;
import com.intrbiz.balsa.metadata.WithDataAdapter;
import com.intrbiz.bergamot.accounting.model.AccountingNotificationType;
import com.intrbiz.bergamot.accounting.model.SendNotificationAccountingEvent;
import com.intrbiz.bergamot.data.BergamotDB;
import com.intrbiz.bergamot.metadata.IgnoreBinding;
import com.intrbiz.bergamot.metadata.IsaObjectId;
import com.intrbiz.bergamot.model.Alert;
import com.intrbiz.bergamot.model.Comment;
import com.intrbiz.bergamot.model.Contact;
import com.intrbiz.bergamot.model.Site;
import com.intrbiz.bergamot.model.message.AlertMO;
import com.intrbiz.bergamot.model.message.notification.Notification;
import com.intrbiz.bergamot.model.message.notification.SendAcknowledge;
import com.intrbiz.bergamot.model.message.update.AlertUpdate;
import com.intrbiz.bergamot.model.message.update.Update;
import com.intrbiz.bergamot.queue.NotificationQueue;
import com.intrbiz.bergamot.queue.UpdateQueue;
import com.intrbiz.bergamot.queue.key.NotificationKey;
import com.intrbiz.bergamot.queue.key.UpdateKey;
import com.intrbiz.bergamot.queue.key.UpdateKey.UpdateType;
import com.intrbiz.bergamot.ui.BergamotApp;
import com.intrbiz.metadata.Any;
import com.intrbiz.metadata.CheckStringLength;
import com.intrbiz.metadata.Get;
import com.intrbiz.metadata.JSON;
import com.intrbiz.metadata.ListOf;
import com.intrbiz.metadata.Param;
import com.intrbiz.metadata.Prefix;
import com.intrbiz.metadata.RequirePermission;
import com.intrbiz.metadata.RequireValidPrincipal;
import com.intrbiz.metadata.Var;
import com.intrbiz.queue.RoutedProducer;
@Prefix("/api/alert")
@RequireValidPrincipal()
public class AlertsAPIRouter extends Router<BergamotApp>
{
private Logger logger = Logger.getLogger(AlertsAPIRouter.class);
private NotificationQueue notificationQueue;
private RoutedProducer<Notification, NotificationKey> notificationsProducer;
private UpdateQueue updateQueue;
private RoutedProducer<Update, UpdateKey> updateProducer;
private Accounting accounting = Accounting.create(AlertsAPIRouter.class);
public AlertsAPIRouter()
{
}
@Override
public void start() throws Exception
{
// notifications
this.notificationQueue = NotificationQueue.open();
this.notificationsProducer = this.notificationQueue.publishNotifications();
// updates
this.updateQueue = UpdateQueue.open();
this.updateProducer = this.updateQueue.publishUpdates();
}
@Get("/")
@JSON
@WithDataAdapter(BergamotDB.class)
@ListOf(AlertMO.class)
public List<AlertMO> getAlerts(BergamotDB db, @Var("site") Site site)
{
return db.listAlerts(site.getId()).stream().filter((a) -> permission("read", a.getCheckId())).map((x) -> x.toMO(currentPrincipal())).collect(Collectors.toList());
}
@Get("/id/:id")
@JSON(notFoundIfNull = true)
@WithDataAdapter(BergamotDB.class)
public AlertMO getAlert(BergamotDB db, @IsaObjectId() UUID id)
{
Alert alert = notNull(db.getAlert(id));
require(permission("read", alert.getCheckId()));
return alert.toMO(currentPrincipal());
}
@Get("/for-check/id/:id")
@JSON()
@WithDataAdapter(BergamotDB.class)
@ListOf(AlertMO.class)
public List<AlertMO> getAlertsForCheck(BergamotDB db, @IsaObjectId() UUID id)
{
require(permission("read", id));
return db.getAlertsForCheck(id).stream().map((x) -> x.toMO(currentPrincipal())).collect(Collectors.toList());
}
@Get("/current/for-check/id/:id")
@JSON(notFoundIfNull = true)
@WithDataAdapter(BergamotDB.class)
public AlertMO getCurrentAlertForCheck(BergamotDB db, @IsaObjectId() UUID id)
{
Alert alert = notNull(db.getCurrentAlertForCheck(id));
require(permission("read", alert.getCheckId()));
return alert.toMO(currentPrincipal());
}
@Any("/id/:id/acknowledge")
@JSON(notFoundIfNull = true)
@WithDataAdapter(BergamotDB.class)
public AlertMO acknowledgeAlert(
BergamotDB db,
@IsaObjectId() UUID id,
@Param("summary") @CheckStringLength(min = 1, max = 80, mandatory = true) String summary,
@Param("comment") @CheckStringLength(min = 0, max = 4096, mandatory = false) String comment
)
{
Alert alert = notNull(db.getAlert(id));
require(permission("acknowledge", alert.getCheckId()));
// can only acknowledge an non-recovered and non-acknowledged alert
if (! (alert.isAcknowledged() || alert.isRecovered()))
{
// the contact
Contact contact = currentPrincipal();
// the comment to add
Comment ackCom = new Comment().author(contact).acknowledges(alert).summary(summary).message(comment);
// acknowledge
db.execute(() -> {
db.setComment(ackCom);
alert.setAcknowledged(true);
alert.setAcknowledgedAt(new Timestamp(System.currentTimeMillis()));
alert.setAcknowledgedById(contact.getId());
db.setAlert(alert);
db.acknowledgeCheck(alert.getCheckId(), true);
});
// send acknowledge notifications
if (! alert.getCheck().getState().isSuppressedOrInDowntime())
{
SendAcknowledge sendAck = alert.createAcknowledgeNotification(Calendar.getInstance(), contact, ackCom);
if (sendAck != null && (! sendAck.getTo().isEmpty()))
{
logger.warn("Sending acknowledge for " + alert.getId());
this.notificationsProducer.publish(new NotificationKey(contact.getSite().getId()), sendAck);
// accounting
this.accounting.account(new SendNotificationAccountingEvent(alert.getSiteId(), alert.getId(), alert.getCheckId(), AccountingNotificationType.ACKNOWLEDGEMENT, sendAck.getTo().size(), 0, null));
}
else
{
logger.warn("Not sending acknowledge for " + alert.getId());
}
}
// send alert update
this.updateProducer.publish(new UpdateKey(UpdateType.ALERT, alert.getSiteId(), alert.getId()), new AlertUpdate(alert.toMO(currentPrincipal())));
}
return alert.toMO(currentPrincipal());
}
@Get("/id/:id/render")
@JSON
@WithDataAdapter(BergamotDB.class)
@IgnoreBinding
public String renderAlert(BergamotDB db, @IsaObjectId() UUID id)
{
Alert alert = var("alert", notNull(db.getAlert(id)));
require(permission("read", alert.getCheckId()));
return encodeBuffered("include/alert");
}
@Get("/id/:id/dashboard/render")
@JSON()
@RequirePermission("api.read.alert")
@WithDataAdapter(BergamotDB.class)
@IgnoreBinding
public String renderComment(BergamotDB db, @IsaObjectId() UUID id)
{
Alert alert = notNull(db.getAlert(id));
require(permission("read", alert.getCheckId()));
var("check", alert.getCheck());
var("alert", true);
return encodeBuffered("include/check");
}
}