Commit cb9150c7 authored by LAVENIER's avatar LAVENIER
Browse files

Merge branch 'release/1.6.0'

parents 7302b2b7 578a3979
......@@ -3,7 +3,7 @@
<groupId>net.sumaris</groupId>
<artifactId>sumaris-pod</artifactId>
<version>1.5.6</version>
<version>1.6.0</version>
<packaging>pom</packaging>
<name>SUMARiS</name>
<description>SUMARiS :: Maven parent</description>
......@@ -102,7 +102,7 @@
<elasticsearch.version>6.2.2</elasticsearch.version>
<jna.version>4.5.2</jna.version>
<tyrus.version>1.15</tyrus.version>
<jackson.version>2.10.2</jackson.version><!-- /!\ 2.9.8 import kotlin error -->
<jackson.version>2.11.3</jackson.version><!-- /!\ 2.9.8 import kotlin error -->
<stringtemplate.version>4.0.2</stringtemplate.version>
<jTextUtilsVersion>0.3.3</jTextUtilsVersion>
<commonBeanutilsVersion>1.9.4</commonBeanutilsVersion>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>net.sumaris</groupId>
<artifactId>sumaris-pod</artifactId>
<version>1.5.6</version>
<version>1.6.0</version>
</parent>
<artifactId>sumaris-core-extraction</artifactId>
......
......@@ -3,7 +3,7 @@
<parent>
<artifactId>sumaris-pod</artifactId>
<groupId>net.sumaris</groupId>
<version>1.5.6</version>
<version>1.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -3,7 +3,7 @@
<parent>
<artifactId>sumaris-pod</artifactId>
<groupId>net.sumaris</groupId>
<version>1.5.6</version>
<version>1.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>net.sumaris</groupId>
<artifactId>sumaris-pod</artifactId>
<version>1.5.6</version>
<version>1.6.0</version>
</parent>
<artifactId>sumaris-core-shared</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>net.sumaris</groupId>
<artifactId>sumaris-pod</artifactId>
<version>1.5.6</version>
<version>1.6.0</version>
</parent>
<artifactId>sumaris-core</artifactId>
......
......@@ -30,6 +30,8 @@ import java.util.List;
public interface VesselPositionDao {
List<VesselPositionVO> getAllByOperationId(int operationId);
List<VesselPositionVO> getAllByOperationId(int operationId, int offset, int size, String sortAttribute, SortDirection sortDirection);
VesselPositionVO get(int id);
......
......@@ -60,6 +60,11 @@ public class VesselPositionDaoImpl extends HibernateDaoSupport implements Vessel
@Autowired
private ReferentialDao referentialDao;
@Override
public List<VesselPositionVO> getAllByOperationId(int operationId) {
return getAllByOperationId(operationId, 0, 1000, VesselPositionVO.Fields.DATE_TIME, SortDirection.ASC);
}
@Override
@SuppressWarnings("unchecked")
public List<VesselPositionVO> getAllByOperationId(int operationId, int offset, int size, String sortAttribute, SortDirection sortDirection) {
......
......@@ -22,6 +22,7 @@ package net.sumaris.core.dao.data.operation;
* #L%
*/
import net.sumaris.core.dao.data.VesselPositionDao;
import net.sumaris.core.dao.data.batch.BatchRepository;
import net.sumaris.core.dao.data.DataRepositoryImpl;
import net.sumaris.core.dao.data.MeasurementDao;
......@@ -87,41 +88,14 @@ public class OperationRepositoryImpl
@Autowired
private BatchRepository batchRepository;
@Autowired
protected VesselPositionDao vesselPositionDao;
protected OperationRepositoryImpl(EntityManager entityManager) {
super(Operation.class, OperationVO.class, entityManager);
setLockForUpdate(true);
}
@Override
public List<OperationVO> findAllVO(Specification<Operation> spec, DataFetchOptions fetchOptions) {
// Standard load
if (!fetchOptions.isWithMeasurementValues()) {
return super.findAllVO(spec, fetchOptions);
}
List<OperationVO> result = super.findAllVO(spec, DataFetchOptions.builder()
.withChildrenEntities(fetchOptions.isWithChildrenEntities())
.withMeasurementValues(false) // Load just later
.withRecorderDepartment(fetchOptions.isWithRecorderDepartment())
.withRecorderPerson(fetchOptions.isWithRecorderPerson())
.build());
// Load measurement in an optimize way
Collection<Integer> ids = Beans.collectIds(result);
Map<Integer, Map<Integer, String>> vum = measurementDao.getOperationsVesselUseMeasurementsMap(ids);
Map<Integer, Map<Integer, String>> gum = measurementDao.getOperationsGearUseMeasurementsMap(ids);
// Apply to operations
result.forEach(o -> {
int id = o.getId();
o.setMeasurementValues(vum.get(id));
o.setGearMeasurementValues(gum.get(id));
});
return result;
}
@Override
public void toVO(Operation source, OperationVO target, DataFetchOptions fetchOptions, boolean copyIfNull) {
super.toVO(source, target, fetchOptions, copyIfNull);
......@@ -149,6 +123,9 @@ public class OperationRepositoryImpl
Integer operationId = source.getId();
if (fetchOptions != null && fetchOptions.isWithChildrenEntities() && operationId != null) {
// Positions
target.setPositions(vesselPositionDao.getAllByOperationId(operationId));
// Fishing Areas
target.setFishingAreas(fishingAreaRepository.findAllVO(fishingAreaRepository.hasOperationId(operationId)));
......@@ -167,8 +144,6 @@ public class OperationRepositoryImpl
.withRecorderDepartment(false)
.withMeasurementValues(true)
.build()));
}
// Measurements
......
......@@ -28,9 +28,11 @@ import net.sumaris.core.vo.data.DataFetchOptions;
import net.sumaris.core.vo.data.TripSaveOptions;
import net.sumaris.core.vo.data.TripVO;
import net.sumaris.core.vo.filter.TripFilterVO;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* @author BLA
......@@ -70,9 +72,13 @@ public interface TripService {
List<TripVO> save(List<TripVO> trips, TripSaveOptions saveOptions);
void asyncDelete(int id);
@Transactional(timeout = -1)
@Async
CompletableFuture<Boolean> asyncDelete(int id);
void asyncDelete(List<Integer> ids);
@Transactional(timeout = -1)
@Async
CompletableFuture<Boolean> asyncDelete(List<Integer> ids);
@Transactional(timeout = -1)
void delete(List<Integer> ids);
......
......@@ -57,9 +57,12 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Service("tripService")
......@@ -106,11 +109,8 @@ public class TripServiceImpl implements TripService {
@Autowired
private FishingAreaService fishingAreaService;
@Autowired(required = false)
private TaskExecutor taskExecutor;
@Autowired
private TripService self;
private VesselService vesselService;
@Override
public List<TripVO> getAllTrips(int offset, int size) {
......@@ -145,6 +145,7 @@ public class TripServiceImpl implements TripService {
// Fetch children (disabled by default)
if (fetchOptions.isWithChildrenEntities()) {
target.setVesselSnapshot(vesselService.getSnapshotByIdAndDate(target.getVesselSnapshot().getId(), target.getDepartureDateTime()));
target.setGears(physicalGearService.getAllByTripId(id, fetchOptions));
target.setSales(saleService.getAllByTripId(id, fetchOptions));
......@@ -477,7 +478,7 @@ public class TripServiceImpl implements TripService {
log.info("Delete Trip#{} {trash: {}}", id, enableTrash);
TripVO eventData = enableTrash ?
get(id, DataFetchOptions.builder().withChildrenEntities(true).build()) :
get(id, DataFetchOptions.FULL_GRAPH) :
null;
// Remove link LANDING->TRIP
......@@ -494,25 +495,6 @@ public class TripServiceImpl implements TripService {
publisher.publishEvent(new EntityDeleteEvent(id, Trip.class.getSimpleName(), eventData));
}
@Override
public void asyncDelete(int id) {
if (taskExecutor == null) {
delete(id);
} else {
// Delete async
taskExecutor.execute(() -> {
try {
Thread.sleep(2000); // Wait 2 s
// Call self, to be sure to have a transaction
self.delete(id);
} catch (Exception e) {
log.warn(String.format("Error while deleting trip {id: %s}: %s", id, e.getMessage()), e);
}
});
}
}
@Override
public void delete(List<Integer> ids) {
......@@ -523,21 +505,26 @@ public class TripServiceImpl implements TripService {
}
@Override
public void asyncDelete(List<Integer> ids) {
if (taskExecutor == null) {
delete(ids);
} else {
// Delete async
taskExecutor.execute(() -> {
try {
Thread.sleep(2000); // Wait 2 s
// Call self, to be sure to have a transaction
self.delete(ids);
} catch (Exception e) {
log.warn(String.format("Error while deleting trip {ids: %s}: %s", ids, e.getMessage()), e);
}
});
public CompletableFuture<Boolean> asyncDelete(int id) {
try {
// Call self, to be sure to have a transaction
this.delete(id);
return CompletableFuture.completedFuture(Boolean.TRUE);
} catch (Exception e) {
log.warn(String.format("Error while deleting trip {id: %s}: %s", id, e.getMessage()), e);
return CompletableFuture.completedFuture(Boolean.FALSE);
}
}
@Override
public CompletableFuture<Boolean> asyncDelete(List<Integer> ids) {
try {
// Call self, to be sure to have a transaction
this.delete(ids);
return CompletableFuture.completedFuture(Boolean.TRUE);
} catch (Exception e) {
log.warn(String.format("Error while deleting trip {ids: %s}: %s", ids, e.getMessage()), e);
return CompletableFuture.completedFuture(Boolean.FALSE);
}
}
......
......@@ -51,4 +51,6 @@ public class DataFetchOptions implements IDataFetchOptions {
@Builder.Default
private boolean withMeasurementValues = false;
}
......@@ -69,6 +69,7 @@ sumaris.error.person.notFound=
sumaris.error.read.file=
sumaris.error.server.internal=Internal server error\: %s
sumaris.error.timeout=Connection time out
sumaris.error.trash.notfound=%s\#%s not found in the trash
sumaris.error.validation.required=
sumaris.error.write.file=
sumaris.model.person.department=Organization
......
......@@ -63,6 +63,7 @@ sumaris.error.person.register.duplicatedPerson=Un utilisateur avec le même emai
sumaris.error.read.file=Erreur de lecture du fichier [%s] \: %s
sumaris.error.server.internal=Erreur renvoyée par le serveur \: %s
sumaris.error.timeout=Délai de réponse du serveur dépassé.
sumaris.error.trash.notfound=%s\#%s non trouvé dans la corbeille
sumaris.error.validation.required=Le champ {%s} est requis
sumaris.error.write.file=Erreur d'écriture du fichier [%s] \: %s
sumaris.model.person.department=Organisme
......
......@@ -31,6 +31,7 @@ import net.sumaris.core.vo.data.OperationVO;
import net.sumaris.core.vo.data.PhysicalGearVO;
import net.sumaris.core.vo.data.TripVO;
import net.sumaris.core.vo.data.batch.BatchVO;
import net.sumaris.core.vo.data.sample.SampleVO;
import net.sumaris.core.vo.filter.TripFilterVO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
......@@ -43,6 +44,7 @@ import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
public class TripServiceReadTest extends AbstractServiceTest{
......@@ -118,11 +120,13 @@ public class TripServiceReadTest extends AbstractServiceTest{
@Test
public void getWithChildren() {
public void getFullGraph() {
Integer id = fixtures.getTripId(0);
TripVO trip = service.get(id, DataFetchOptions.FULL_GRAPH);
Assert.assertNotNull(trip);
Assert.assertNotNull(trip.getVesselSnapshot());
Assert.assertNotNull(trip.getVesselSnapshot().getExteriorMarking());
// PhysicalGear
{
......@@ -141,8 +145,21 @@ public class TripServiceReadTest extends AbstractServiceTest{
{
Assert.assertTrue(CollectionUtils.isNotEmpty(trip.getOperations()));
// Check positions
{
Assert.assertTrue(trip.getOperations()
.stream().map(OperationVO::getPositions).anyMatch(CollectionUtils::isNotEmpty));
}
/* // Check samples
// Measurements
{
Assert.assertTrue(trip.getOperations()
.stream()
.flatMap(o -> Stream.concat(Beans.getStream(o.getMeasurements()), Beans.getStream(o.getGearMeasurements())))
.findAny()
.isPresent());
}
// Check samples
{
Assert.assertTrue(trip.getOperations()
.stream().map(OperationVO::getSamples).anyMatch(CollectionUtils::isNotEmpty));
......@@ -161,7 +178,7 @@ public class TripServiceReadTest extends AbstractServiceTest{
.flatMap(Beans::getStream)
.map(SampleVO::getParentId)
.anyMatch(Objects::nonNull));
}*/
}
// Check batches
{
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>net.sumaris</groupId>
<artifactId>sumaris-pod</artifactId>
<version>1.5.6</version>
<version>1.6.0</version>
</parent>
<artifactId>sumaris-server</artifactId>
......
......@@ -35,11 +35,17 @@ spring.datasource.password=
spring.datasource.url=@jdbc.url@
spring.datasource.hikari.connectionTestQuery=SELECT 1 FROM STATUS WHERE ID=1
spring.datasource.hikari.initializationFailTimeout=-1
# JPA configuration
spring.jpa.database-platform=net.sumaris.core.dao.technical.hibernate.spatial.HSQLSpatialDialect
spring.jpa.properties.query.timeout=30000
spring.jpa.properties.javax.persistence.query.timeout=30000
spring.jpa.properties.org.hibernate.timeout=${spring.jpa.properties.javax.persistence.query.timeout}
spring.jpa.properties.hibernate.timeout=${spring.jpa.properties.javax.persistence.query.timeout}
spring.jpa.properties.hibernate.dialect=${spring.jpa.database-platform}
spring.jpa.properties.hibernate.hbm2ddl.auto=none
# Liquibase
spring.jpa.properties.hibernate.hbm2ddl.auto=none
spring.liquibase.enabled=true
# Local SMTP server
......
......@@ -194,6 +194,7 @@ public class Application extends SpringBootServletInitializer {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(4);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("default_task_executor_thread");
executor.initialize();
return executor;
......
......@@ -118,7 +118,6 @@ public class GraphQLConfiguration implements WebSocketConfigurer {
// Data
.withOperationsFromSingleton(dataService, DataGraphQLService.class)
// Social
.withOperationsFromSingleton(socialService, SocialGraphQLService.class)
......
......@@ -24,11 +24,16 @@ package net.sumaris.server.http.graphql.technical;
import com.google.common.collect.ImmutableList;
import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLEnvironment;
import io.leangen.graphql.annotations.GraphQLMutation;
import io.leangen.graphql.annotations.GraphQLQuery;
import net.sumaris.core.dao.technical.Pageables;
import net.sumaris.core.dao.technical.SortDirection;
import net.sumaris.core.dao.technical.model.IUpdateDateEntityBean;
import net.sumaris.core.vo.data.TripSaveOptions;
import net.sumaris.core.vo.data.TripVO;
import net.sumaris.server.http.security.IsAdmin;
import net.sumaris.server.http.security.IsUser;
import net.sumaris.server.service.technical.TrashService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
......@@ -39,6 +44,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Set;
@Service
@Transactional
......@@ -51,9 +57,9 @@ public class TrashGraphQLService {
@Autowired
private TrashService service;
@GraphQLQuery(name = "trash", description = "Get trash content")
@GraphQLQuery(name = "trashEntities", description = "Get trash content")
@IsAdmin
public List<String> getTrashContent(
public List<String> findTrashByPage(
@GraphQLArgument(name = "entityName") String entityName,
@GraphQLArgument(name = "offset", defaultValue = "0") Integer offset,
@GraphQLArgument(name = "size", defaultValue = "1000") Integer size,
......@@ -66,5 +72,20 @@ public class TrashGraphQLService {
return page.hasContent() ? page.getContent() : ImmutableList.of();
}
// TODO: add delete from trash
@GraphQLQuery(name = "trashEntity", description = "Get trash file content")
@IsAdmin
public String getTrashContent(
@GraphQLArgument(name = "entityName") String entityName,
@GraphQLArgument(name = "id") String id
) {
return service.getById(entityName, id, String.class);
}
@GraphQLMutation(name = "deleteTrashEntity", description = "Delete an entity from the trash")
@IsAdmin
public void saveTrip(@GraphQLArgument(name = "entityName") String entityName,
@GraphQLArgument(name = "id") String id) {
service.delete(entityName, id);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment