TimeseriesReferenceService.java
package de.dlr.shepard.context.references.timeseriesreference.services;
import de.dlr.shepard.auth.permission.services.PermissionsService;
import de.dlr.shepard.auth.users.entities.User;
import de.dlr.shepard.auth.users.services.UserService;
import de.dlr.shepard.common.exceptions.InvalidAuthException;
import de.dlr.shepard.common.exceptions.InvalidPathException;
import de.dlr.shepard.common.exceptions.InvalidRequestException;
import de.dlr.shepard.common.util.DateHelper;
import de.dlr.shepard.context.collection.entities.DataObject;
import de.dlr.shepard.context.collection.services.CollectionService;
import de.dlr.shepard.context.collection.services.DataObjectService;
import de.dlr.shepard.context.references.IReferenceService;
import de.dlr.shepard.context.references.timeseriesreference.daos.ReferencedTimeseriesNodeEntityDAO;
import de.dlr.shepard.context.references.timeseriesreference.daos.TimeseriesReferenceDAO;
import de.dlr.shepard.context.references.timeseriesreference.io.TimeseriesReferenceIO;
import de.dlr.shepard.context.references.timeseriesreference.model.ReferencedTimeseriesNodeEntity;
import de.dlr.shepard.context.references.timeseriesreference.model.TimeseriesReference;
import de.dlr.shepard.context.version.services.VersionService;
import de.dlr.shepard.data.timeseries.io.TimeseriesWithDataPoints;
import de.dlr.shepard.data.timeseries.model.Timeseries;
import de.dlr.shepard.data.timeseries.model.TimeseriesContainer;
import de.dlr.shepard.data.timeseries.model.TimeseriesDataPointsQueryParams;
import de.dlr.shepard.data.timeseries.model.enums.AggregateFunction;
import de.dlr.shepard.data.timeseries.model.enums.FillOption;
import de.dlr.shepard.data.timeseries.services.TimeseriesContainerService;
import de.dlr.shepard.data.timeseries.services.TimeseriesCsvService;
import de.dlr.shepard.data.timeseries.services.TimeseriesService;
import de.dlr.shepard.data.timeseries.utilities.TimeseriesValidator;
import io.quarkus.logging.Log;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@RequestScoped
public class TimeseriesReferenceService implements IReferenceService<TimeseriesReference, TimeseriesReferenceIO> {
@Inject
TimeseriesReferenceDAO timeseriesReferenceDAO;
@Inject
TimeseriesService timeseriesService;
@Inject
TimeseriesCsvService timeseriesCsvService;
@Inject
DataObjectService dataObjectService;
@Inject
ReferencedTimeseriesNodeEntityDAO timeseriesDAO;
@Inject
UserService userService;
@Inject
CollectionService collectionService;
@Inject
TimeseriesContainerService timeseriesContainerService;
@Inject
VersionService versionService;
@Inject
DateHelper dateHelper;
@Inject
PermissionsService permissionsService;
/**
* Gets TimeseriesReference list for a given dataobject.
*
* @param collectionShepardId
* @param dataObjectShepardId
* @param versionUID the version UUID
* @return List<TimeseriesReference>
* @throws InvalidPathException If collection or dataobject cannot be found, or no association between dataobject and collection exists
* @throws InvalidAuthException If user has no read permissions on collection or dataobject specified by request path
*/
@Override
public List<TimeseriesReference> getAllReferencesByDataObjectId(
long collectionShepardId,
long dataObjectShepardId,
UUID versionUID
) {
dataObjectService.getDataObject(collectionShepardId, dataObjectShepardId, versionUID);
var references = timeseriesReferenceDAO.findByDataObjectShepardId(dataObjectShepardId);
return references;
}
/**
* Gets TimeseriesReference by shepard id.
*
* @param collectionShepardId
* @param dataObjectShepardId
* @param shepardId
* @param versionUID the version UUID
* @return TimeseriesReference
* @throws InvalidPathException If reference with Id does not exist or is deleted, or if collection or dataObject Id of path is not valid
* @throws InvalidAuthException If user has no read permissions on collection or dataobject specified by request path
*/
@Override
public TimeseriesReference getReference(
long collectionShepardId,
long dataObjectShepardId,
long shepardId,
UUID versionUID
) {
dataObjectService.getDataObject(collectionShepardId, dataObjectShepardId, versionUID);
TimeseriesReference reference = timeseriesReferenceDAO.findByShepardId(shepardId, versionUID);
if (reference == null || reference.isDeleted()) {
String errorMsg = String.format("ID ERROR - Timeseries Reference with id %s is null or deleted", shepardId);
Log.error(errorMsg);
throw new InvalidPathException(errorMsg);
}
if (reference.getDataObject() == null || !reference.getDataObject().getShepardId().equals(dataObjectShepardId)) {
String errorMsg = "ID ERROR - There is no association between dataObject and reference";
Log.error(errorMsg);
throw new InvalidPathException(errorMsg);
}
return reference;
}
/**
* Creates a new TimeseriesReference reference
*
* @param collectionShepardId
* @param dataObjectShepardId DataObject id for the reference to be created
* @param timeseriesReference Reference object
* @return TimeseriesReference
* @throws InvalidPathException if collection or dataobject specified by their Ids are null or deleted
* @throws InvalidAuthException if user has no permission to edit referencing collection or no read permissions on referenced container
* @throws InvalidRequestException if user provides a timeseries reference with a non-accessible container
*/
@Override
public TimeseriesReference createReference(
long collectionShepardId,
long dataObjectShepardId,
TimeseriesReferenceIO timeseriesReference
) {
DataObject dataObject = dataObjectService.getDataObject(collectionShepardId, dataObjectShepardId);
collectionService.assertIsAllowedToEditCollection(collectionShepardId);
User user = userService.getCurrentUser();
TimeseriesContainer container;
try {
container = timeseriesContainerService.getContainer(timeseriesReference.getTimeseriesContainerId());
} catch (InvalidPathException ex) {
Log.error(ex.getMessage());
throw new InvalidRequestException(ex.getMessage());
}
// sanitize timeseries
timeseriesReference
.getTimeseries()
.forEach(timeseries -> TimeseriesValidator.assertTimeseriesPropertiesAreValid(timeseries));
var toCreate = new TimeseriesReference();
toCreate.setCreatedAt(dateHelper.getDate());
toCreate.setCreatedBy(user);
toCreate.setDataObject(dataObject);
toCreate.setName(timeseriesReference.getName());
toCreate.setStart(timeseriesReference.getStart());
toCreate.setEnd(timeseriesReference.getEnd());
toCreate.setTimeseriesContainer(container);
for (var ts : timeseriesReference.getTimeseries()) {
var found = timeseriesDAO.find(
ts.getMeasurement(),
ts.getDevice(),
ts.getLocation(),
ts.getSymbolicName(),
ts.getField()
);
if (found != null) {
toCreate.addTimeseries(found);
} else {
toCreate.addTimeseries(new ReferencedTimeseriesNodeEntity(ts));
}
}
TimeseriesReference created = timeseriesReferenceDAO.createOrUpdate(toCreate);
created.setShepardId(created.getId());
created = timeseriesReferenceDAO.createOrUpdate(created);
versionService.attachToVersionOfVersionableEntityAndReturnVersion(dataObject.getId(), created.getId());
return created;
}
/**
* Deletes the Timeseries reference.
*
* @param collectionShepardId
* @param dataObjectShepardId
* @param timeseriesReferenceShepardId
* @throws InvalidPathException if collection or dataobject specified by their Ids are null or deleted
* @throws InvalidAuthException if user has no permissions to edit the collection, which the reference is assigned to
*/
@Override
public void deleteReference(long collectionShepardId, long dataObjectShepardId, long timeseriesReferenceShepardId) {
TimeseriesReference timeseriesReference = getReference(
collectionShepardId,
dataObjectShepardId,
timeseriesReferenceShepardId,
null
);
collectionService.assertIsAllowedToEditCollection(collectionShepardId);
User user = userService.getCurrentUser();
timeseriesReference.setDeleted(true);
timeseriesReference.setUpdatedAt(dateHelper.getDate());
timeseriesReference.setUpdatedBy(user);
timeseriesReferenceDAO.createOrUpdate(timeseriesReference);
}
public List<TimeseriesWithDataPoints> getReferencedTimeseriesWithDataPointsList(
long collectionShepardId,
long dataObjectShepardId,
long timeseriesShepardId,
AggregateFunction function,
Long timeSliceNanoseconds,
FillOption fillOption,
Set<String> devicesFilterSet,
Set<String> locationsFilterSet,
Set<String> symbolicNameFilterSet
) {
TimeseriesReference reference = getReference(collectionShepardId, dataObjectShepardId, timeseriesShepardId, null);
if (reference.getTimeseriesContainer() == null || reference.getTimeseriesContainer().isDeleted()) {
String errorMsg = String.format(
"Referenced Timeseries Container from reference with id %s is null or has been deleted",
timeseriesShepardId
);
Log.error(errorMsg);
throw new NotFoundException(errorMsg);
}
try {
// check that referenced container is actually accessible
timeseriesContainerService.getContainer(reference.getTimeseriesContainer().getId());
} catch (InvalidPathException ex) {
throw new NotFoundException(ex.getMessage());
}
var timeseriesList = reference.getReferencedTimeseriesList().stream().map(ts -> ts.toTimeseries()).toList();
var filteredTimeseriesList = timeseriesList
.stream()
.filter(timeseries -> matchFilter(timeseries, devicesFilterSet, locationsFilterSet, symbolicNameFilterSet))
.toList();
var containerId = reference.getTimeseriesContainer().getId();
TimeseriesDataPointsQueryParams queryParams = new TimeseriesDataPointsQueryParams(
reference.getStart(),
reference.getEnd(),
timeSliceNanoseconds,
fillOption,
function
);
return timeseriesService.getManyTimeseriesWithDataPoints(containerId, filteredTimeseriesList, queryParams);
}
public InputStream exportReferencedTimeseriesByShepardId(
long collectionShepardId,
long dataObjectShepardId,
long timeseriesShepardId,
AggregateFunction function,
Long timeSliceNanoseconds,
FillOption fillOption,
Set<String> devicesFilterSet,
Set<String> locationsFilterSet,
Set<String> symbolicNameFilterSet
) throws IOException {
TimeseriesReference reference = getReference(collectionShepardId, dataObjectShepardId, timeseriesShepardId, null);
if (reference.getTimeseriesContainer() == null || reference.getTimeseriesContainer().isDeleted()) {
String errorMsg = String.format(
"The referenced TimeseriesContainer is null or deleted for Reference with id %s",
timeseriesShepardId
);
Log.error(errorMsg);
throw new NotFoundException(errorMsg);
}
try {
timeseriesContainerService.getContainer(reference.getTimeseriesContainer().getId());
} catch (InvalidPathException ex) {
throw new InvalidRequestException(ex.getMessage());
}
var timeseriesList = reference.getReferencedTimeseriesList().stream().map(ts -> ts.toTimeseries()).toList();
var filteredTimeseriesList = timeseriesList
.stream()
.filter(timeseries -> matchFilter(timeseries, devicesFilterSet, locationsFilterSet, symbolicNameFilterSet))
.toList();
var containerId = reference.getTimeseriesContainer().getId();
TimeseriesDataPointsQueryParams queryParams = new TimeseriesDataPointsQueryParams(
reference.getStart(),
reference.getEnd(),
timeSliceNanoseconds,
fillOption,
function
);
return timeseriesCsvService.exportManyTimeseriesWithDataPointsToCsv(
containerId,
filteredTimeseriesList,
queryParams
);
}
public InputStream exportReferencedTimeseriesByShepardId(
long collectionShepardId,
long dataObjectShepardId,
long referenceId
) throws IOException {
return exportReferencedTimeseriesByShepardId(
collectionShepardId,
dataObjectShepardId,
referenceId,
null,
null,
null,
Collections.emptySet(),
Collections.emptySet(),
Collections.emptySet()
);
}
private boolean matchFilter(Timeseries timeseries, Set<String> device, Set<String> location, Set<String> symName) {
var deviceMatches = true;
var locationMatches = true;
var symbolicNameMatches = true;
if (!device.isEmpty()) {
deviceMatches = device.contains(timeseries.getDevice());
}
if (!location.isEmpty()) {
locationMatches = location.contains(timeseries.getLocation());
}
if (!symName.isEmpty()) {
symbolicNameMatches = symName.contains(timeseries.getSymbolicName());
}
return deviceMatches && locationMatches && symbolicNameMatches;
}
}