package ru.semiot.platform.apigateway.rest;
import static ru.semiot.commons.restapi.AsyncResponseHelper.resume;
import static ru.semiot.commons.restapi.AsyncResponseHelper.resumeOnError;
import com.github.jsonldjava.core.JsonLdError;
import com.github.jsonldjava.utils.JsonUtils;
import org.aeonbits.owner.ConfigFactory;
import org.aeonbits.owner.util.Collections;
import org.apache.commons.lang3.StringUtils;
import org.apache.jena.ext.com.google.common.base.Strings;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ResIterator;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.vocabulary.RDF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.semiot.commons.namespaces.Hydra;
import ru.semiot.commons.namespaces.SSN;
import ru.semiot.commons.rdf.ModelJsonLdUtils;
import ru.semiot.commons.restapi.MediaType;
import ru.semiot.platform.apigateway.ServerConfig;
import ru.semiot.platform.apigateway.beans.TSDBQueryService;
import ru.semiot.platform.apigateway.beans.impl.ContextProvider;
import ru.semiot.platform.apigateway.beans.impl.SPARQLQueryService;
import ru.semiot.platform.apigateway.utils.MapBuilder;
import ru.semiot.platform.apigateway.utils.RDFUtils;
import ru.semiot.platform.apigateway.utils.URIUtils;
import rx.exceptions.Exceptions;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
@Path("/systems/{system_id}/observations")
public class SystemObservationsResource extends AbstractSystemResource {
private static final ServerConfig config = ConfigFactory.create(ServerConfig.class);
private static final Logger logger = LoggerFactory.getLogger(SystemObservationsResource.class);
private static final String QUERY_GET_SYSTEM_SENSORS = "SELECT DISTINCT ?sensor_id {"
+ " ?uri a ssn:System ;"
+ " dcterms:identifier \"${SYSTEM_ID}\"^^xsd:string ;"
+ " ssn:hasSubSystem ?sensor ."
+ " ?sensor dcterms:identifier ?sensor_id }";
private static final String VAR_SENSOR_ID = "sensor_id";
private static final String TOPIC_SENSOR_TEMPLATE = "${SYSTEM_ID}.observations.${SENSOR_ID}";
private static final String TOPIC_SYSTEM_TEMPLATE = "${SYSTEM_ID}.observations";
public SystemObservationsResource() {
super();
}
@Inject
private SPARQLQueryService sparqlQuery;
@Inject
private TSDBQueryService tsdbQuery;
@Inject
private ContextProvider contextProvider;
@Context
UriInfo uriInfo;
@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_LD_JSON})
public void observations(@Suspended final AsyncResponse response,
@PathParam("system_id") String systemId, @QueryParam("sensor_id") String sensorId,
@QueryParam("start") String start, @QueryParam("end") String end)
throws IOException {
if (Strings.isNullOrEmpty(systemId)) {
response.resume(Response.status(Response.Status.NOT_FOUND).build());
}
URI root = uriInfo.getRequestUri();
String rootUrl = URIUtils.extractRootURL(root);
Map params = MapBuilder.newMap()
.put(ContextProvider.VAR_ROOT_URL, rootUrl)
.put(ContextProvider.VAR_WAMP_URL, UriBuilder
.fromUri(rootUrl + config.wampPublicPath())
.scheme(config.wampProtocolScheme()).build())
.put(ContextProvider.VAR_SYSTEM_ID, systemId).build();
List<String> listSensorId;
if (sensorId == null) {
listSensorId = null;
params.put(ContextProvider.VAR_TOPIC_NAME,
TOPIC_SYSTEM_TEMPLATE.replace("${SYSTEM_ID}", systemId));
} else {
listSensorId = Collections.list(sensorId);
params.put(ContextProvider.VAR_TOPIC_NAME, TOPIC_SENSOR_TEMPLATE
.replace("${SYSTEM_ID}", systemId).replace("${SENSOR_ID}", sensorId));
}
if (Strings.isNullOrEmpty(start)) {
tsdbQuery.queryDateTimeOfLatestObservation(
systemId, getListSensorsId(systemId, listSensorId)).subscribe((result) -> {
if (StringUtils.isNotBlank(result)) {
UriBuilder uriBuilder = UriBuilder.fromUri(root).queryParam("start", result);
response.resume(Response.seeOther(uriBuilder.build()).build());
} else {
try {
Map<String, Object> frame = contextProvider.getFrame(
ContextProvider.SYSTEM_OBSERVATIONS_COLLECTION, root);
params.put(ContextProvider.VAR_QUERY_PARAMS, "?noparams");
Model model = contextProvider.getRDFModel(
ContextProvider.SYSTEM_OBSERVATIONS_COLLECTION, params);
Resource view = RDFUtils.subjectWithProperty(
model, RDF.type, Hydra.PartialCollectionView);
model.removeAll(view, null, null);
response.resume(JsonUtils.toPrettyString(ModelJsonLdUtils.toJsonLdCompact(model, frame)));
} catch (Throwable ex) {
response.resume(ex);
}
}
}, resumeOnError(response));
} else {
Map<String, Object> frame = contextProvider.getFrame(
ContextProvider.SYSTEM_OBSERVATIONS_PARTIAL_COLLECTION, root);
String queryParams = "?";
if (listSensorId != null && !listSensorId.isEmpty()) {
queryParams += "sensor_id=" + StringUtils.join(listSensorId, ',') + "&";
}
queryParams += "start=" + start;
if (!Strings.isNullOrEmpty(end)) {
queryParams += "&end=" + end;
}
params.put(ContextProvider.VAR_QUERY_PARAMS, queryParams);
Model model = contextProvider.getRDFModel(
ContextProvider.SYSTEM_OBSERVATIONS_COLLECTION, params);
tsdbQuery.queryObservationsByRange(systemId,
getListSensorsId(systemId, listSensorId), start, end).map((result) -> {
model.add(result);
Resource collection = model.listSubjectsWithProperty(
RDF.type, Hydra.PartialCollectionView).next();
ResIterator iter = model.listSubjectsWithProperty(RDF.type, SSN.Observaton);
while (iter.hasNext()) {
Resource obs = iter.next();
model.add(collection, Hydra.member, obs);
}
try {
return JsonUtils.toPrettyString(ModelJsonLdUtils.toJsonLdCompact(model, frame));
} catch (JsonLdError | IOException ex) {
throw Exceptions.propagate(ex);
}
}).map((json) -> Response.ok(json).build()).subscribe(resume(response));
}
}
private List<String> getListSensorsId(String systemId,
List<String> sensorsId) {
if (sensorsId == null || sensorsId.isEmpty()) {
return rsToList(sparqlQuery.select(QUERY_GET_SYSTEM_SENSORS.replace("${SYSTEM_ID}", systemId))
.toBlocking().single());
} else {
return sensorsId;
}
}
private List<String> rsToList(ResultSet rs) {
List<String> list = new ArrayList<String>();
while (rs.hasNext()) {
QuerySolution qs = rs.next();
list.add(qs.getLiteral(VAR_SENSOR_ID).getString());
}
return list;
}
}