|
|
@@ -2,13 +2,10 @@ package cz.senslog.telemetry.server.ws;
|
|
|
|
|
|
import cz.senslog.telemetry.app.Application;
|
|
|
import cz.senslog.telemetry.app.PropertyConfig;
|
|
|
-import cz.senslog.telemetry.database.DataNotFoundException;
|
|
|
-import cz.senslog.telemetry.database.PagingRetrieve;
|
|
|
-import cz.senslog.telemetry.database.SortType;
|
|
|
+import cz.senslog.telemetry.database.*;
|
|
|
import cz.senslog.telemetry.database.domain.*;
|
|
|
import cz.senslog.telemetry.database.repository.MapLogRepository;
|
|
|
import cz.senslog.telemetry.database.repository.SensLogRepository;
|
|
|
-import cz.senslog.telemetry.module.EventBusModulePaths;
|
|
|
import cz.senslog.telemetry.utils.CascadeCondition;
|
|
|
import cz.senslog.telemetry.utils.ResourcesUtils;
|
|
|
import io.vertx.core.CompositeFuture;
|
|
|
@@ -23,6 +20,7 @@ import io.vertx.core.json.JsonObject;
|
|
|
import io.vertx.ext.web.MIMEHeader;
|
|
|
import io.vertx.ext.web.RoutingContext;
|
|
|
import io.vertx.json.schema.common.JsonUtil;
|
|
|
+import io.vertx.pgclient.PgException;
|
|
|
import io.vertx.sqlclient.RowSet;
|
|
|
import io.vertx.sqlclient.Tuple;
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
@@ -31,13 +29,10 @@ import org.apache.pdfbox.Loader;
|
|
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
|
import org.apache.pdfbox.pdmodel.PDPage;
|
|
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
|
|
-import org.apache.pdfbox.pdmodel.font.PDFont;
|
|
|
-import org.apache.pdfbox.pdmodel.font.PDSimpleFont;
|
|
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
|
|
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
|
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
|
|
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
|
|
|
-import org.apache.pdfbox.pdmodel.interactive.form.PDField;
|
|
|
|
|
|
|
|
|
import java.io.*;
|
|
|
@@ -57,6 +52,8 @@ import static cz.senslog.telemetry.server.ws.AuthorizationScope.READ_PERSONAL;
|
|
|
import static cz.senslog.telemetry.server.ws.AuthorizationType.BEARER;
|
|
|
import static cz.senslog.telemetry.server.ws.AuthorizationType.NONE;
|
|
|
import static cz.senslog.telemetry.server.ws.ContentType.*;
|
|
|
+import static cz.senslog.telemetry.server.ws.WSParameters.ObservationFormat.ID;
|
|
|
+import static cz.senslog.telemetry.server.ws.WSParameters.ObservationFormat.NAME;
|
|
|
import static cz.senslog.telemetry.utils.ListUtils.*;
|
|
|
import static io.vertx.core.http.HttpHeaders.*;
|
|
|
import static java.lang.String.format;
|
|
|
@@ -67,7 +64,6 @@ import static java.util.Comparator.comparing;
|
|
|
import static java.util.Optional.ofNullable;
|
|
|
import static java.util.stream.Collectors.*;
|
|
|
import static org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode.APPEND;
|
|
|
-import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA;
|
|
|
|
|
|
public class OpenAPIHandler {
|
|
|
|
|
|
@@ -210,6 +206,7 @@ public class OpenAPIHandler {
|
|
|
int offset = params.queryParams().offset();
|
|
|
int limit = params.queryParams().limit();
|
|
|
ContentType format = params.queryParams().format();
|
|
|
+ WSParameters.ObservationFormat obsFormat = params.queryParams().observationFormat();
|
|
|
List<Filter> filters = params.queryParams().filter();
|
|
|
boolean navigationLinks = params.queryParams().navigationLinks();
|
|
|
|
|
|
@@ -231,7 +228,7 @@ public class OpenAPIHandler {
|
|
|
.ifThen(u -> u.hasPermissionScope(READ_PERSONAL), u -> repo.findObservationsByIdentityAndCampaignIdWithPaging(u.getId(), campaignId, from, to, offset, limit, filters))
|
|
|
.execute(r -> {
|
|
|
switch (format) {
|
|
|
- case JSON -> r.onSuccess(paging -> rc.response().putHeader(CONTENT_TYPE, JSON.contentType())
|
|
|
+ case JSON -> r.onSuccess(paging -> rc.response().putHeader(CONTENT_TYPE, format.contentType())
|
|
|
.end(navLinks.mergeIn(navigationLinks && paging.hasNext() ? JsonObject.of(
|
|
|
"next@NavigationLink", createNextNavLink.apply(paging.size())) : JsonObject.of())
|
|
|
.mergeIn(JsonObject.of(
|
|
|
@@ -249,12 +246,15 @@ public class OpenAPIHandler {
|
|
|
"latitude", o.getLocation().getLatitude(),
|
|
|
"altitude", o.getLocation().getAltitude()
|
|
|
),
|
|
|
- "observedValues", o.getObservedValues()
|
|
|
+ "observedValues", CascadeCondition.of(obsFormat)
|
|
|
+ .ifThen(f -> f.equals(NAME), f -> o.getObservedValuesByName())
|
|
|
+ .ifThen(f -> f.equals(ID), f -> o.getObservedValuesById())
|
|
|
+ .get()
|
|
|
)).toList()
|
|
|
)
|
|
|
)).encode()))
|
|
|
.onFailure(rc::fail);
|
|
|
- case GEOJSON -> r.onSuccess(paging -> rc.response().putHeader(CONTENT_TYPE, GEOJSON.contentType()).end(JsonObject.of(
|
|
|
+ case GEOJSON -> r.onSuccess(paging -> rc.response().putHeader(CONTENT_TYPE, format.contentType()).end(JsonObject.of(
|
|
|
"type", "FeatureCollection",
|
|
|
"metadata", JsonObject.of(
|
|
|
"size", paging.size(),
|
|
|
@@ -276,7 +276,10 @@ public class OpenAPIHandler {
|
|
|
"unitId", o.getUnitId(),
|
|
|
"timestamp", DATE_TIME_FORMATTER_AT_ZONE.apply(o.getTimestamp(), zone),
|
|
|
"speed", o.getSpeed(),
|
|
|
- "observedValues", o.getObservedValues()
|
|
|
+ "observedValues", CascadeCondition.of(obsFormat)
|
|
|
+ .ifThen(f -> f.equals(NAME), f -> o.getObservedValuesByName())
|
|
|
+ .ifThen(f -> f.equals(ID), f -> o.getObservedValuesById())
|
|
|
+ .get()
|
|
|
)
|
|
|
)).collect(toList()))).encode()))
|
|
|
.onFailure(rc::fail);
|
|
|
@@ -301,6 +304,7 @@ public class OpenAPIHandler {
|
|
|
int limit = params.queryParams().limit();
|
|
|
SortType sort = params.queryParams().sort();
|
|
|
ContentType format = params.queryParams().format();
|
|
|
+ WSParameters.ObservationFormat obsFormat = params.queryParams().observationFormat();
|
|
|
List<Filter> filters = params.queryParams().filter();
|
|
|
boolean navigationLinks = params.queryParams().navigationLinks();
|
|
|
|
|
|
@@ -338,7 +342,10 @@ public class OpenAPIHandler {
|
|
|
"longitude", o.getLocation().getLongitude(),
|
|
|
"latitude", o.getLocation().getLatitude(),
|
|
|
"altitude", o.getLocation().getAltitude()),
|
|
|
- "observedValues", o.getObservedValues()
|
|
|
+ "observedValues", CascadeCondition.of(obsFormat)
|
|
|
+ .ifThen(f -> f.equals(NAME), f -> o.getObservedValuesByName())
|
|
|
+ .ifThen(f -> f.equals(ID), f -> o.getObservedValuesById())
|
|
|
+ .get()
|
|
|
)).collect(toList())))).encode()))
|
|
|
.onFailure(rc::fail);
|
|
|
case GEOJSON -> r.onSuccess(paging -> rc.response().putHeader(CONTENT_TYPE, format.contentType()).end(JsonObject.of(
|
|
|
@@ -363,7 +370,10 @@ public class OpenAPIHandler {
|
|
|
"unitId", o.getUnitId(),
|
|
|
"timestamp", DATE_TIME_FORMATTER_AT_ZONE.apply(o.getTimestamp(), zone),
|
|
|
"speed", o.getSpeed(),
|
|
|
- "observedValues", o.getObservedValues()
|
|
|
+ "observedValues", CascadeCondition.of(obsFormat)
|
|
|
+ .ifThen(f -> f.equals(NAME), f -> o.getObservedValuesByName())
|
|
|
+ .ifThen(f -> f.equals(ID), f -> o.getObservedValuesById())
|
|
|
+ .get()
|
|
|
)
|
|
|
)).collect(toList()))).encode()))
|
|
|
.onFailure(rc::fail);
|
|
|
@@ -399,7 +409,7 @@ public class OpenAPIHandler {
|
|
|
.ifThen(u -> u.hasPermissionScope(READ_PERSONAL), u -> repo.findUnitsLocationsByIdentityAndCampaignId(u.getId(), campaignId, limitPerUnit, from, to, sortType, filters))
|
|
|
.execute(r -> {
|
|
|
switch (format) {
|
|
|
- case JSON -> r.onSuccess(locations -> rc.response().putHeader(CONTENT_TYPE, JSON.contentType()).end(navLinks.mergeIn(JsonObject.of(
|
|
|
+ case JSON -> r.onSuccess(locations -> rc.response().putHeader(CONTENT_TYPE, format.contentType()).end(navLinks.mergeIn(JsonObject.of(
|
|
|
"params", paramsJson,
|
|
|
"size", locations.size(),
|
|
|
"data", new JsonArray(locations.stream().map(l -> JsonObject.of(
|
|
|
@@ -412,7 +422,7 @@ public class OpenAPIHandler {
|
|
|
)).toList()))).encode()))
|
|
|
.onFailure(rc::fail);
|
|
|
case GEOJSON -> r.onSuccess(data -> Optional.of(data.stream().collect(groupingBy(UnitLocation::getUnitId))).ifPresent(unitLocation -> rc.response()
|
|
|
- .putHeader(CONTENT_TYPE, GEOJSON.contentType()).end(JsonObject.of(
|
|
|
+ .putHeader(CONTENT_TYPE, format.contentType()).end(JsonObject.of(
|
|
|
"type", "FeatureCollection",
|
|
|
"metadata", JsonObject.of(
|
|
|
"limitPerUnit", limitPerUnit
|
|
|
@@ -1287,7 +1297,7 @@ public class OpenAPIHandler {
|
|
|
"longitude", o.getLocation().getLongitude(),
|
|
|
"latitude", o.getLocation().getLatitude(),
|
|
|
"altitude", o.getLocation().getAltitude()),
|
|
|
- "observedValues", o.getObservedValues()
|
|
|
+ "observedValues", o.getObservedValuesByName()
|
|
|
)).collect(toList())))).encode()))
|
|
|
.onFailure(rc::fail);
|
|
|
case GEOJSON -> r.onSuccess(paging -> rc.response().putHeader(CONTENT_TYPE, format.contentType()).end(JsonObject.of(
|
|
|
@@ -1312,7 +1322,7 @@ public class OpenAPIHandler {
|
|
|
"unitId", o.getUnitId(),
|
|
|
"timestamp", DATE_TIME_FORMATTER_AT_ZONE.apply(o.getTimestamp(), zone),
|
|
|
"speed", o.getSpeed(),
|
|
|
- "observedValues", o.getObservedValues()
|
|
|
+ "observedValues", o.getObservedValuesByName()
|
|
|
)
|
|
|
)).collect(toList()))).encode()))
|
|
|
.onFailure(rc::fail);
|
|
|
@@ -1420,6 +1430,16 @@ public class OpenAPIHandler {
|
|
|
.onFailure(rc::fail));
|
|
|
}
|
|
|
|
|
|
+ private void failureHandler(Throwable th, Consumer<Throwable> contextFailure) {
|
|
|
+ Throwable newTh = th;
|
|
|
+ if (th instanceof DatabaseException dbTh) {
|
|
|
+ if (dbTh.getErrorState() == SQLErrorState.DUPLICATE_KEY) {
|
|
|
+ newTh = new HttpIllegalArgumentException(409, dbTh.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ contextFailure.accept(newTh);
|
|
|
+ }
|
|
|
+
|
|
|
public void campaignIdUnitsObservationsPOST(RoutingContext rc) {
|
|
|
AuthBearerUser user = AuthBearerUser.of(rc.user());
|
|
|
WSParameters params = WSParameters.wrap(rc.queryParams(), rc.pathParams());
|
|
|
@@ -1432,6 +1452,7 @@ public class OpenAPIHandler {
|
|
|
if (original.isEmpty()) { return Future.succeededFuture(emptyList()); }
|
|
|
List<Future<UnitTelemetry>> futures = new ArrayList<>(original.size());
|
|
|
Map<Long, List<UnitTelemetry>> staticUnitToTlm = new HashMap<>();
|
|
|
+ // group telemetries by unit_id that do not have location
|
|
|
for (UnitTelemetry t : original) {
|
|
|
if (t.getLocation() == null) {
|
|
|
staticUnitToTlm.computeIfAbsent(t.getUnitId(), unitId -> new ArrayList<>()).add(t);
|
|
|
@@ -1440,6 +1461,7 @@ public class OpenAPIHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // find the nearest location to the timestamp by the unit or use default location
|
|
|
for (Map.Entry<Long, List<UnitTelemetry>> entry : staticUnitToTlm.entrySet()) {
|
|
|
long unitId = entry.getKey();
|
|
|
for (UnitTelemetry telemetry : entry.getValue()) {
|
|
|
@@ -1447,7 +1469,8 @@ public class OpenAPIHandler {
|
|
|
Future<UnitTelemetry> future = repo.findMobileUnitByStaticUnitIdAndTimestamp(unitId, timestamp)
|
|
|
.compose(u -> repo.findNearestLocationToTimestampByCampaignIdAndUnitId(u.getCampaignId(), u.getMobileUnitId(), timestamp, timestamp.minusMinutes(minuteDiff), timestamp.plusMinutes(minuteDiff))
|
|
|
.map(t -> t.map(UnitLocation::getLocation).orElseGet(u::getDefaultLocation))
|
|
|
- .map(l -> UnitTelemetry.of(unitId, timestamp, l, 0, telemetry.getObservedValues())));
|
|
|
+ .map(l -> UnitTelemetry.ofByIDs(unitId, timestamp, l, 0, telemetry.getObservedValuesById())))
|
|
|
+ .onFailure(th -> failureHandler(th, rc::fail));
|
|
|
futures.add(future);
|
|
|
}
|
|
|
}
|
|
|
@@ -1467,8 +1490,9 @@ public class OpenAPIHandler {
|
|
|
.toSensLogObservationAsStream(savedTelemetries)
|
|
|
.map(SensLogObservation::toJsonObject).toList()
|
|
|
);
|
|
|
- vertx.eventBus().request(EventBusModulePaths.SENSLOG_OBSERVATIONS, sensLogObsArr)
|
|
|
- .onSuccess(v -> logger.info(v.body())).onFailure(logger::error);
|
|
|
+ // TODO enable if Analytics is used
|
|
|
+// vertx.eventBus().request(EventBusModulePaths.SENSLOG_OBSERVATIONS, sensLogObsArr)
|
|
|
+// .onSuccess(v -> logger.info(v.body())).onFailure(logger::error);
|
|
|
rc.response().end(new JsonArray(savedTelemetries.stream().map(t -> JsonObject.of(
|
|
|
"id", t.getId(),
|
|
|
"unitId", t.getUnitId(),
|
|
|
@@ -1479,11 +1503,11 @@ public class OpenAPIHandler {
|
|
|
"latitude", t.getLocation().getLatitude(),
|
|
|
"altitude", t.getLocation().getAltitude()
|
|
|
),
|
|
|
- "observedValues", t.getObservedValues()
|
|
|
+ "observedValues", t.getObservedValuesById()
|
|
|
)).toList()
|
|
|
).encode());
|
|
|
})
|
|
|
- .onFailure(rc::fail)
|
|
|
+ .onFailure(th -> failureHandler(th, rc::fail))
|
|
|
)))));
|
|
|
|
|
|
final BiFunction<String, Integer, OffsetDateTime> toTimestamp = (timestamp, epoch) -> {
|
|
|
@@ -1498,7 +1522,7 @@ public class OpenAPIHandler {
|
|
|
switch (ContentType.ofType(rc.request().getHeader(CONTENT_TYPE))) {
|
|
|
case JSON -> Optional.of(rc.body().asJsonArray())
|
|
|
.map(jsonArray -> jsonArray.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast)
|
|
|
- .map(tel -> UnitTelemetry.of(
|
|
|
+ .map(tel -> UnitTelemetry.ofByIDs(
|
|
|
tel.getLong("unitId"),
|
|
|
toTimestamp.apply(tel.getString("timestamp"), tel.getInteger("epoch")),
|
|
|
Optional.ofNullable(tel.getJsonObject("location")).map(location -> Location.of(
|
|
|
@@ -1513,7 +1537,7 @@ public class OpenAPIHandler {
|
|
|
|
|
|
case GEOJSON -> Optional.of(rc.body().asJsonObject()).map(j -> j.getJsonArray("features"))
|
|
|
.map(jsonArray -> jsonArray.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast)
|
|
|
- .map(feature -> UnitTelemetry.of(
|
|
|
+ .map(feature -> UnitTelemetry.ofByIDs(
|
|
|
feature.getJsonObject("properties").getLong("unitId"),
|
|
|
OffsetDateTime.parse(feature.getJsonObject("properties").getString("timestamp")),
|
|
|
Optional.of(feature.getJsonObject("geometry").getJsonArray("coordinates")).map(location -> Location.of(
|
|
|
@@ -1558,7 +1582,7 @@ public class OpenAPIHandler {
|
|
|
"latitude", a.getObservation().getLocation().getLatitude(),
|
|
|
"altitude", a.getObservation().getLocation().getAltitude()
|
|
|
),
|
|
|
- "observedValues", a.getObservation().getObservedValues()
|
|
|
+ "observedValues", a.getObservation().getObservedValuesByName()
|
|
|
)
|
|
|
)).encode()))
|
|
|
.onFailure(rc::fail)
|
|
|
@@ -1813,7 +1837,7 @@ public class OpenAPIHandler {
|
|
|
.onSuccess(id -> rc.response().end(JsonObject.of(
|
|
|
"id", id
|
|
|
).encode()))
|
|
|
- .onFailure(rc::fail);
|
|
|
+ .onFailure(th -> failureHandler(th, rc::fail));
|
|
|
}
|
|
|
|
|
|
public void alertIdPUT(RoutingContext rc) {
|
|
|
@@ -1825,7 +1849,7 @@ public class OpenAPIHandler {
|
|
|
.onSuccess(id -> rc.response().end(JsonObject.of(
|
|
|
"id", id
|
|
|
).encode()))
|
|
|
- .onFailure(rc::fail);
|
|
|
+ .onFailure(th -> failureHandler(th, rc::fail));
|
|
|
}
|
|
|
|
|
|
public void alertIdDELETE(RoutingContext rc) {
|
|
|
@@ -1837,7 +1861,7 @@ public class OpenAPIHandler {
|
|
|
.onSuccess(id -> rc.response().end(JsonObject.of(
|
|
|
"id", id
|
|
|
).encode()))
|
|
|
- .onFailure(rc::fail);
|
|
|
+ .onFailure(th -> failureHandler(th, rc::fail));
|
|
|
}
|
|
|
|
|
|
public void legacyInsertObservationsGET(RoutingContext rc) {
|
|
|
@@ -1863,7 +1887,7 @@ public class OpenAPIHandler {
|
|
|
.compose(u -> repo
|
|
|
.findNearestLocationToTimestampByCampaignIdAndUnitId(u.getCampaignId(), u.getMobileUnitId(), timestamp, timestamp.minusMinutes(minuteDiff), timestamp.plusMinutes(minuteDiff))
|
|
|
.map(t -> t.map(UnitLocation::getLocation).orElseGet(u::getDefaultLocation)))
|
|
|
- .compose(l -> repo.saveTelemetry(UnitTelemetry.of(unitId, timestamp, l, 0, JsonObject.of(sensorIdStr, value))).map(r -> r.getId() > 0))
|
|
|
+ .compose(l -> repo.saveTelemetry(UnitTelemetry.ofByIDs(unitId, timestamp, l, 0, JsonObject.of(sensorIdStr, value))).map(r -> r.getId() > 0))
|
|
|
.onSuccess(res -> rc.response().end(res.toString()))
|
|
|
.onFailure(rc::fail);
|
|
|
}
|
|
|
@@ -1885,15 +1909,15 @@ public class OpenAPIHandler {
|
|
|
.onSuccess(f -> rc.response().end(JsonObject.of(
|
|
|
"total", f.list().stream().mapToLong(Long.class::cast).sum()
|
|
|
).encode()))
|
|
|
- .onFailure(rc::fail)
|
|
|
+ .onFailure(th -> failureHandler(th, rc::fail))
|
|
|
)
|
|
|
- .onFailure(rc::fail);
|
|
|
+ .onFailure(th -> failureHandler(th, rc::fail));
|
|
|
|
|
|
switch (ContentType.ofType(rc.request().getHeader(CONTENT_TYPE))) {
|
|
|
case JSON -> ofNullable(rc.body().asJsonArray())
|
|
|
.map(arr -> arr.stream()
|
|
|
.filter(JsonObject.class::isInstance).map(JsonObject.class::cast)
|
|
|
- .map(tel -> UnitTelemetry.of(
|
|
|
+ .map(tel -> UnitTelemetry.ofByIDs(
|
|
|
tel.getLong("unitId"),
|
|
|
OffsetDateTime.parse(tel.getString("timestamp")),
|
|
|
Optional.of(tel.getJsonObject("location")).map(location -> Location.of(
|
|
|
@@ -1907,7 +1931,7 @@ public class OpenAPIHandler {
|
|
|
|
|
|
case GEOJSON -> Optional.of(rc.body().asJsonObject()).map(j -> j.getJsonArray("features", JsonArray.of()))
|
|
|
.map(jsonArray -> jsonArray.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast)
|
|
|
- .map(feature -> UnitTelemetry.of(
|
|
|
+ .map(feature -> UnitTelemetry.ofByIDs(
|
|
|
feature.getJsonObject("properties").getLong("unitId"),
|
|
|
OffsetDateTime.parse(feature.getJsonObject("properties").getString("timestamp")),
|
|
|
Optional.of(feature.getJsonObject("geometry").getJsonArray("coordinates")).map(location -> Location.of(
|
|
|
@@ -1943,7 +1967,7 @@ public class OpenAPIHandler {
|
|
|
"latitude", org.getLocation().getLatitude(),
|
|
|
"altitude", org.getLocation().getAltitude()
|
|
|
),
|
|
|
- "observedValues", org.getObservedValues()
|
|
|
+ "observedValues", org.getObservedValuesByName()
|
|
|
));
|
|
|
}
|
|
|
rc.response().end(new JsonArray(resList).encode());
|
|
|
@@ -1971,7 +1995,7 @@ public class OpenAPIHandler {
|
|
|
switch (ContentType.ofType(rc.request().getHeader(CONTENT_TYPE))) {
|
|
|
case JSON -> Optional.of(rc.body().asJsonArray())
|
|
|
.map(jsonArray -> jsonArray.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast)
|
|
|
- .map(tel -> UnitTelemetry.of(
|
|
|
+ .map(tel -> UnitTelemetry.ofByIDs(
|
|
|
tel.getLong("unitId"),
|
|
|
toTimestamp.apply(tel.getString("timestamp"), tel.getInteger("epoch")),
|
|
|
Optional.of(tel.getJsonObject("location", defaultLocation)).map(location -> Location.of(
|
|
|
@@ -1986,7 +2010,7 @@ public class OpenAPIHandler {
|
|
|
|
|
|
case GEOJSON -> Optional.of(rc.body().asJsonObject()).map(j -> j.getJsonArray("features"))
|
|
|
.map(jsonArray -> jsonArray.stream().filter(JsonObject.class::isInstance).map(JsonObject.class::cast)
|
|
|
- .map(feature -> UnitTelemetry.of(
|
|
|
+ .map(feature -> UnitTelemetry.ofByIDs(
|
|
|
feature.getJsonObject("properties").getLong("unitId"),
|
|
|
OffsetDateTime.parse(feature.getJsonObject("properties").getString("timestamp")),
|
|
|
Optional.of(feature.getJsonObject("geometry").getJsonArray("coordinates")).map(location -> Location.of(
|
|
|
@@ -2111,7 +2135,7 @@ public class OpenAPIHandler {
|
|
|
.map(c -> Map.entry(c.sensorId(), row.getBoolean(Long.toString(c.sensorId()))))
|
|
|
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue))
|
|
|
)).toList()).map(certificateData::addViolationObservations)
|
|
|
- ).compose(certificateData -> Future.succeededFuture(ResourcesUtils.getBytes("no_trajectory.png"))
|
|
|
+ ).compose(certificateData -> Future.succeededFuture(ResourcesUtils.getBytes("sample_trajectory.png"))
|
|
|
.map(certificateData::addTrajectoryImage)
|
|
|
).onSuccess(certificateData -> {
|
|
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
|
@@ -2284,7 +2308,7 @@ public class OpenAPIHandler {
|
|
|
READY, MONITORING, DELIVERED
|
|
|
}
|
|
|
|
|
|
- private record TrackingRecord(long unitId, long orderId, TrackingStatus status, OffsetDateTime trackingStart) {
|
|
|
+ private record TrackingRecord(long unitId, long orderId, TrackingStatus status, String deliveryType, OffsetDateTime trackingStart) {
|
|
|
boolean isTracking() {
|
|
|
return status.equals(TrackingStatus.MONITORING);
|
|
|
}
|
|
|
@@ -2417,7 +2441,7 @@ public class OpenAPIHandler {
|
|
|
"unitId", rec.unitId(),
|
|
|
"orderId", rec.orderId(),
|
|
|
"isDelivering", rec.isTracking(),
|
|
|
- "deliveryType", "SERVICE_FOFR"
|
|
|
+ "deliveryType", rec.deliveryType()
|
|
|
).encode());
|
|
|
|
|
|
Consumer<TrackingRecord> replyConsumer = switch (acceptType) {
|
|
|
@@ -2431,7 +2455,7 @@ public class OpenAPIHandler {
|
|
|
}
|
|
|
|
|
|
MapLogRepository locRepo = (MapLogRepository) repo;
|
|
|
- locRepo.rawPool().preparedQuery("SELECT t.unit_id, t.status, t.order_id, t.time_tracking_start FROM tracking.record t WHERE t.unit_id = $1 AND t.status != 'DELIVERED' LIMIT 1")
|
|
|
+ locRepo.rawPool().preparedQuery("SELECT t.unit_id, t.status, t.order_id, t.time_tracking_start, t.delivery_type FROM tracking.record t WHERE t.unit_id = $1 AND t.status != 'DELIVERED' LIMIT 1")
|
|
|
.execute(Tuple.of(unitId))
|
|
|
.map(RowSet::iterator)
|
|
|
.map(row -> row.hasNext() ? row.next() : null)
|
|
|
@@ -2440,12 +2464,33 @@ public class OpenAPIHandler {
|
|
|
r.getLong("unit_id"),
|
|
|
r.getLong("order_id"),
|
|
|
TrackingStatus.valueOf(r.getString("status")),
|
|
|
+ r.getString("delivery_type"),
|
|
|
r.getOffsetDateTime("time_tracking_start"))
|
|
|
- ).orElse(new TrackingRecord(unitId, -1L, TrackingStatus.DELIVERED, null)))
|
|
|
+ ).orElse(new TrackingRecord(unitId, -1L, TrackingStatus.DELIVERED, null,null)))
|
|
|
.onSuccess(replyConsumer::accept)
|
|
|
.onFailure(rc::fail);
|
|
|
}
|
|
|
|
|
|
+ public void integrationOrderStatusGET(RoutingContext rc) {
|
|
|
+ long orderId = Long.parseLong(rc.queryParam("order_id").get(0));
|
|
|
+ MapLogRepository locRepo = (MapLogRepository) repo;
|
|
|
+ locRepo.rawPool().preparedQuery("SELECT order_id, unit_id, status, time_tracking_start, time_tracking_stop FROM tracking.record WHERE order_id = $1 LIMIT 1")
|
|
|
+ .execute(Tuple.of(orderId))
|
|
|
+ .map(RowSet::iterator)
|
|
|
+ .map(row -> row.hasNext() ? row.next() : null)
|
|
|
+ .map(Optional::ofNullable)
|
|
|
+ .map(p -> p.orElseThrow(() -> new HttpIllegalArgumentException(404, String.format("Order not found: %s", orderId))))
|
|
|
+ .onSuccess(res -> rc.response().putHeader(CONTENT_TYPE, JSON.contentType())
|
|
|
+ .end(JsonObject.of(
|
|
|
+ "orderId", res.getLong("order_id"),
|
|
|
+ "unitId", res.getLong("unit_id"),
|
|
|
+ "status", res.getString("status"),
|
|
|
+ "trackingStart", DATE_TIME_FORMATTER.apply(res.getOffsetDateTime("time_tracking_start")),
|
|
|
+ "trackingStop", DATE_TIME_FORMATTER.apply(res.getOffsetDateTime("time_tracking_stop"))
|
|
|
+ ).encode()))
|
|
|
+ .onFailure(rc::fail);
|
|
|
+ }
|
|
|
+
|
|
|
public void integrationOrderCreateGET(RoutingContext rc) {
|
|
|
long unitId = Long.parseLong(rc.queryParam("unit_id").get(0));
|
|
|
long orderId = Long.parseLong(rc.queryParam("order_id").get(0));
|
|
|
@@ -2454,8 +2499,31 @@ public class OpenAPIHandler {
|
|
|
|
|
|
|
|
|
MapLogRepository locRepo = (MapLogRepository) repo;
|
|
|
- locRepo.rawPool().withTransaction(tr -> tr.preparedQuery("INSERT INTO tracking.record(unit_id, order_id, tracking_id, delivery_type) VALUES ($1, $2, $3, $4) " +
|
|
|
- "RETURNING order_id, unit_id, tracking_id, time_received")
|
|
|
+ locRepo.rawPool().withTransaction(tr -> tr.preparedQuery("WITH change AS (\n" +
|
|
|
+ " INSERT INTO maplog.unit_static_to_mobile (time_last_assignment, static_unit_id, mobile_unit_id)\n" +
|
|
|
+ " SELECT now(), $1, unit_id\n" +
|
|
|
+ " FROM tracking.unit_to_delivery_type\n" +
|
|
|
+ " WHERE delivery_type = $4\n" +
|
|
|
+ " -- AND true/false if a change for static <-> mobile unit comparing to the last assignment\n" +
|
|
|
+ " AND (SELECT COALESCE((SELECT uttm.mobile_unit_id != utdt.unit_id\n" +
|
|
|
+ " FROM maplog.unit_static_to_mobile uttm, tracking.unit_to_delivery_type utdt\n" +
|
|
|
+ " WHERE static_unit_id = $1 AND delivery_type = $4\n" +
|
|
|
+ " ORDER BY time_last_assignment DESC\n" +
|
|
|
+ " LIMIT 1), (SELECT\n" +
|
|
|
+ " (CASE WHEN count(*) = 0 THEN TRUE ELSE FALSE END)\n" +
|
|
|
+ " FROM maplog.unit_static_to_mobile WHERE static_unit_id = 100 LIMIT 1\n" +
|
|
|
+ " )) AS update\n" +
|
|
|
+ " )\n" +
|
|
|
+ " LIMIT 1 RETURNING mobile_unit_id AS unit_id\n" +
|
|
|
+ "), mobile AS (\n" +
|
|
|
+ " select case\n" +
|
|
|
+ " when count(*) > 0 then (SELECT unit_id AS tracking_id FROM tracking.unit_to_delivery_type WHERE delivery_type = $4 LIMIT 1)\n" +
|
|
|
+ " when count(*) = 0 then $3\n" +
|
|
|
+ " end as tracking_id\n" +
|
|
|
+ " from tracking.unit_to_delivery_type WHERE delivery_type = $4 LIMIT 1\n" +
|
|
|
+ ")\n" +
|
|
|
+ "INSERT INTO tracking.record(unit_id, order_id, tracking_id, delivery_type) SELECT $1, $2, mobile.tracking_id, $4 FROM mobile\n" +
|
|
|
+ "RETURNING order_id, unit_id, tracking_id, time_received;")
|
|
|
.execute(Tuple.of(unitId, orderId, trackingId, deliveryType))
|
|
|
.map(RowSet::iterator)
|
|
|
.map(row -> row.hasNext() ? row.next() : null)
|