Restore tachographfilesession changes from 2f4ffc2
This commit is contained in:
parent
91c2da02fc
commit
8a106c02c5
|
|
@ -11,6 +11,8 @@ import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDri
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
||||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionProcessingService;
|
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionProcessingService;
|
||||||
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionService;
|
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionService;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowResponse;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
@ -44,10 +46,21 @@ public class TachographFileSessionController {
|
||||||
@RequestParam("file") MultipartFile file,
|
@RequestParam("file") MultipartFile file,
|
||||||
@RequestParam(required = false) String tenantKey,
|
@RequestParam(required = false) String tenantKey,
|
||||||
@RequestParam(required = false) String sourceInstanceKey,
|
@RequestParam(required = false) String sourceInstanceKey,
|
||||||
@RequestParam(required = false) String sessionLabel
|
@RequestParam(required = false) String sessionLabel,
|
||||||
|
HttpSession webSession
|
||||||
) {
|
) {
|
||||||
return ResponseEntity.status(HttpStatus.CREATED)
|
return ResponseEntity.status(HttpStatus.CREATED)
|
||||||
.body(service.createSession(file, tenantKey, sourceInstanceKey, sessionLabel));
|
.body(service.createSession(file, tenantKey, sourceInstanceKey, sessionLabel, webSession));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/workflow/reset")
|
||||||
|
public ResponseEntity<TachographUploadWorkflowResponse> resetUploadWorkflow(HttpSession webSession) {
|
||||||
|
return ResponseEntity.ok(service.resetUploadWorkflow(webSession));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/workflow")
|
||||||
|
public ResponseEntity<TachographUploadWorkflowResponse> getUploadWorkflow(HttpSession webSession) {
|
||||||
|
return ResponseEntity.ok(service.getUploadWorkflow(webSession));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{sessionId}")
|
@GetMapping("/{sessionId}")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
package at.procon.eventhub.tachographfilesession.dto;
|
package at.procon.eventhub.tachographfilesession.dto;
|
||||||
|
|
||||||
public record CreateTachographFileSessionResponse(
|
public record CreateTachographFileSessionResponse(
|
||||||
TachographFileSessionSummaryDto session
|
TachographFileSessionSummaryDto session,
|
||||||
|
TachographUploadWorkflowDto workflow,
|
||||||
|
TachographCompositeSessionSummaryDto compositeSession
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
public CreateTachographFileSessionResponse(TachographFileSessionSummaryDto session) {
|
||||||
|
this(session, null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.dto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record TachographUploadWorkflowDto(
|
||||||
|
boolean driverCardUploaded,
|
||||||
|
UUID driverCardSessionId,
|
||||||
|
List<UUID> uploadedSessionIds,
|
||||||
|
List<UUID> vehicleUnitSessionIds,
|
||||||
|
UUID compositeSessionId
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.dto;
|
||||||
|
|
||||||
|
public record TachographUploadWorkflowResponse(
|
||||||
|
TachographUploadWorkflowDto workflow
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -28,10 +28,8 @@ public class LegalRequirementsClient {
|
||||||
|
|
||||||
public LegalRequirementsUploadResult uploadTachographFile(byte[] fileBytes, String fileName) {
|
public LegalRequirementsUploadResult uploadTachographFile(byte[] fileBytes, String fileName) {
|
||||||
EventHubProperties.LegalRequirements config = properties.getTachographFileSession().getLegalRequirements();
|
EventHubProperties.LegalRequirements config = properties.getTachographFileSession().getLegalRequirements();
|
||||||
HttpClient client = HttpClient.newBuilder()
|
CookieManager cookieManager = newCookieManager();
|
||||||
.connectTimeout(config.getConnectTimeout())
|
HttpClient client = httpClient(config, cookieManager);
|
||||||
.cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ALL))
|
|
||||||
.build();
|
|
||||||
try {
|
try {
|
||||||
String dataPackageId = uploadDataPackage(client, config, fileBytes, fileName);
|
String dataPackageId = uploadDataPackage(client, config, fileBytes, fileName);
|
||||||
String xml = downloadXml(client, config, dataPackageId);
|
String xml = downloadXml(client, config, dataPackageId);
|
||||||
|
|
@ -43,6 +41,23 @@ public class LegalRequirementsClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LegalRequirementsUploadResult uploadTachographFile(CookieManager cookieManager, byte[] fileBytes, String fileName) {
|
||||||
|
EventHubProperties.LegalRequirements config = properties.getTachographFileSession().getLegalRequirements();
|
||||||
|
HttpClient client = httpClient(config, cookieManager);
|
||||||
|
String dataPackageId = uploadDataPackage(client, config, fileBytes, fileName);
|
||||||
|
String xml = downloadXml(client, config, dataPackageId);
|
||||||
|
return new LegalRequirementsUploadResult(dataPackageId, xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetSession(CookieManager cookieManager) {
|
||||||
|
EventHubProperties.LegalRequirements config = properties.getTachographFileSession().getLegalRequirements();
|
||||||
|
resetSessionQuietly(httpClient(config, cookieManager), config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CookieManager newCookieManager() {
|
||||||
|
return new CookieManager(null, CookiePolicy.ACCEPT_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
private String uploadDataPackage(HttpClient client, EventHubProperties.LegalRequirements config, byte[] fileBytes, String fileName) {
|
private String uploadDataPackage(HttpClient client, EventHubProperties.LegalRequirements config, byte[] fileBytes, String fileName) {
|
||||||
try {
|
try {
|
||||||
String payload = objectMapper.createObjectNode()
|
String payload = objectMapper.createObjectNode()
|
||||||
|
|
@ -115,6 +130,13 @@ public class LegalRequirementsClient {
|
||||||
return HttpRequest.newBuilder(URI.create(url)).timeout(timeout);
|
return HttpRequest.newBuilder(URI.create(url)).timeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HttpClient httpClient(EventHubProperties.LegalRequirements config, CookieManager cookieManager) {
|
||||||
|
return HttpClient.newBuilder()
|
||||||
|
.connectTimeout(config.getConnectTimeout())
|
||||||
|
.cookieHandler(cookieManager == null ? newCookieManager() : cookieManager)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private String basicAuth(EventHubProperties.LegalRequirements config) {
|
private String basicAuth(EventHubProperties.LegalRequirements config) {
|
||||||
String user = config.getUsername() == null ? "" : config.getUsername();
|
String user = config.getUsername() == null ? "" : config.getUsername();
|
||||||
String password = config.getPassword() == null ? "" : config.getPassword();
|
String password = config.getPassword() == null ? "" : config.getPassword();
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,20 @@ public class TachographCompositeSessionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateTachographCompositeSessionResponse createCompositeSession(CreateTachographCompositeSessionRequest request) {
|
public CreateTachographCompositeSessionResponse createCompositeSession(CreateTachographCompositeSessionRequest request) {
|
||||||
if (request == null || request.sessionIds() == null || request.sessionIds().isEmpty()) {
|
return new CreateTachographCompositeSessionResponse(
|
||||||
|
upsertCompositeSession(null, request == null ? null : request.sessionIds(), request == null ? null : request.label())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TachographCompositeSessionSummaryDto upsertCompositeSession(
|
||||||
|
UUID compositeSessionId,
|
||||||
|
List<UUID> requestedSessionIds,
|
||||||
|
String label
|
||||||
|
) {
|
||||||
|
if (requestedSessionIds == null || requestedSessionIds.isEmpty()) {
|
||||||
throw new IllegalArgumentException("sessionIds must not be empty.");
|
throw new IllegalArgumentException("sessionIds must not be empty.");
|
||||||
}
|
}
|
||||||
List<UUID> memberSessionIds = request.sessionIds().stream()
|
List<UUID> memberSessionIds = requestedSessionIds.stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.distinct()
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
@ -72,14 +82,20 @@ public class TachographCompositeSessionService {
|
||||||
.filter(value -> value != null && !value.isBlank())
|
.filter(value -> value != null && !value.isBlank())
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse("default");
|
.orElse("default");
|
||||||
|
UUID id = compositeSessionId == null ? UUID.randomUUID() : compositeSessionId;
|
||||||
|
Instant createdAt = compositeSessionId == null
|
||||||
|
? Instant.now()
|
||||||
|
: compositeRepository.find(compositeSessionId)
|
||||||
|
.map(TachographCompositeSession::createdAt)
|
||||||
|
.orElseGet(Instant::now);
|
||||||
TachographCompositeSession compositeSession = compositeRepository.save(new TachographCompositeSession(
|
TachographCompositeSession compositeSession = compositeRepository.save(new TachographCompositeSession(
|
||||||
UUID.randomUUID(),
|
id,
|
||||||
tenantKey,
|
tenantKey,
|
||||||
blankToNull(request.label()),
|
blankToNull(label),
|
||||||
memberSessionIds,
|
memberSessionIds,
|
||||||
Instant.now()
|
createdAt
|
||||||
));
|
));
|
||||||
return new CreateTachographCompositeSessionResponse(toSummary(compositeSession, sessions));
|
return toSummary(compositeSession, sessions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TachographCompositeSessionSummaryDto getCompositeSession(UUID compositeSessionId) {
|
public TachographCompositeSessionSummaryDto getCompositeSession(UUID compositeSessionId) {
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,18 @@ package at.procon.eventhub.tachographfilesession.service;
|
||||||
|
|
||||||
import at.procon.eventhub.config.EventHubProperties;
|
import at.procon.eventhub.config.EventHubProperties;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographCompositeSessionSummaryDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryDto;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowResponse;
|
||||||
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import java.net.CookieManager;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
@ -27,9 +31,14 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
public class TachographFileSessionService {
|
public class TachographFileSessionService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TachographFileSessionService.class);
|
private static final Logger log = LoggerFactory.getLogger(TachographFileSessionService.class);
|
||||||
|
private static final String LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE =
|
||||||
|
TachographFileSessionService.class.getName() + ".legalRequirementsCookieManager";
|
||||||
|
private static final String WORKFLOW_STATE_ATTRIBUTE =
|
||||||
|
TachographFileSessionService.class.getName() + ".uploadWorkflowState";
|
||||||
|
|
||||||
private final EventHubProperties properties;
|
private final EventHubProperties properties;
|
||||||
private final TachographFileSessionRepository repository;
|
private final TachographFileSessionRepository repository;
|
||||||
|
private final TachographCompositeSessionService compositeSessionService;
|
||||||
private final LegalRequirementsClient legalRequirementsClient;
|
private final LegalRequirementsClient legalRequirementsClient;
|
||||||
private final TachographXmlParser tachographXmlParser;
|
private final TachographXmlParser tachographXmlParser;
|
||||||
private final DriverCardXmlExtractionService driverCardExtractionService;
|
private final DriverCardXmlExtractionService driverCardExtractionService;
|
||||||
|
|
@ -38,6 +47,7 @@ public class TachographFileSessionService {
|
||||||
public TachographFileSessionService(
|
public TachographFileSessionService(
|
||||||
EventHubProperties properties,
|
EventHubProperties properties,
|
||||||
TachographFileSessionRepository repository,
|
TachographFileSessionRepository repository,
|
||||||
|
TachographCompositeSessionService compositeSessionService,
|
||||||
LegalRequirementsClient legalRequirementsClient,
|
LegalRequirementsClient legalRequirementsClient,
|
||||||
TachographXmlParser tachographXmlParser,
|
TachographXmlParser tachographXmlParser,
|
||||||
DriverCardXmlExtractionService driverCardExtractionService,
|
DriverCardXmlExtractionService driverCardExtractionService,
|
||||||
|
|
@ -45,6 +55,7 @@ public class TachographFileSessionService {
|
||||||
) {
|
) {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
|
this.compositeSessionService = compositeSessionService;
|
||||||
this.legalRequirementsClient = legalRequirementsClient;
|
this.legalRequirementsClient = legalRequirementsClient;
|
||||||
this.tachographXmlParser = tachographXmlParser;
|
this.tachographXmlParser = tachographXmlParser;
|
||||||
this.driverCardExtractionService = driverCardExtractionService;
|
this.driverCardExtractionService = driverCardExtractionService;
|
||||||
|
|
@ -55,14 +66,21 @@ public class TachographFileSessionService {
|
||||||
MultipartFile file,
|
MultipartFile file,
|
||||||
String tenantKey,
|
String tenantKey,
|
||||||
String sourceInstanceKey,
|
String sourceInstanceKey,
|
||||||
String sessionLabel
|
String sessionLabel,
|
||||||
|
HttpSession webSession
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
if (webSession == null) {
|
||||||
|
throw new IllegalArgumentException("HTTP session is required for tachograph upload workflow.");
|
||||||
|
}
|
||||||
validateFile(file);
|
validateFile(file);
|
||||||
byte[] fileBytes = file.getBytes();
|
byte[] fileBytes = file.getBytes();
|
||||||
validateFileBytes(fileBytes);
|
validateFileBytes(fileBytes);
|
||||||
|
CookieManager cookieManager = cookieManager(webSession);
|
||||||
|
TachographUploadWorkflowState workflowState = workflowState(webSession);
|
||||||
long uploadStartedAt = System.nanoTime();
|
long uploadStartedAt = System.nanoTime();
|
||||||
LegalRequirementsUploadResult uploadResult = legalRequirementsClient.uploadTachographFile(fileBytes, file.getOriginalFilename());
|
LegalRequirementsUploadResult uploadResult =
|
||||||
|
legalRequirementsClient.uploadTachographFile(cookieManager, fileBytes, file.getOriginalFilename());
|
||||||
long uploadDurationMs = elapsedMillis(uploadStartedAt);
|
long uploadDurationMs = elapsedMillis(uploadStartedAt);
|
||||||
|
|
||||||
long parseStartedAt = System.nanoTime();
|
long parseStartedAt = System.nanoTime();
|
||||||
|
|
@ -72,6 +90,7 @@ public class TachographFileSessionService {
|
||||||
Instant createdAt = Instant.now();
|
Instant createdAt = Instant.now();
|
||||||
Instant expiresAt = createdAt.plus(properties.getTachographFileSession().getTtl());
|
Instant expiresAt = createdAt.plus(properties.getTachographFileSession().getTtl());
|
||||||
boolean driverCardFile = "DriverCard".equals(parsedXml.rootElementName());
|
boolean driverCardFile = "DriverCard".equals(parsedXml.rootElementName());
|
||||||
|
validateWorkflowSequence(webSession, workflowState, cookieManager, driverCardFile);
|
||||||
TachographFileSessionMetadata metadata = new TachographFileSessionMetadata(
|
TachographFileSessionMetadata metadata = new TachographFileSessionMetadata(
|
||||||
tenant(tenantKey),
|
tenant(tenantKey),
|
||||||
sourceInstance(sourceInstanceKey, driverCardFile),
|
sourceInstance(sourceInstanceKey, driverCardFile),
|
||||||
|
|
@ -100,7 +119,10 @@ public class TachographFileSessionService {
|
||||||
fileBytes.length
|
fileBytes.length
|
||||||
);
|
);
|
||||||
TachographFileSession saved = repository.save(session);
|
TachographFileSession saved = repository.save(session);
|
||||||
return new CreateTachographFileSessionResponse(toSummary(saved));
|
workflowState.recordUploadedSession(saved.sessionId(), driverCardFile);
|
||||||
|
TachographCompositeSessionSummaryDto compositeSession = updateCompositeSession(workflowState, sessionLabel);
|
||||||
|
webSession.setAttribute(WORKFLOW_STATE_ATTRIBUTE, workflowState);
|
||||||
|
return new CreateTachographFileSessionResponse(toSummary(saved), workflowState.toDto(), compositeSession);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (e instanceof RuntimeException runtimeException) {
|
if (e instanceof RuntimeException runtimeException) {
|
||||||
throw runtimeException;
|
throw runtimeException;
|
||||||
|
|
@ -109,6 +131,27 @@ public class TachographFileSessionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TachographUploadWorkflowResponse resetUploadWorkflow(HttpSession webSession) {
|
||||||
|
if (webSession == null) {
|
||||||
|
throw new IllegalArgumentException("HTTP session is required for tachograph upload workflow.");
|
||||||
|
}
|
||||||
|
CookieManager existingCookieManager = existingCookieManager(webSession);
|
||||||
|
if (existingCookieManager != null) {
|
||||||
|
legalRequirementsClient.resetSession(existingCookieManager);
|
||||||
|
}
|
||||||
|
webSession.setAttribute(LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE, legalRequirementsClient.newCookieManager());
|
||||||
|
TachographUploadWorkflowState state = new TachographUploadWorkflowState();
|
||||||
|
webSession.setAttribute(WORKFLOW_STATE_ATTRIBUTE, state);
|
||||||
|
return new TachographUploadWorkflowResponse(state.toDto());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TachographUploadWorkflowResponse getUploadWorkflow(HttpSession webSession) {
|
||||||
|
if (webSession == null) {
|
||||||
|
throw new IllegalArgumentException("HTTP session is required for tachograph upload workflow.");
|
||||||
|
}
|
||||||
|
return new TachographUploadWorkflowResponse(workflowState(webSession).toDto());
|
||||||
|
}
|
||||||
|
|
||||||
public TachographFileSessionSummaryDto getSession(UUID sessionId) {
|
public TachographFileSessionSummaryDto getSession(UUID sessionId) {
|
||||||
return toSummary(requireSession(sessionId));
|
return toSummary(requireSession(sessionId));
|
||||||
}
|
}
|
||||||
|
|
@ -146,6 +189,27 @@ public class TachographFileSessionService {
|
||||||
return repository.find(sessionId).orElseThrow(() -> new TachographFileSessionNotFoundException(sessionId));
|
return repository.find(sessionId).orElseThrow(() -> new TachographFileSessionNotFoundException(sessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TachographCompositeSessionSummaryDto updateCompositeSession(
|
||||||
|
TachographUploadWorkflowState workflowState,
|
||||||
|
String sessionLabel
|
||||||
|
) {
|
||||||
|
if (workflowState.uploadedSessionIds().size() < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TachographCompositeSessionSummaryDto compositeSession = compositeSessionService.upsertCompositeSession(
|
||||||
|
workflowState.compositeSessionId(),
|
||||||
|
workflowState.uploadedSessionIds(),
|
||||||
|
compositeSessionLabel(sessionLabel)
|
||||||
|
);
|
||||||
|
workflowState.compositeSessionId(compositeSession.compositeSessionId());
|
||||||
|
return compositeSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String compositeSessionLabel(String sessionLabel) {
|
||||||
|
String normalizedLabel = blankToNull(sessionLabel);
|
||||||
|
return normalizedLabel == null ? "tachograph-upload-workflow" : normalizedLabel;
|
||||||
|
}
|
||||||
|
|
||||||
private TachographFileSessionSummaryDto toSummary(TachographFileSession session) {
|
private TachographFileSessionSummaryDto toSummary(TachographFileSession session) {
|
||||||
return new TachographFileSessionSummaryDto(
|
return new TachographFileSessionSummaryDto(
|
||||||
session.sessionId(),
|
session.sessionId(),
|
||||||
|
|
@ -207,6 +271,63 @@ public class TachographFileSessionService {
|
||||||
return driverCardFile ? "legalrequirements-drivercard" : "legalrequirements-vehicleunit";
|
return driverCardFile ? "legalrequirements-drivercard" : "legalrequirements-vehicleunit";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateWorkflowSequence(
|
||||||
|
HttpSession webSession,
|
||||||
|
TachographUploadWorkflowState workflowState,
|
||||||
|
CookieManager cookieManager,
|
||||||
|
boolean driverCardFile
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
validateWorkflowSequenceInternal(workflowState, driverCardFile);
|
||||||
|
} catch (IllegalArgumentException exception) {
|
||||||
|
resetWorkflowState(webSession, cookieManager);
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateWorkflowSequenceInternal(TachographUploadWorkflowState workflowState, boolean driverCardFile) {
|
||||||
|
if (!workflowState.driverCardUploaded() && !workflowState.uploadedSessionIds().isEmpty() && driverCardFile) {
|
||||||
|
throw new IllegalArgumentException("Workflow already contains uploaded files. Reset the workflow before starting a new driver card import.");
|
||||||
|
}
|
||||||
|
if (!workflowState.driverCardUploaded() && !driverCardFile) {
|
||||||
|
throw new IllegalArgumentException("Driver card file must be uploaded first after workflow reset.");
|
||||||
|
}
|
||||||
|
if (workflowState.driverCardUploaded() && driverCardFile) {
|
||||||
|
throw new IllegalArgumentException("Workflow already contains a driver card file. Upload vehicle-unit files next or reset the workflow.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CookieManager cookieManager(HttpSession webSession) {
|
||||||
|
CookieManager existing = existingCookieManager(webSession);
|
||||||
|
if (existing != null) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
CookieManager created = legalRequirementsClient.newCookieManager();
|
||||||
|
webSession.setAttribute(LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE, created);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CookieManager existingCookieManager(HttpSession webSession) {
|
||||||
|
Object value = webSession.getAttribute(LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE);
|
||||||
|
return value instanceof CookieManager cookieManager ? cookieManager : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TachographUploadWorkflowState workflowState(HttpSession webSession) {
|
||||||
|
Object value = webSession.getAttribute(WORKFLOW_STATE_ATTRIBUTE);
|
||||||
|
if (value instanceof TachographUploadWorkflowState state) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
TachographUploadWorkflowState state = new TachographUploadWorkflowState();
|
||||||
|
webSession.setAttribute(WORKFLOW_STATE_ATTRIBUTE, state);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetWorkflowState(HttpSession webSession, CookieManager cookieManager) {
|
||||||
|
legalRequirementsClient.resetSession(cookieManager);
|
||||||
|
webSession.setAttribute(LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE, legalRequirementsClient.newCookieManager());
|
||||||
|
webSession.setAttribute(WORKFLOW_STATE_ATTRIBUTE, new TachographUploadWorkflowState());
|
||||||
|
}
|
||||||
|
|
||||||
private String blankToNull(String value) {
|
private String blankToNull(String value) {
|
||||||
return value == null || value.isBlank() ? null : value.trim();
|
return value == null || value.isBlank() ? null : value.trim();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.service;
|
||||||
|
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowDto;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
final class TachographUploadWorkflowState {
|
||||||
|
|
||||||
|
private UUID driverCardSessionId;
|
||||||
|
private final List<UUID> uploadedSessionIds = new ArrayList<>();
|
||||||
|
private final List<UUID> vehicleUnitSessionIds = new ArrayList<>();
|
||||||
|
private UUID compositeSessionId;
|
||||||
|
|
||||||
|
boolean driverCardUploaded() {
|
||||||
|
return driverCardSessionId != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID driverCardSessionId() {
|
||||||
|
return driverCardSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UUID> uploadedSessionIds() {
|
||||||
|
return List.copyOf(uploadedSessionIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UUID> vehicleUnitSessionIds() {
|
||||||
|
return List.copyOf(vehicleUnitSessionIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID compositeSessionId() {
|
||||||
|
return compositeSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void recordUploadedSession(UUID sessionId, boolean driverCardFile) {
|
||||||
|
uploadedSessionIds.add(sessionId);
|
||||||
|
if (driverCardFile) {
|
||||||
|
driverCardSessionId = sessionId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vehicleUnitSessionIds.add(sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compositeSessionId(UUID compositeSessionId) {
|
||||||
|
this.compositeSessionId = compositeSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
TachographUploadWorkflowDto toDto() {
|
||||||
|
return new TachographUploadWorkflowDto(
|
||||||
|
driverCardUploaded(),
|
||||||
|
driverCardSessionId,
|
||||||
|
uploadedSessionIds(),
|
||||||
|
vehicleUnitSessionIds(),
|
||||||
|
compositeSessionId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,10 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import javax.xml.XMLConstants;
|
import javax.xml.XMLConstants;
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
|
@ -21,6 +25,9 @@ import org.xml.sax.SAXException;
|
||||||
public class TachographXmlParser {
|
public class TachographXmlParser {
|
||||||
|
|
||||||
private static final String JAXP_MAX_OCCUR_LIMIT = "http://www.oracle.com/xml/jaxp/properties/maxOccurLimit";
|
private static final String JAXP_MAX_OCCUR_LIMIT = "http://www.oracle.com/xml/jaxp/properties/maxOccurLimit";
|
||||||
|
private static final Pattern MANUFACTURER_SPECIFIC_ERROR_CODE_PATTERN = Pattern.compile(
|
||||||
|
"(<manufacturerSpecificErrorCode>)([^<\\s]+)(</manufacturerSpecificErrorCode>)"
|
||||||
|
);
|
||||||
|
|
||||||
private final Schema schema;
|
private final Schema schema;
|
||||||
|
|
||||||
|
|
@ -62,11 +69,45 @@ public class TachographXmlParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String normalizeXmlContent(String xmlContent) {
|
String normalizeXmlContent(String xmlContent) {
|
||||||
if (xmlContent == null || xmlContent.isEmpty()) {
|
if (xmlContent == null || xmlContent.isEmpty()) {
|
||||||
return xmlContent;
|
return xmlContent;
|
||||||
}
|
}
|
||||||
return xmlContent.charAt(0) == '\uFEFF' ? xmlContent.substring(1) : xmlContent;
|
String normalized = xmlContent.charAt(0) == '\uFEFF' ? xmlContent.substring(1) : xmlContent;
|
||||||
|
return normalizeManufacturerSpecificErrorCodes(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeManufacturerSpecificErrorCodes(String xmlContent) {
|
||||||
|
Matcher matcher = MANUFACTURER_SPECIFIC_ERROR_CODE_PATTERN.matcher(xmlContent);
|
||||||
|
StringBuffer normalized = new StringBuffer(xmlContent.length());
|
||||||
|
while (matcher.find()) {
|
||||||
|
String rawValue = matcher.group(2);
|
||||||
|
String replacementValue = normalizeThreeByteOctetString(rawValue);
|
||||||
|
matcher.appendReplacement(
|
||||||
|
normalized,
|
||||||
|
Matcher.quoteReplacement(matcher.group(1) + replacementValue + matcher.group(3))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
matcher.appendTail(normalized);
|
||||||
|
return normalized.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeThreeByteOctetString(String rawValue) {
|
||||||
|
if (rawValue == null || rawValue.isBlank()) {
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
if (rawValue.matches("(?i)[0-9a-f]{6}")) {
|
||||||
|
return rawValue.toUpperCase();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
byte[] decoded = Base64.getDecoder().decode(rawValue);
|
||||||
|
if (decoded.length == 3) {
|
||||||
|
return HexFormat.of().formatHex(decoded).toUpperCase();
|
||||||
|
}
|
||||||
|
return rawValue;
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Schema loadSchema() {
|
private Schema loadSchema() {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryD
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowDto;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowResponse;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
|
||||||
|
|
@ -34,6 +36,7 @@ import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
import org.springframework.mock.web.MockMultipartFile;
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
@ -49,6 +52,7 @@ class TachographFileSessionControllerTest {
|
||||||
.setControllerAdvice(new TachographFileSessionExceptionHandler())
|
.setControllerAdvice(new TachographFileSessionExceptionHandler())
|
||||||
.build();
|
.build();
|
||||||
UUID sessionId = UUID.randomUUID();
|
UUID sessionId = UUID.randomUUID();
|
||||||
|
MockHttpSession webSession = new MockHttpSession();
|
||||||
TachographFileDriverSummaryDto driver = new TachographFileDriverSummaryDto("12:123", "Muster", "Max", "12", "123", 3, 2);
|
TachographFileDriverSummaryDto driver = new TachographFileDriverSummaryDto("12:123", "Muster", "Max", "12", "123", 3, 2);
|
||||||
TachographFileSessionSummaryDto summary = new TachographFileSessionSummaryDto(
|
TachographFileSessionSummaryDto summary = new TachographFileSessionSummaryDto(
|
||||||
sessionId,
|
sessionId,
|
||||||
|
|
@ -64,8 +68,26 @@ class TachographFileSessionControllerTest {
|
||||||
Instant.parse("2026-05-12T10:00:00Z"),
|
Instant.parse("2026-05-12T10:00:00Z"),
|
||||||
Instant.parse("2026-05-12T14:00:00Z")
|
Instant.parse("2026-05-12T14:00:00Z")
|
||||||
);
|
);
|
||||||
when(service.createSession(org.mockito.ArgumentMatchers.any(), eq("default"), eq("src"), eq("sample")))
|
when(service.createSession(
|
||||||
.thenReturn(new CreateTachographFileSessionResponse(summary));
|
org.mockito.ArgumentMatchers.any(),
|
||||||
|
eq("default"),
|
||||||
|
eq("src"),
|
||||||
|
eq("sample"),
|
||||||
|
org.mockito.ArgumentMatchers.any(jakarta.servlet.http.HttpSession.class)
|
||||||
|
))
|
||||||
|
.thenReturn(new CreateTachographFileSessionResponse(
|
||||||
|
summary,
|
||||||
|
new TachographUploadWorkflowDto(true, sessionId, List.of(sessionId), List.of(), null),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
when(service.resetUploadWorkflow(org.mockito.ArgumentMatchers.any(jakarta.servlet.http.HttpSession.class)))
|
||||||
|
.thenReturn(new TachographUploadWorkflowResponse(
|
||||||
|
new TachographUploadWorkflowDto(false, null, List.of(), List.of(), null)
|
||||||
|
));
|
||||||
|
when(service.getUploadWorkflow(org.mockito.ArgumentMatchers.any(jakarta.servlet.http.HttpSession.class)))
|
||||||
|
.thenReturn(new TachographUploadWorkflowResponse(
|
||||||
|
new TachographUploadWorkflowDto(true, sessionId, List.of(sessionId), List.of(), null)
|
||||||
|
));
|
||||||
when(service.getSession(sessionId)).thenReturn(summary);
|
when(service.getSession(sessionId)).thenReturn(summary);
|
||||||
when(service.listDrivers(sessionId)).thenReturn(new TachographFileSessionListDriversResponse(sessionId, List.of(driver)));
|
when(service.listDrivers(sessionId)).thenReturn(new TachographFileSessionListDriversResponse(sessionId, List.of(driver)));
|
||||||
when(service.getDriver(sessionId, "12:123")).thenReturn(new TachographFileDriverDetailDto(sessionId, "12:123", null, null, List.of(), List.of(), List.of(), List.of(), List.of(), List.of()));
|
when(service.getDriver(sessionId, "12:123")).thenReturn(new TachographFileDriverDetailDto(sessionId, "12:123", null, null, List.of(), List.of(), List.of(), List.of(), List.of(), List.of()));
|
||||||
|
|
@ -319,13 +341,23 @@ class TachographFileSessionControllerTest {
|
||||||
));
|
));
|
||||||
when(service.deleteSession(sessionId)).thenReturn(new TachographFileSessionDeleteResponse(sessionId, true));
|
when(service.deleteSession(sessionId)).thenReturn(new TachographFileSessionDeleteResponse(sessionId, true));
|
||||||
|
|
||||||
|
mockMvc.perform(post("/api/eventhub/tachograph-file-sessions/workflow/reset").session(webSession))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.workflow.driverCardUploaded").value(false));
|
||||||
|
|
||||||
mockMvc.perform(multipart("/api/eventhub/tachograph-file-sessions")
|
mockMvc.perform(multipart("/api/eventhub/tachograph-file-sessions")
|
||||||
.file(new MockMultipartFile("file", "sample.ddd", "application/octet-stream", "abc".getBytes()))
|
.file(new MockMultipartFile("file", "sample.ddd", "application/octet-stream", "abc".getBytes()))
|
||||||
.param("tenantKey", "default")
|
.param("tenantKey", "default")
|
||||||
.param("sourceInstanceKey", "src")
|
.param("sourceInstanceKey", "src")
|
||||||
.param("sessionLabel", "sample"))
|
.param("sessionLabel", "sample")
|
||||||
|
.session(webSession))
|
||||||
.andExpect(status().isCreated())
|
.andExpect(status().isCreated())
|
||||||
.andExpect(jsonPath("$.session.sessionId").value(sessionId.toString()));
|
.andExpect(jsonPath("$.session.sessionId").value(sessionId.toString()))
|
||||||
|
.andExpect(jsonPath("$.workflow.driverCardUploaded").value(true));
|
||||||
|
|
||||||
|
mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/workflow").session(webSession))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.workflow.driverCardSessionId").value(sessionId.toString()));
|
||||||
|
|
||||||
mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/{sessionId}", sessionId))
|
mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/{sessionId}", sessionId))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,27 @@ package at.procon.eventhub.tachographfilesession.service;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.verifyNoInteractions;
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import at.procon.eventhub.config.EventHubProperties;
|
import at.procon.eventhub.config.EventHubProperties;
|
||||||
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographCompositeSessionSummaryDto;
|
||||||
|
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryDto;
|
||||||
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
|
||||||
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
|
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.springframework.mock.web.MockMultipartFile;
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
|
|
||||||
class TachographFileSessionServiceTest {
|
class TachographFileSessionServiceTest {
|
||||||
|
|
||||||
|
|
@ -26,6 +31,7 @@ class TachographFileSessionServiceTest {
|
||||||
void createsAndLoadsSession() {
|
void createsAndLoadsSession() {
|
||||||
EventHubProperties properties = new EventHubProperties();
|
EventHubProperties properties = new EventHubProperties();
|
||||||
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
||||||
|
TachographCompositeSessionService compositeSessionService = Mockito.mock(TachographCompositeSessionService.class);
|
||||||
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
|
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
|
||||||
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
|
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
|
||||||
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
|
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
|
||||||
|
|
@ -33,14 +39,17 @@ class TachographFileSessionServiceTest {
|
||||||
TachographFileSessionService service = new TachographFileSessionService(
|
TachographFileSessionService service = new TachographFileSessionService(
|
||||||
properties,
|
properties,
|
||||||
repository,
|
repository,
|
||||||
|
compositeSessionService,
|
||||||
client,
|
client,
|
||||||
parser,
|
parser,
|
||||||
driverCardExtractor,
|
driverCardExtractor,
|
||||||
vehicleUnitExtractor
|
vehicleUnitExtractor
|
||||||
);
|
);
|
||||||
|
when(client.newCookieManager()).thenReturn(new java.net.CookieManager());
|
||||||
|
|
||||||
MockMultipartFile file = new MockMultipartFile("file", "sample.ddd", "application/octet-stream", "abc".getBytes(StandardCharsets.UTF_8));
|
MockMultipartFile file = new MockMultipartFile("file", "sample.ddd", "application/octet-stream", "abc".getBytes(StandardCharsets.UTF_8));
|
||||||
when(client.uploadTachographFile(any(), eq("sample.ddd"))).thenReturn(new LegalRequirementsUploadResult("42", "<DriverCard/>"));
|
when(client.uploadTachographFile(any(java.net.CookieManager.class), any(), eq("sample.ddd")))
|
||||||
|
.thenReturn(new LegalRequirementsUploadResult("42", "<DriverCard/>"));
|
||||||
TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
|
TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
|
||||||
when(parsed.rootElementName()).thenReturn("DriverCard");
|
when(parsed.rootElementName()).thenReturn("DriverCard");
|
||||||
when(parser.parse("<DriverCard/>")).thenReturn(parsed);
|
when(parser.parse("<DriverCard/>")).thenReturn(parsed);
|
||||||
|
|
@ -55,9 +64,12 @@ class TachographFileSessionServiceTest {
|
||||||
);
|
);
|
||||||
when(driverCardExtractor.extract(eq(parsed), any(), any(), any())).thenReturn(extracted);
|
when(driverCardExtractor.extract(eq(parsed), any(), any(), any())).thenReturn(extracted);
|
||||||
|
|
||||||
CreateTachographFileSessionResponse response = service.createSession(file, null, null, "sample");
|
CreateTachographFileSessionResponse response = service.createSession(file, null, null, "sample", new MockHttpSession());
|
||||||
|
|
||||||
assertThat(response.session().sessionId()).isEqualTo(extracted.sessionId());
|
assertThat(response.session().sessionId()).isEqualTo(extracted.sessionId());
|
||||||
|
assertThat(response.workflow()).isNotNull();
|
||||||
|
assertThat(response.workflow().driverCardUploaded()).isTrue();
|
||||||
|
assertThat(response.compositeSession()).isNull();
|
||||||
assertThat(service.getSession(extracted.sessionId()).sessionId()).isEqualTo(extracted.sessionId());
|
assertThat(service.getSession(extracted.sessionId()).sessionId()).isEqualTo(extracted.sessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,6 +78,7 @@ class TachographFileSessionServiceTest {
|
||||||
EventHubProperties properties = new EventHubProperties();
|
EventHubProperties properties = new EventHubProperties();
|
||||||
properties.getTachographFileSession().setMaxFileSizeBytes(1024);
|
properties.getTachographFileSession().setMaxFileSizeBytes(1024);
|
||||||
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
||||||
|
TachographCompositeSessionService compositeSessionService = Mockito.mock(TachographCompositeSessionService.class);
|
||||||
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
|
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
|
||||||
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
|
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
|
||||||
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
|
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
|
||||||
|
|
@ -73,6 +86,7 @@ class TachographFileSessionServiceTest {
|
||||||
TachographFileSessionService service = new TachographFileSessionService(
|
TachographFileSessionService service = new TachographFileSessionService(
|
||||||
properties,
|
properties,
|
||||||
repository,
|
repository,
|
||||||
|
compositeSessionService,
|
||||||
client,
|
client,
|
||||||
parser,
|
parser,
|
||||||
driverCardExtractor,
|
driverCardExtractor,
|
||||||
|
|
@ -81,17 +95,18 @@ class TachographFileSessionServiceTest {
|
||||||
|
|
||||||
MockMultipartFile file = new MockMultipartFile("file", "sample.ddd", "application/octet-stream", new byte[1025]);
|
MockMultipartFile file = new MockMultipartFile("file", "sample.ddd", "application/octet-stream", new byte[1025]);
|
||||||
|
|
||||||
assertThatThrownBy(() -> service.createSession(file, null, null, "sample"))
|
assertThatThrownBy(() -> service.createSession(file, null, null, "sample", new MockHttpSession()))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessageContaining("size limit");
|
.hasMessageContaining("size limit");
|
||||||
|
|
||||||
verifyNoInteractions(client, parser, driverCardExtractor, vehicleUnitExtractor);
|
verifyNoInteractions(compositeSessionService, client, parser, driverCardExtractor, vehicleUnitExtractor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void usesVehicleUnitDefaultsAndExtractorForVehicleUnitXml() {
|
void usesVehicleUnitDefaultsAndExtractorForVehicleUnitXml() {
|
||||||
EventHubProperties properties = new EventHubProperties();
|
EventHubProperties properties = new EventHubProperties();
|
||||||
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
||||||
|
TachographCompositeSessionService compositeSessionService = Mockito.mock(TachographCompositeSessionService.class);
|
||||||
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
|
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
|
||||||
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
|
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
|
||||||
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
|
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
|
||||||
|
|
@ -99,14 +114,17 @@ class TachographFileSessionServiceTest {
|
||||||
TachographFileSessionService service = new TachographFileSessionService(
|
TachographFileSessionService service = new TachographFileSessionService(
|
||||||
properties,
|
properties,
|
||||||
repository,
|
repository,
|
||||||
|
compositeSessionService,
|
||||||
client,
|
client,
|
||||||
parser,
|
parser,
|
||||||
driverCardExtractor,
|
driverCardExtractor,
|
||||||
vehicleUnitExtractor
|
vehicleUnitExtractor
|
||||||
);
|
);
|
||||||
|
when(client.newCookieManager()).thenReturn(new java.net.CookieManager());
|
||||||
|
|
||||||
MockMultipartFile file = new MockMultipartFile("file", "vu.ddd", "application/octet-stream", "vu".getBytes(StandardCharsets.UTF_8));
|
MockMultipartFile file = new MockMultipartFile("file", "vu.ddd", "application/octet-stream", "vu".getBytes(StandardCharsets.UTF_8));
|
||||||
when(client.uploadTachographFile(any(), eq("vu.ddd"))).thenReturn(new LegalRequirementsUploadResult("77", "<VehicleUnit/>"));
|
when(client.uploadTachographFile(any(java.net.CookieManager.class), any(), eq("vu.ddd")))
|
||||||
|
.thenReturn(new LegalRequirementsUploadResult("77", "<VehicleUnit/>"));
|
||||||
TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
|
TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
|
||||||
when(parsed.rootElementName()).thenReturn("VehicleUnit");
|
when(parsed.rootElementName()).thenReturn("VehicleUnit");
|
||||||
when(parser.parse("<VehicleUnit/>")).thenReturn(parsed);
|
when(parser.parse("<VehicleUnit/>")).thenReturn(parsed);
|
||||||
|
|
@ -121,9 +139,74 @@ class TachographFileSessionServiceTest {
|
||||||
);
|
);
|
||||||
when(vehicleUnitExtractor.extract(eq(parsed), any(), any(), any())).thenReturn(extracted);
|
when(vehicleUnitExtractor.extract(eq(parsed), any(), any(), any())).thenReturn(extracted);
|
||||||
|
|
||||||
CreateTachographFileSessionResponse response = service.createSession(file, null, null, "vu");
|
MockHttpSession webSession = new MockHttpSession();
|
||||||
|
service.resetUploadWorkflow(webSession);
|
||||||
|
MockMultipartFile driverCardFile = new MockMultipartFile("file", "driver.ddd", "application/octet-stream", "dc".getBytes(StandardCharsets.UTF_8));
|
||||||
|
when(client.uploadTachographFile(any(java.net.CookieManager.class), any(), eq("driver.ddd")))
|
||||||
|
.thenReturn(new LegalRequirementsUploadResult("42", "<DriverCard/>"));
|
||||||
|
TachographXmlParser.ParsedTachographXml driverParsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
|
||||||
|
when(driverParsed.rootElementName()).thenReturn("DriverCard");
|
||||||
|
when(parser.parse("<DriverCard/>")).thenReturn(driverParsed);
|
||||||
|
TachographFileSession driverExtracted = new TachographFileSession(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "driver.ddd", "a", 2, "42", "b", true, null),
|
||||||
|
Map.of(),
|
||||||
|
new ExtractionStats(0, 0, 0, 0, 0, 0),
|
||||||
|
java.util.List.of(),
|
||||||
|
Instant.now(),
|
||||||
|
Instant.now().plusSeconds(10)
|
||||||
|
);
|
||||||
|
when(driverCardExtractor.extract(eq(driverParsed), any(), any(), any())).thenReturn(driverExtracted);
|
||||||
|
when(compositeSessionService.upsertCompositeSession(any(), anyList(), any()))
|
||||||
|
.thenReturn(new TachographCompositeSessionSummaryDto(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
"default",
|
||||||
|
"vu",
|
||||||
|
List.of(driverExtracted.sessionId(), extracted.sessionId()),
|
||||||
|
List.<TachographFileDriverSummaryDto>of(),
|
||||||
|
Instant.now()
|
||||||
|
));
|
||||||
|
|
||||||
|
service.createSession(driverCardFile, null, null, "sample", webSession);
|
||||||
|
CreateTachographFileSessionResponse response = service.createSession(file, null, null, "vu", webSession);
|
||||||
|
|
||||||
assertThat(response.session().driverCardFile()).isFalse();
|
assertThat(response.session().driverCardFile()).isFalse();
|
||||||
assertThat(response.session().sourceInstanceKey()).isEqualTo("legalrequirements-vehicleunit");
|
assertThat(response.session().sourceInstanceKey()).isEqualTo("legalrequirements-vehicleunit");
|
||||||
|
assertThat(response.compositeSession()).isNotNull();
|
||||||
|
assertThat(response.workflow().uploadedSessionIds()).hasSize(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsVehicleUnitBeforeDriverCardInWorkflow() {
|
||||||
|
EventHubProperties properties = new EventHubProperties();
|
||||||
|
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
|
||||||
|
TachographCompositeSessionService compositeSessionService = Mockito.mock(TachographCompositeSessionService.class);
|
||||||
|
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
|
||||||
|
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
|
||||||
|
TachographFileSessionService service = new TachographFileSessionService(
|
||||||
|
properties,
|
||||||
|
repository,
|
||||||
|
compositeSessionService,
|
||||||
|
client,
|
||||||
|
parser,
|
||||||
|
Mockito.mock(DriverCardXmlExtractionService.class),
|
||||||
|
Mockito.mock(VehicleUnitXmlExtractionService.class)
|
||||||
|
);
|
||||||
|
when(client.newCookieManager()).thenReturn(new java.net.CookieManager());
|
||||||
|
when(client.uploadTachographFile(any(java.net.CookieManager.class), any(), eq("vu.ddd")))
|
||||||
|
.thenReturn(new LegalRequirementsUploadResult("77", "<VehicleUnit/>"));
|
||||||
|
TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
|
||||||
|
when(parsed.rootElementName()).thenReturn("VehicleUnit");
|
||||||
|
when(parser.parse("<VehicleUnit/>")).thenReturn(parsed);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> service.createSession(
|
||||||
|
new MockMultipartFile("file", "vu.ddd", "application/octet-stream", "vu".getBytes(StandardCharsets.UTF_8)),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"vu",
|
||||||
|
new MockHttpSession()
|
||||||
|
))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
|
.hasMessageContaining("Driver card file must be uploaded first");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,26 @@ class TachographXmlParserTest {
|
||||||
.isInstanceOf(TachographXmlValidationException.class);
|
.isInstanceOf(TachographXmlValidationException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void normalizesManufacturerSpecificErrorCodeFromBase64ToHex() {
|
||||||
|
String normalized = parser.normalizeXmlContent("""
|
||||||
|
<VehicleUnit>
|
||||||
|
<EventsFaults>
|
||||||
|
<vuFaultData>
|
||||||
|
<vuFaultRecords>
|
||||||
|
<manufacturerSpecificEventFaultData>
|
||||||
|
<manufacturerCode>0</manufacturerCode>
|
||||||
|
<manufacturerSpecificErrorCode>////</manufacturerSpecificErrorCode>
|
||||||
|
</manufacturerSpecificEventFaultData>
|
||||||
|
</vuFaultRecords>
|
||||||
|
</vuFaultData>
|
||||||
|
</EventsFaults>
|
||||||
|
</VehicleUnit>
|
||||||
|
""");
|
||||||
|
|
||||||
|
assertThat(normalized).contains("<manufacturerSpecificErrorCode>FFFFFF</manufacturerSpecificErrorCode>");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void rejectsXmlWithDoctype() {
|
void rejectsXmlWithDoctype() {
|
||||||
String xmlWithDoctype = """
|
String xmlWithDoctype = """
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue