package com.intrbiz.bergamot.ui.api;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import com.fasterxml.jackson.core.JsonGenerator;
import com.intrbiz.Util;
import com.intrbiz.balsa.engine.route.Router;
import com.intrbiz.balsa.metadata.WithDataAdapter;
import com.intrbiz.bergamot.metadata.IgnoreBinding;
import com.intrbiz.bergamot.metadata.IsaObjectId;
import com.intrbiz.bergamot.model.Site;
import com.intrbiz.bergamot.model.message.reading.CheckReadingMO;
import com.intrbiz.bergamot.ui.BergamotApp;
import com.intrbiz.lamplighter.data.LamplighterDB;
import com.intrbiz.lamplighter.model.CheckReading;
import com.intrbiz.lamplighter.model.StoredDoubleGaugeReading;
import com.intrbiz.lamplighter.model.StoredFloatGaugeReading;
import com.intrbiz.lamplighter.model.StoredIntGaugeReading;
import com.intrbiz.lamplighter.model.StoredLongGaugeReading;
import com.intrbiz.lamplighter.model.StoredReading;
import com.intrbiz.metadata.Any;
import com.intrbiz.metadata.CheckRegEx;
import com.intrbiz.metadata.CoalesceMode;
import com.intrbiz.metadata.IsaInt;
import com.intrbiz.metadata.IsaLong;
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;
@Prefix("/api/lamplighter")
@RequireValidPrincipal()
public class LamplighterAPIRouter extends Router<BergamotApp>
{
@Any("/check/id/:id/readings")
@JSON(notFoundIfNull = true)
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@ListOf(CheckReadingMO.class)
public List<CheckReadingMO> getReadingsByCheck(LamplighterDB db, @Var("site") Site site, @IsaObjectId() UUID id)
{
List<CheckReadingMO> readings = new LinkedList<CheckReadingMO>();
for (CheckReading reading : db.getCheckReadingsForCheck(id))
{
readings.add(new CheckReadingMO(
reading.getId(),
reading.getSiteId(),
reading.getCheckId(),
reading.getName(),
reading.getSummary(),
reading.getDescription(),
reading.getUnit(),
reading.getReadingType(),
reading.getCreated() == null ? 0 : reading.getCreated().getTime(),
reading.getUpdated() == null ? 0 : reading.getUpdated().getTime(),
reading.getPollInterval()
));
}
return readings;
}
// double gauge
@Any("/graph/reading/gauge/double/:id/latest/:limit")
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@IgnoreBinding
public void getLatestDoubleReadings(
LamplighterDB db,
@Var("site") Site site,
@IsaObjectId() UUID id,
@IsaInt(min = 1, max = 1000, defaultValue = 100, coalesce = CoalesceMode.ALWAYS) Integer limit,
@Param("series") String series
) throws IOException
{
// get the data
CheckReading checkReading = db.getCheckReading(id);
List<StoredDoubleGaugeReading> readings = db.getLatestDoubleGaugeReadings(site.getId(), id, limit);
// write
JsonGenerator jenny = response().ok().json().getJsonWriter();
this.writeLineChartData(jenny, checkReading, readings, series, StoredDoubleGaugeReading::getValue, StoredDoubleGaugeReading::getWarning, StoredDoubleGaugeReading::getCritical, StoredDoubleGaugeReading::getMin, StoredDoubleGaugeReading::getMax);
}
@Any("/graph/reading/gauge/double/:id/date/:rollup/:agg/:start/:end")
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@IgnoreBinding
public void getDoubleReadingsByDate(
LamplighterDB db,
@Var("site") Site site,
@IsaObjectId() UUID id,
@IsaLong(mandatory = true, defaultValue = 300_000L, coalesce = CoalesceMode.ALWAYS) Long rollup,
@CheckRegEx(value="(avg|sum)", mandatory = true, defaultValue = "avg", coalesce = CoalesceMode.ALWAYS) String agg,
@IsaLong() Long start,
@IsaLong() Long end,
@Param("series") String series
) throws IOException
{
// get the data
CheckReading checkReading = db.getCheckReading(id);
List<StoredDoubleGaugeReading> readings = db.getDoubleGaugeReadingsByDate(checkReading.getSiteId(), checkReading.getId(), new Timestamp(start), new Timestamp(end), rollup, agg);
// write
JsonGenerator jenny = response().ok().json().getJsonWriter();
this.writeLineChartData(jenny, checkReading, readings, series, StoredDoubleGaugeReading::getValue, StoredDoubleGaugeReading::getWarning, StoredDoubleGaugeReading::getCritical, StoredDoubleGaugeReading::getMin, StoredDoubleGaugeReading::getMax);
}
// float gauge
@Any("/graph/reading/gauge/float/:id/latest/:limit")
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@IgnoreBinding
public void getLatestFloatReadings(
LamplighterDB db,
@Var("site") Site site,
@IsaObjectId() UUID id,
@IsaInt(min = 1, max = 1000, defaultValue = 100, coalesce = CoalesceMode.ALWAYS) Integer limit,
@Param("series") String series
) throws IOException
{
// get the data
CheckReading checkReading = db.getCheckReading(id);
List<StoredFloatGaugeReading> readings = db.getLatestFloatGaugeReadings(site.getId(), id, limit);
// write
JsonGenerator jenny = response().ok().json().getJsonWriter();
this.writeLineChartData(jenny, checkReading, readings, series, StoredFloatGaugeReading::getValue, StoredFloatGaugeReading::getWarning, StoredFloatGaugeReading::getCritical, StoredFloatGaugeReading::getMin, StoredFloatGaugeReading::getMax);
}
@Any("/graph/reading/gauge/float/:id/date/:rollup/:agg/:start/:end")
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@IgnoreBinding
public void getFloatReadingsByDate(
LamplighterDB db,
@Var("site") Site site,
@IsaObjectId() UUID id,
@IsaLong(mandatory = true, defaultValue = 300_000L, coalesce = CoalesceMode.ALWAYS) Long rollup,
@CheckRegEx(value="(avg|sum)", mandatory = true, defaultValue = "avg", coalesce = CoalesceMode.ALWAYS) String agg,
@IsaLong() Long start,
@IsaLong() Long end,
@Param("series") String series
) throws IOException
{
// get the data
CheckReading checkReading = db.getCheckReading(id);
List<StoredFloatGaugeReading> readings = db.getFloatGaugeReadingsByDate(checkReading.getSiteId(), checkReading.getId(), new Timestamp(start), new Timestamp(end), rollup, agg);
// write
JsonGenerator jenny = response().ok().json().getJsonWriter();
this.writeLineChartData(jenny, checkReading, readings, series, StoredFloatGaugeReading::getValue, StoredFloatGaugeReading::getWarning, StoredFloatGaugeReading::getCritical, StoredFloatGaugeReading::getMin, StoredFloatGaugeReading::getMax);
}
// long gauge
@Any("/graph/reading/gauge/long/:id/latest/:limit")
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@IgnoreBinding
public void getLatestLongReadings(
LamplighterDB db,
@Var("site") Site site,
@IsaObjectId() UUID id,
@IsaInt(min = 1, max = 1000, defaultValue = 100, coalesce = CoalesceMode.ALWAYS) Integer limit,
@Param("series") String series
) throws IOException
{
// get the data
CheckReading checkReading = db.getCheckReading(id);
List<StoredLongGaugeReading> readings = db.getLatestLongGaugeReadings(site.getId(), id, limit);
// write
JsonGenerator jenny = response().ok().json().getJsonWriter();
this.writeLineChartData(jenny, checkReading, readings, series, StoredLongGaugeReading::getValue, StoredLongGaugeReading::getWarning, StoredLongGaugeReading::getCritical, StoredLongGaugeReading::getMin, StoredLongGaugeReading::getMax);
}
@Any("/graph/reading/gauge/long/:id/date/:rollup/:agg/:start/:end")
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@IgnoreBinding
public void getLongReadingsByDate(
LamplighterDB db,
@Var("site") Site site,
@IsaObjectId() UUID id,
@IsaLong(mandatory = true, defaultValue = 300_000L, coalesce = CoalesceMode.ALWAYS) Long rollup,
@CheckRegEx(value="(avg|sum)", mandatory = true, defaultValue = "avg", coalesce = CoalesceMode.ALWAYS) String agg,
@IsaLong() Long start,
@IsaLong() Long end,
@Param("series") String series
) throws IOException
{
// get the data
CheckReading checkReading = db.getCheckReading(id);
List<StoredLongGaugeReading> readings = db.getLongGaugeReadingsByDate(checkReading.getSiteId(), checkReading.getId(), new Timestamp(start), new Timestamp(end), rollup, agg);
// write
JsonGenerator jenny = response().ok().json().getJsonWriter();
this.writeLineChartData(jenny, checkReading, readings, series, StoredLongGaugeReading::getValue, StoredLongGaugeReading::getWarning, StoredLongGaugeReading::getCritical, StoredLongGaugeReading::getMin, StoredLongGaugeReading::getMax);
}
// int gauge
@Any("/graph/reading/gauge/int/:id/latest/:limit")
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@IgnoreBinding
public void getLatestIntReadings(
LamplighterDB db,
@Var("site") Site site,
@IsaObjectId() UUID id,
@IsaInt(min = 1, max = 1000, defaultValue = 100, coalesce = CoalesceMode.ALWAYS) Integer limit,
@Param("series") String series
) throws IOException
{
// get the data
CheckReading checkReading = db.getCheckReading(id);
List<StoredIntGaugeReading> readings = db.getLatestIntGaugeReadings(site.getId(), id, limit);
// write
JsonGenerator jenny = response().ok().json().getJsonWriter();
this.writeLineChartData(jenny, checkReading, readings, series, StoredIntGaugeReading::getValue, StoredIntGaugeReading::getWarning, StoredIntGaugeReading::getCritical, StoredIntGaugeReading::getMin, StoredIntGaugeReading::getMax);
}
@Any("/graph/reading/gauge/int/:id/date/:rollup/:agg/:start/:end")
@RequirePermission("api.read.lamplighter.readings")
@WithDataAdapter(LamplighterDB.class)
@IgnoreBinding
public void getIntReadingsByDate(
LamplighterDB db,
@Var("site") Site site,
@IsaObjectId() UUID id,
@IsaLong(mandatory = true, defaultValue = 300_000L, coalesce = CoalesceMode.ALWAYS) Long rollup,
@CheckRegEx(value="(avg|sum)", mandatory = true, defaultValue = "avg", coalesce = CoalesceMode.ALWAYS) String agg,
@IsaLong() Long start,
@IsaLong() Long end,
@Param("series") String series
) throws IOException
{
// get the data
CheckReading checkReading = db.getCheckReading(id);
List<StoredIntGaugeReading> readings = db.getIntGaugeReadingsByDate(checkReading.getSiteId(), checkReading.getId(), new Timestamp(start), new Timestamp(end), rollup, agg);
// write
JsonGenerator jenny = response().ok().json().getJsonWriter();
this.writeLineChartData(jenny, checkReading, readings, series, StoredIntGaugeReading::getValue, StoredIntGaugeReading::getWarning, StoredIntGaugeReading::getCritical, StoredIntGaugeReading::getMin, StoredIntGaugeReading::getMax);
}
// generic
/**
* Generically output a line chart JSON data structure
* @param jenny the json output stream
* @param checkReading the check reading
* @param readings the data points
* @param series which series to output
* @param getValue the value accessor
* @param getWarning the warning accessor
* @param getCritical the critical accessor
* @param getMin the min accessor
* @param getMax the max accessor
* @throws IOException
*/
private <T extends StoredReading> void writeLineChartData(JsonGenerator jenny, CheckReading checkReading, List<T> readings, String series, Function<T,Object> getValue, Function<T,Object> getWarning, Function<T,Object> getCritical, Function<T,Object> getMin, Function<T,Object> getMax) throws IOException
{
jenny.writeStartObject();
// title
jenny.writeFieldName("title");
jenny.writeString(checkReading.getSummary() + (Util.isEmpty(checkReading.getUnit()) ? "" : " (" + checkReading.getUnit() + ")"));
// x-title
jenny.writeFieldName("x-title");
jenny.writeString("");
// y-title
jenny.writeFieldName("y-title");
jenny.writeString(Util.isEmpty(checkReading.getUnit()) ? "" : checkReading.getUnit());
// x
jenny.writeFieldName("x");
jenny.writeStartArray();
for (T reading : readings)
{
jenny.writeNumber(reading.getCollectedAt().getTime());
}
jenny.writeEndArray();
// y sets
jenny.writeFieldName("y");
jenny.writeStartArray();
// value
jenny.writeStartObject();
jenny.writeFieldName("title");
jenny.writeString(checkReading.getSummary() + (Util.isEmpty(checkReading.getUnit()) ? "" : " (" + checkReading.getUnit() + ")"));
jenny.writeFieldName("colour");
jenny.writeString("#00BF00");
jenny.writeFieldName("y");
jenny.writeStartArray();
for (T reading : readings)
{
jenny.writeObject(getValue.apply(reading));
}
jenny.writeEndArray();
jenny.writeEndObject();
// optional series
if (! (Util.isEmpty(series) || "none".equals(series)))
{
// warning
if (series.contains("warning"))
{
jenny.writeStartObject();
jenny.writeFieldName("title");
jenny.writeString("Warning");
jenny.writeFieldName("colour");
jenny.writeString("#FFBF00");
jenny.writeFieldName("y");
jenny.writeStartArray();
for (T reading : readings)
{
jenny.writeObject(getWarning.apply(reading));
}
jenny.writeEndArray();
jenny.writeEndObject();
}
// critical
if (series.contains("critical"))
{
jenny.writeStartObject();
jenny.writeFieldName("title");
jenny.writeString("Critical");
jenny.writeFieldName("colour");
jenny.writeString("#E20800");
jenny.writeFieldName("y");
jenny.writeStartArray();
for (T reading : readings)
{
jenny.writeObject(getCritical.apply(reading));
}
jenny.writeEndArray();
jenny.writeEndObject();
}
// min
if (series.contains("min"))
{
jenny.writeStartObject();
jenny.writeFieldName("title");
jenny.writeString("Min");
jenny.writeFieldName("colour");
jenny.writeString("#A4C0E4");
jenny.writeFieldName("y");
jenny.writeStartArray();
for (T reading : readings)
{
jenny.writeObject(getMin.apply(reading));
}
jenny.writeEndArray();
jenny.writeEndObject();
}
// max
if (series.contains("max"))
{
jenny.writeStartObject();
jenny.writeFieldName("title");
jenny.writeString("Max");
jenny.writeFieldName("colour");
jenny.writeString("#A4C0E4");
jenny.writeFieldName("y");
jenny.writeStartArray();
for (T reading : readings)
{
jenny.writeObject(getMax.apply(reading));
}
jenny.writeEndArray();
jenny.writeEndObject();
}
}
// end y sets
jenny.writeEndArray();
jenny.writeEndObject();
}
}