Commit 07be37a0 authored by LAVENIER's avatar LAVENIER
Browse files

[fix] VesselSnapshot: reset time on date param, to reuse cached value

[fix] MeasurementDaoImpl: add an error message when value not corresponding to the pmfm type
[fix] Auth: when pod is starting, return a graphql error
parent 54740556
/*
* #%L
* SUMARiS
* %%
* Copyright (C) 2019 SUMARiS Consortium
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
package net.sumaris.core.event.config;
public interface ConfigurationEventListener {
default void onReady(ConfigurationReadyEvent event) {
}
default void onUpdated(ConfigurationUpdatedEvent event) {
}
}
\ No newline at end of file
......@@ -985,7 +985,13 @@ public class MeasurementDaoImpl extends HibernateDaoSupport implements Measureme
break;
case QUALITATIVE_VALUE:
// If find a object structure (e.g. ReferentialVO), try to find the id
target.setQualitativeValue(getReference(QualitativeValue.class, Integer.parseInt(value)));
try {
target.setQualitativeValue(getReference(QualitativeValue.class, Integer.parseInt(value)));
}
catch(NumberFormatException e) {
throw new SumarisTechnicalException(String.format("Invalid value for pmfm with id=%s. Expected an integer (to link with a QualitativeValue.id), but got: '%s'. Please fix value, or change the Pmfm type to alphanumerical",
pmfmId, value));
}
break;
case STRING:
target.setAlphanumericalValue(value);
......
......@@ -35,6 +35,7 @@ import net.sumaris.core.event.config.ConfigurationUpdatedEvent;
import net.sumaris.core.event.entity.EntityDeleteEvent;
import net.sumaris.core.event.entity.EntityInsertEvent;
import net.sumaris.core.event.entity.EntityUpdateEvent;
import net.sumaris.core.model.data.IMeasurementEntity;
import net.sumaris.core.model.data.Landing;
import net.sumaris.core.model.data.LandingMeasurement;
import net.sumaris.core.model.data.Trip;
......@@ -266,6 +267,8 @@ public class LandingServiceImpl implements LandingService {
trip.setLandingId(source.getId());
trip.setLanding(null);
fillDefaultProperties(source, trip);
TripVO savedTrip = tripService.save(source.getTrip(), TripSaveOptions.builder()
.withLanding(false)
.withOperation(false)
......@@ -301,9 +304,7 @@ public class LandingServiceImpl implements LandingService {
if (sample == null) return;
// Copy recorder department from the parent
if (sample.getRecorderDepartment() == null || sample.getRecorderDepartment().getId() == null) {
sample.setRecorderDepartment(parent.getRecorderDepartment());
}
DataBeans.setDefaultRecorderDepartment(sample, parent.getRecorderDepartment());
// Fill matrix
if (sample.getMatrix() == null || sample.getMatrix().getId() == null) {
......@@ -320,6 +321,13 @@ public class LandingServiceImpl implements LandingService {
sample.setLandingId(parent.getId());
}
protected void fillDefaultProperties(LandingVO parent, TripVO trip) {
if (trip == null) return;
DataBeans.setDefaultRecorderDepartment(trip, parent.getRecorderDepartment());
DataBeans.setDefaultRecorderPerson(trip, parent.getRecorderPerson());
}
/**
* Get all samples, in the sample tree parent/children
*
......
......@@ -133,7 +133,7 @@ public class TripServiceImpl implements TripService {
// Fetch children (disabled by default)
if (fetchOptions.isWithChildrenEntities()) {
target.setVesselSnapshot(vesselService.getSnapshotByIdAndDate(target.getVesselSnapshot().getId(), target.getDepartureDateTime()));
target.setVesselSnapshot(vesselService.getSnapshotByIdAndDate(target.getVesselSnapshot().getId(), Dates.resetTime(target.getDepartureDateTime())));
target.setGears(physicalGearService.getAllByTripId(id, fetchOptions));
target.setSales(saleService.getAllByTripId(id, fetchOptions));
target.setExpectedSales(expectedSaleService.getAllByTripId(id));
......
......@@ -22,17 +22,29 @@ package net.sumaris.core.service.technical;
* #L%
*/
import com.google.common.base.Function;
import net.sumaris.core.config.SumarisConfiguration;
import net.sumaris.core.event.config.ConfigurationEventListener;
import net.sumaris.core.event.config.ConfigurationReadyEvent;
import net.sumaris.core.vo.technical.SoftwareVO;
import org.springframework.transaction.annotation.Transactional;
@Transactional(readOnly = true)
public interface ConfigurationService {
SumarisConfiguration getConfiguration();
SoftwareVO getCurrentSoftware();
boolean isReady();
void applySoftwareProperties();
void addListener(ConfigurationEventListener listener);
void removeListener(ConfigurationEventListener listener);
}
......@@ -22,14 +22,17 @@ package net.sumaris.core.service.technical;
* #L%
*/
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import net.sumaris.core.config.SumarisConfiguration;
import net.sumaris.core.config.SumarisConfigurationOption;
import net.sumaris.core.dao.technical.model.IEntity;
import net.sumaris.core.dao.technical.model.annotation.EntityEnum;
import net.sumaris.core.dao.technical.model.annotation.EntityEnums;
import net.sumaris.core.event.config.ConfigurationEventListener;
import net.sumaris.core.event.config.ConfigurationReadyEvent;
import net.sumaris.core.event.config.ConfigurationUpdatedEvent;
import net.sumaris.core.event.entity.AbstractEntityEvent;
......@@ -76,7 +79,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
private final SumarisConfiguration configuration;
private final String currentSoftwareLabel;
private boolean ready = false;
private boolean ready;
@Autowired
private EntityManager entityManager;
......@@ -92,13 +95,22 @@ public class ConfigurationServiceImpl implements ConfigurationService {
private Version dbVersion;
private final List<ConfigurationEventListener> listeners = Lists.newArrayList();
@Autowired
public ConfigurationServiceImpl(SumarisConfiguration configuration) {
this.configuration = configuration;
this.currentSoftwareLabel = configuration.getAppName();
Preconditions.checkNotNull(currentSoftwareLabel);
this.ready = !configuration.enableConfigurationDbPersistence(); // Mark as ready, if configuration not loaded from DB
}
@Override
public SumarisConfiguration getConfiguration() {
return configuration;
}
@Override
public SoftwareVO getCurrentSoftware() {
return softwareService.getByLabel(currentSoftwareLabel);
......@@ -120,7 +132,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
if (!configuration.enableConfigurationDbPersistence()) {
if (event instanceof SchemaReadyEvent && configuration.isProduction()) {
publisher.publishEvent(new ConfigurationReadyEvent(configuration));
publishEvent(new ConfigurationReadyEvent(configuration));
}
}
......@@ -130,11 +142,11 @@ public class ConfigurationServiceImpl implements ConfigurationService {
// Publish ready event
if (event instanceof SchemaReadyEvent) {
publisher.publishEvent(new ConfigurationReadyEvent(configuration));
publishEvent(new ConfigurationReadyEvent(configuration));
}
// Publish update event
else {
publisher.publishEvent(new ConfigurationUpdatedEvent(configuration));
publishEvent(new ConfigurationUpdatedEvent(configuration));
}
}
......@@ -167,7 +179,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
configuration.cleanCache();
// Publish update event
publisher.publishEvent(new ConfigurationUpdatedEvent(configuration));
publishEvent(new ConfigurationUpdatedEvent(configuration));
// Mark as ready
ready = true;
......@@ -191,6 +203,18 @@ public class ConfigurationServiceImpl implements ConfigurationService {
}
}
@Override
public void addListener(ConfigurationEventListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
@Override
public void removeListener(ConfigurationEventListener listener) {
listeners.remove(listener);
}
@Override
public void applySoftwareProperties() {
......@@ -378,6 +402,32 @@ public class ConfigurationServiceImpl implements ConfigurationService {
}
protected void publishEvent(ConfigurationUpdatedEvent event) {
// Emit to Spring event bus
publisher.publishEvent(event);
// Emit to registered listeners
for(ConfigurationEventListener listener: listeners) {
try {
listener.onUpdated(event);
} catch (Throwable t) {
log.error("Error on configuration updated event listener: " + t.getMessage(), t);
}
}
}
protected void publishEvent(ConfigurationReadyEvent event) {
// Emit to Spring event bus
publisher.publishEvent(event);
// Emit to registered listeners
for (ConfigurationEventListener listener: listeners) {
try {
listener.onReady(event);
} catch (Throwable t) {
log.error("Error on configuration ready event listener: " + t.getMessage(), t);
}
}
}
}
......@@ -30,6 +30,7 @@ import net.sumaris.core.model.data.IWithVesselSnapshotEntity;
import net.sumaris.core.model.data.Landing;
import net.sumaris.core.service.AbstractServiceTest;
import net.sumaris.core.service.data.vessel.VesselService;
import net.sumaris.core.util.Dates;
import net.sumaris.core.vo.data.LandingVO;
import net.sumaris.core.vo.data.VesselSnapshotVO;
import net.sumaris.core.vo.filter.LandingFilterVO;
......@@ -95,7 +96,7 @@ public class LandingServiceReadOracleTest extends AbstractServiceTest {
// Add vessel if need
beans.forEach(bean -> {
if (bean.getVesselSnapshot() != null && bean.getVesselSnapshot().getId() != null && bean.getVesselSnapshot().getName() == null) {
bean.setVesselSnapshot(vesselService.getSnapshotByIdAndDate(bean.getVesselSnapshot().getId(), bean.getVesselDateTime()));
bean.setVesselSnapshot(vesselService.getSnapshotByIdAndDate(bean.getVesselSnapshot().getId(), Dates.resetTime(bean.getVesselDateTime())));
}
});
}
......
......@@ -23,21 +23,16 @@ package net.sumaris.server.http.graphql;
*/
import com.fasterxml.jackson.databind.ObjectMapper;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.*;
import graphql.schema.GraphQLSchema;
import lombok.extern.slf4j.Slf4j;
import net.sumaris.core.config.SumarisConfiguration;
import net.sumaris.core.event.config.ConfigurationEvent;
import net.sumaris.core.event.config.ConfigurationReadyEvent;
import net.sumaris.core.event.config.ConfigurationUpdatedEvent;
import net.sumaris.core.exception.SumarisTechnicalException;
import net.sumaris.server.config.SumarisServerConfiguration;
import net.sumaris.core.service.technical.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
......@@ -49,23 +44,18 @@ public class GraphQLRestController {
private final GraphQL graphQL;
private final ObjectMapper objectMapper;
private boolean ready;
private final ConfigurationService configurationService;
@Autowired
public GraphQLRestController(GraphQLSchema graphQLSchema,
ObjectMapper objectMapper,
SumarisConfiguration configuration) {
ConfigurationService configurationService) {
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
this.objectMapper = objectMapper;
this.ready = !configuration.enableConfigurationDbPersistence(); // Mark as ready, if configuration not loaded from DB
this.configurationService = configurationService;
log.info(String.format("Starting GraphQL endpoint {%s}...", GraphQLPaths.BASE_PATH));
}
@EventListener({ConfigurationReadyEvent.class, ConfigurationUpdatedEvent.class})
protected void onConfigurationReady(ConfigurationEvent event) {
this.ready = true;
}
@PostMapping(value = GraphQLPaths.BASE_PATH,
consumes = {
MediaType.APPLICATION_JSON_VALUE,
......@@ -77,17 +67,22 @@ public class GraphQLRestController {
})
@ResponseBody
public Map<String, Object> indexFromAnnotated(@RequestBody Map<String, Object> request, HttpServletRequest rawRequest) {
if (!this.ready) {
throw new SumarisTechnicalException("Pod not ready. Please wait");
ExecutionResult result;
if (!this.configurationService.isReady()) {
result = new ExecutionResultImpl.Builder()
.addError(GraphqlErrorBuilder.newError().message("Pod is starting. Please wait...").build())
.build();
}
ExecutionResult executionResult = graphQL.execute(ExecutionInput.newExecutionInput()
else {
result = graphQL.execute(ExecutionInput.newExecutionInput()
.query((String) request.get("query"))
.operationName((String) request.get("operationName"))
.variables(GraphQLHelper.getVariables(request, objectMapper))
.context(rawRequest)
.build());
}
return GraphQLHelper.processExecutionResult(executionResult);
return GraphQLHelper.processExecutionResult(result);
}
/* -- private methods -- */
......
......@@ -249,7 +249,7 @@ public class VesselGraphQLService {
if (hasVesselFeaturesField(fields)) {
beans.forEach(bean -> {
if (bean.getVesselSnapshot() != null && bean.getVesselSnapshot().getId() != null && bean.getVesselSnapshot().getName() == null) {
bean.setVesselSnapshot(vesselService.getSnapshotByIdAndDate(bean.getVesselSnapshot().getId(), bean.getVesselDateTime()));
bean.setVesselSnapshot(vesselService.getSnapshotByIdAndDate(bean.getVesselSnapshot().getId(), Dates.resetTime(bean.getVesselDateTime())));
}
});
}
......
......@@ -50,13 +50,19 @@ public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter
private static final String TOKEN = "token";
private static final String BASIC = "Basic";
private boolean ready = false;
private boolean enableAuthBasic;
private boolean enableAuthToken;
protected AuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
super(requiresAuthenticationRequestMatcher);
}
public void setReady(boolean ready) {
this.ready = ready;
}
public void setEnableAuthToken(boolean enableAuthToken) {
this.enableAuthToken = enableAuthToken;
}
......@@ -68,6 +74,11 @@ public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// When not ready, always auth as anonymous
if (!this.ready) {
getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(AnonymousUserDetails.TOKEN, AnonymousUserDetails.TOKEN));
}
String authorization = request.getHeader(AUTHORIZATION);
String[] values = StringUtils.isNotBlank(authorization)
? authorization.split(",")
......
......@@ -22,6 +22,9 @@ package net.sumaris.server.http.security;
* #L%
*/
import net.sumaris.core.event.config.ConfigurationEventListener;
import net.sumaris.core.event.config.ConfigurationReadyEvent;
import net.sumaris.core.service.technical.ConfigurationService;
import net.sumaris.server.config.SumarisServerConfiguration;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.BeanInitializationException;
......@@ -73,12 +76,16 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
private final ApplicationContext applicationContext;
private final ConfigurationService configurationService;
private final SumarisServerConfiguration configuration;
public WebSecurityConfig(ApplicationContext applicationContext, SumarisServerConfiguration configuration) {
public WebSecurityConfig(ApplicationContext applicationContext,
ConfigurationService configurationService,
SumarisServerConfiguration configuration) {
super();
this.applicationContext = applicationContext;
this.configurationService = configurationService;
this.configuration = configuration;
}
......@@ -125,8 +132,17 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
filter.setEnableAuthToken(configuration.enableAuthToken());
filter.setEnableAuthBasic(configuration.enableAuthBasic());
filter.setEnableAuthToken(configuration.enableAuthToken());
filter.setReady(configurationService.isReady());
configurationService.addListener(new ConfigurationEventListener() {
@Override
public void onReady(ConfigurationReadyEvent event) {
filter.setEnableAuthBasic(configuration.enableAuthBasic());
filter.setEnableAuthToken(configuration.enableAuthToken());
filter.setReady(true);
}
});
return filter;
}
......
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