diff --git a/.github/workflows/create-release.yaml b/.github/workflows/create-release.yaml index 501ccb132..831874772 100644 --- a/.github/workflows/create-release.yaml +++ b/.github/workflows/create-release.yaml @@ -71,7 +71,7 @@ jobs: java-version: '25' - name: install model filiere - run: mvn install:install-file -Dfile="$(pwd)/libs/modelefiliere-2.2.2-springboot4.jar" -DgroupId=fr.insee -DartifactId=modelefiliere -Dversion=2.2.2-springboot4 -Dpackaging=jar -DgeneratePom=true + run: mvn install:install-file -Dfile="$(pwd)/libs/modelefiliere-2.1.0.jar" -DgroupId=fr.insee -DartifactId=modelefiliere -Dversion=2.1.0 -Dpackaging=jar -DgeneratePom=true - name: Clone BPM uses: actions/checkout@v6 @@ -86,10 +86,8 @@ jobs: cd .. - uses: actions/checkout@v6 - - name: Build app - run: | - git fetch origin main - git checkout origin/main + - name: Build app + run: mvn package --no-transfer-progress - name: Upload app jar @@ -182,4 +180,4 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} default_branch: ${{ github.ref }} tags: ${{ needs.check-version.outputs.release-tag }} - workdir: . + workdir: . \ No newline at end of file diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 34ef52940..030f04e8c 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -28,7 +28,7 @@ jobs: java-version: "25" - name: install model filiere - run: mvn install:install-file -Dfile="$(pwd)/libs/modelefiliere-2.2.2-springboot4.jar" -DgroupId=fr.insee -DartifactId=modelefiliere -Dversion=2.2.2-springboot4 -Dpackaging=jar -DgeneratePom=true + run: mvn install:install-file -Dfile="$(pwd)/libs/modelefiliere-2.1.0.jar" -DgroupId=fr.insee -DartifactId=modelefiliere -Dversion=2.1.0 -Dpackaging=jar -DgeneratePom=true - name: Clone BPM uses: actions/checkout@master diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 9fb5dcea9..ca58eeca0 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -30,7 +30,7 @@ jobs: cache: maven - name: install model filiere - run: mvn install:install-file -Dfile="$(pwd)/libs/modelefiliere-2.2.2-springboot4.jar" -DgroupId=fr.insee -DartifactId=modelefiliere -Dversion=2.2.2-springboot4 -Dpackaging=jar -DgeneratePom=true + run: mvn install:install-file -Dfile="$(pwd)/libs/modelefiliere-2.1.0.jar" -DgroupId=fr.insee -DartifactId=modelefiliere -Dversion=2.1.0 -Dpackaging=jar -DgeneratePom=true - name: Clone BPM uses: actions/checkout@master diff --git a/.github/workflows/scheduled-version-update.yaml b/.github/workflows/scheduled-version-update.yaml index c06fa32c4..29adabad1 100644 --- a/.github/workflows/scheduled-version-update.yaml +++ b/.github/workflows/scheduled-version-update.yaml @@ -87,7 +87,7 @@ jobs: - name: install model filiere run: | - mvn install:install-file -Dfile="$(pwd)/genesis/libs/modelefiliere-2.2.2-springboot4.jar" -DgroupId=fr.insee -DartifactId=modelefiliere -Dversion=2.2.2-springboot4 -Dpackaging=jar -DgeneratePom=true + mvn install:install-file -Dfile="$(pwd)/genesis/libs/modelefiliere-2.1.0.jar" -DgroupId=fr.insee -DartifactId=modelefiliere -Dversion=2.1.0 -Dpackaging=jar -DgeneratePom=true - name: Clone BPM uses: actions/checkout@master diff --git a/.gitignore b/.gitignore index 918479bc7..21e24e4c5 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ src/bin/ # Package Files # *.jar -!libs/*.jar *.war *.nar *.ear diff --git a/CHANGELOG.md b/CHANGELOG.md index d6ef84aec..ca2f686e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,4 @@ # Changelog -## 2.7.0 [2026-06-18] -### Changed -- spring-boot 3.5.14 -> 4.0.6 - -## 2.6.10 [2026-06-18] -### Fixed -- Aligned the conversion logic for multi-iteration variables with single-value variables during collectedVariables creation to avoid unexpected xxx.0 values for INTEGER variables. - ## 2.6.9 [2026-06-11] ### Fixed - Delete only expired V2 schedules from dataProcessingContext diff --git a/libs/modelefiliere-2.1.0.jar b/libs/modelefiliere-2.1.0.jar new file mode 100644 index 000000000..cadda87eb Binary files /dev/null and b/libs/modelefiliere-2.1.0.jar differ diff --git a/libs/modelefiliere-2.2.2-springboot4.jar b/libs/modelefiliere-2.2.2-springboot4.jar deleted file mode 100644 index 37408feeb..000000000 Binary files a/libs/modelefiliere-2.2.2-springboot4.jar and /dev/null differ diff --git a/pom.xml b/pom.xml index 500047373..822310aa2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ 4.0.0 fr.insee.genesis genesis-api - 2.7.0 + 2.6.9 jar genesis-api org.springframework.boot spring-boot-starter-parent - 4.0.6 + 3.5.14 @@ -20,12 +20,14 @@ 10.1.55 3.20.0 + 5.13.4 + 2.21.1 1.2.0 - 3.0.3 + 2.8.17 1.6.3 - 1.25.5 + 1.25.3 1.2.3 @@ -73,19 +75,10 @@ test - org.springframework.boot - spring-boot-starter-security-test - test - - - org.springframework.boot - spring-boot-starter-webmvc-test + org.springframework.security + spring-security-test test - - org.springframework.boot - spring-boot-starter-actuator - @@ -113,7 +106,7 @@ fr.insee modelefiliere - 2.2.2-springboot4 + 2.1.0 diff --git a/src/main/java/fr/insee/genesis/controller/dto/SurveyUnitInputDto.java b/src/main/java/fr/insee/genesis/controller/dto/SurveyUnitInputDto.java index c0af750d1..1c7ec8efd 100644 --- a/src/main/java/fr/insee/genesis/controller/dto/SurveyUnitInputDto.java +++ b/src/main/java/fr/insee/genesis/controller/dto/SurveyUnitInputDto.java @@ -1,16 +1,12 @@ package fr.insee.genesis.controller.dto; import fr.insee.genesis.domain.model.surveyunit.Mode; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; import java.util.List; @Builder -@NoArgsConstructor -@AllArgsConstructor @Data public class SurveyUnitInputDto { private String questionnaireId; diff --git a/src/main/java/fr/insee/genesis/controller/rest/LunaticModelController.java b/src/main/java/fr/insee/genesis/controller/rest/LunaticModelController.java index ed38ccee4..47def0852 100644 --- a/src/main/java/fr/insee/genesis/controller/rest/LunaticModelController.java +++ b/src/main/java/fr/insee/genesis/controller/rest/LunaticModelController.java @@ -1,6 +1,7 @@ package fr.insee.genesis.controller.rest; -import tools.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import fr.insee.genesis.domain.model.lunaticmodel.LunaticModelModel; import fr.insee.genesis.domain.ports.api.LunaticModelApiPort; import fr.insee.genesis.exceptions.GenesisException; @@ -14,7 +15,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import tools.jackson.databind.json.JsonMapper; import java.util.Map; @@ -45,12 +45,10 @@ public ResponseEntity saveRawResponsesFromJsonBody( @PreAuthorize("hasRole('READER')") public ResponseEntity getLunaticModelFromQuestionnaireId( @RequestParam("questionnaireId") String questionnaireId - ) throws JacksonException, GenesisException { + ) throws JsonProcessingException, GenesisException { LunaticModelModel lunaticModelModel = lunaticModelApiPort.get(questionnaireId); - JsonMapper objectMapper = JsonMapper.builder() - .findAndAddModules() - .build(); + ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); return ResponseEntity.ok(objectMapper.writeValueAsString(lunaticModelModel.lunaticModel())); } } diff --git a/src/main/java/fr/insee/genesis/controller/rest/responses/InterrogationController.java b/src/main/java/fr/insee/genesis/controller/rest/responses/InterrogationController.java index bd93de3ec..b25eb947a 100644 --- a/src/main/java/fr/insee/genesis/controller/rest/responses/InterrogationController.java +++ b/src/main/java/fr/insee/genesis/controller/rest/responses/InterrogationController.java @@ -2,6 +2,7 @@ import fr.insee.genesis.controller.dto.InterrogationBatchResponse; import fr.insee.genesis.controller.rest.CommonApiResponse; +import fr.insee.genesis.controller.utils.DateTimeUtils; import fr.insee.genesis.domain.model.surveyunit.InterrogationId; import fr.insee.genesis.domain.model.surveyunit.InterrogationInfo; import fr.insee.genesis.domain.ports.api.SurveyUnitApiPort; @@ -61,14 +62,27 @@ public ResponseEntity getAllInterrogationIdsByQuesti ) @RequestParam(value = "since", required = false) Instant since, + @RequestParam(value = "localSinceDate", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Filter interrogations to those recorded strictly after the given timestamp (Europe/Paris timezone)", + schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localSinceDate, @RequestParam(value = "until", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) @Parameter( description = "Filter interrogations to those recorded before the given timestamp or at the same time (ISO-8601 UTC format).", schema = @Schema(type = "string", format = "date-time", example = "2026-01-31T23:59:59Z") ) - Instant until) { - List idsInfo = surveyUnitService.searchInterrogations(collectionInstrumentId, since, until); + Instant until, + @RequestParam(value = "localUntilDate", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Filter interrogations to those recorded before the given timestamp or at the same time (Europe/Paris timezone)", + schema = @Schema(type = "string", format = "date-time", example = "2026-04-02T01:00:00")) + LocalDateTime localUntilDate) { + + Instant resolvedSinceDate = DateTimeUtils.resolveInstant(since, localSinceDate); + Instant resolvedEndDate = DateTimeUtils.resolveInstant(until, localUntilDate); + List idsInfo = surveyUnitService.searchInterrogations(collectionInstrumentId, resolvedSinceDate, resolvedEndDate); InterrogationBatchResponse response = buildInterrogationBatchResponse(idsInfo); return ResponseEntity.ok(response); } diff --git a/src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseController.java b/src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseController.java index b9d1be35d..010097d7b 100644 --- a/src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseController.java +++ b/src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseController.java @@ -1,6 +1,7 @@ package fr.insee.genesis.controller.rest.responses; import fr.insee.genesis.controller.dto.rawdata.LunaticJsonRawDataUnprocessedDto; +import fr.insee.genesis.controller.utils.DateTimeUtils; import fr.insee.genesis.domain.model.surveyunit.Mode; import fr.insee.genesis.domain.model.surveyunit.rawdata.DataProcessResult; import fr.insee.genesis.domain.model.surveyunit.rawdata.LunaticJsonRawDataModel; @@ -14,6 +15,7 @@ import fr.insee.modelefiliere.RawResponseDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -199,15 +201,44 @@ public ResponseEntity>> getProcessedDataIdsSinceHours( @PreAuthorize("hasRole('USER_BATCH_GENERIC')") public ResponseEntity> getLunaticJsonRawDataModelFromJsonBody( @PathVariable String campaignId, - @RequestParam(value = "startDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant startDate, - @RequestParam(value = "endDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant endDate, + @RequestParam(value = "startDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Start date in UTC", example = "2026-02-02T00:00:00Z") + Instant startDate, + @RequestParam(value = "localStartDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Extract since in Europe/Paris timezone", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localStartDate, + @RequestParam(value = "endDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "End date in UTC", example = "2026-02-02T00:00:00Z") + Instant endDate, + @RequestParam(value = "localEndDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Extract until in Europe/Paris timezone", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localEndDate, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "1000") int size ) { - log.info("Try to read raw JSONs for campaign {}, with startDate={} and endDate={} - page={} - size={}", campaignId, startDate, endDate,page,size); + Instant resolvedStartDate = DateTimeUtils.resolveInstant(startDate, localStartDate); + Instant resolvedEndDate = DateTimeUtils.resolveInstant(endDate, localEndDate); + log.info( + "Try to read raw JSONs for campaign {} with startDateUtc={} startDateLocal={} endDateUtc={} endDateLocal={} - page={} - size={}", + campaignId, + resolvedStartDate, + DateTimeUtils.toFranceDateTime(resolvedStartDate), + resolvedEndDate, + DateTimeUtils.toFranceDateTime(resolvedEndDate), + page, + size + ); Pageable pageable = PageRequest.of(page, size); - Page rawResponses = lunaticJsonRawDataApiPort.findRawDataByCampaignIdAndDate(campaignId, startDate, endDate, pageable); - log.info("rawResponses, lunatic-json for campaign {}, with startDate={} and endDate={} ={}", campaignId, startDate, endDate,rawResponses.getContent().size()); + Page rawResponses = lunaticJsonRawDataApiPort.findRawDataByCampaignIdAndDate(campaignId, resolvedStartDate, resolvedEndDate, pageable); + log.info( + "rawResponses, lunatic-json for campaign {} with startDateUtc={} startDateLocal={} endDateUtc={} endDateLocal={} count={}", + campaignId, + resolvedStartDate, + DateTimeUtils.toFranceDateTime(resolvedStartDate), + resolvedEndDate, + DateTimeUtils.toFranceDateTime(resolvedEndDate), + rawResponses.getContent().size() + ); return ResponseEntity.status(HttpStatus.OK).body(new PagedModel<>(rawResponses)); } @@ -256,15 +287,41 @@ public ResponseEntity existsLunaticJsonByInterrogationId(@PathVariable Str @PreAuthorize("hasRole('USER_BATCH_GENERIC')") public ResponseEntity> getRawResponsesFromJsonBody( @PathVariable String campaignId, - @RequestParam(value = "startDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant startDate, - @RequestParam(value = "endDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant endDate, + @RequestParam(value = "startDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Start date in UTC", example = "2026-02-02T00:00:00Z") + Instant startDate, + @RequestParam(value = "localStartDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Extract since in Europe/Paris timezone", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localStartDate, + @RequestParam(value = "endDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "End date in UTC", example = "2026-04-02T00:00:00Z") + Instant endDate, + @RequestParam(value = "localEndDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Extract until in Europe/Paris timezone", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localEndDate, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "1000") int size ) { - log.info("Try to read raw lunatic JSONs for campaign {}, with startDate={} and endDate={} - page={} - size={}", campaignId, startDate, endDate,page,size); + Instant resolvedStartDate = DateTimeUtils.resolveInstant(startDate, localStartDate); + Instant resolvedEndDate = DateTimeUtils.resolveInstant(endDate, localEndDate); + log.info("Try to read raw JSONs for campaign {} with startDateUtc={} startDateLocal={} endDateUtc={} endDateLocal={} - page={} - size={}", + campaignId, + resolvedStartDate, + DateTimeUtils.toFranceDateTime(resolvedStartDate), + resolvedEndDate, + DateTimeUtils.toFranceDateTime(resolvedEndDate), + page, + size + ); Pageable pageable = PageRequest.of(page, size); - Page rawResponses = rawResponseApiPort.findRawResponseDataByCampaignIdAndDate(campaignId, startDate, endDate, pageable); - log.info("rawResponses for campaign {}, with startDate={} and endDate={} ={}",campaignId, startDate, endDate, rawResponses.getContent().size()); + Page rawResponses = rawResponseApiPort.findRawResponseDataByCampaignIdAndDate(campaignId, resolvedStartDate, resolvedEndDate, pageable); + log.info("rawResponses for campaign {},with startDateUtc={} startDateLocal={} endDateUtc={} endDateLocal={} count={}", + campaignId, + resolvedStartDate, + DateTimeUtils.toFranceDateTime(resolvedStartDate), + resolvedEndDate, + DateTimeUtils.toFranceDateTime(resolvedEndDate), + rawResponses.getContent().size()); return ResponseEntity.status(HttpStatus.OK).body(new PagedModel<>(rawResponses)); } diff --git a/src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseReprocessController.java b/src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseReprocessController.java index 2f9d87250..ce20341ee 100644 --- a/src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseReprocessController.java +++ b/src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseReprocessController.java @@ -1,5 +1,6 @@ package fr.insee.genesis.controller.rest.responses; +import fr.insee.genesis.controller.utils.DateTimeUtils; import fr.insee.genesis.domain.model.surveyunit.rawdata.DataProcessResult; import fr.insee.genesis.domain.model.surveyunit.rawdata.RawDataModelType; import fr.insee.genesis.domain.ports.api.ReprocessRawResponseApiPort; @@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestParam; import java.time.Instant; +import java.time.LocalDateTime; @Controller @RequiredArgsConstructor @@ -44,20 +46,33 @@ public ResponseEntity reProcessRawResponsesByCollectionInstrumentId( @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant sinceDate, + @RequestParam(value = "localSinceDate", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Extract since in Europe/Paris timezone", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localSinceDate, + @Parameter( description = "Extract until", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T00:00:00Z") ) @RequestParam(value = "endDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - Instant endDate + Instant endDate, + + @RequestParam(value = "localEndDate", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Extract until in Europe/Paris timezone", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localEndDate ) throws GenesisException { + Instant resolvedSinceDate = DateTimeUtils.resolveInstant(sinceDate, localSinceDate); + Instant resolvedEndDate = DateTimeUtils.resolveInstant(endDate, localEndDate); + DataProcessResult result = reprocessRawResponseApiPort.reprocessRawResponses( RawDataModelType.FILIERE, collectionInstrumentId, - sinceDate, - endDate); + resolvedSinceDate, + resolvedEndDate); return ResponseEntity.ok(result.message(collectionInstrumentId)); } @@ -81,20 +96,33 @@ public ResponseEntity reProcessJsonRawDataByQuestionnaireId( @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant sinceDate, + @RequestParam(value = "localSinceDate", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Extract since in Europe/Paris timezone", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localSinceDate, + @Parameter( description = "Extract until", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T00:00:00Z") ) @RequestParam(value = "endDate", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - Instant endDate + Instant endDate, + + @RequestParam(value = "localEndDate", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + @Parameter(description = "Extract until in Europe/Paris timezone", schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T01:00:00")) + LocalDateTime localEndDate ) throws GenesisException { + Instant resolvedSinceDate = DateTimeUtils.resolveInstant(sinceDate, localSinceDate); + Instant resolvedEndDate = DateTimeUtils.resolveInstant(endDate, localEndDate); + DataProcessResult result = reprocessRawResponseApiPort.reprocessRawResponses( RawDataModelType.LEGACY, collectionInstrumentId, - sinceDate, - endDate); + resolvedSinceDate, + resolvedEndDate); return ResponseEntity.ok(result.message(collectionInstrumentId)); } diff --git a/src/main/java/fr/insee/genesis/controller/rest/responses/ResponseController.java b/src/main/java/fr/insee/genesis/controller/rest/responses/ResponseController.java index eba3557f4..2735334cf 100644 --- a/src/main/java/fr/insee/genesis/controller/rest/responses/ResponseController.java +++ b/src/main/java/fr/insee/genesis/controller/rest/responses/ResponseController.java @@ -19,6 +19,7 @@ import fr.insee.genesis.controller.utils.AuthUtils; import fr.insee.genesis.controller.utils.ControllerUtils; import fr.insee.genesis.controller.utils.DataTransformer; +import fr.insee.genesis.controller.utils.DateTimeUtils; import fr.insee.genesis.domain.model.context.DataProcessingContextModel; import fr.insee.genesis.domain.model.surveyunit.InterrogationId; import fr.insee.genesis.domain.model.surveyunit.Mode; @@ -39,6 +40,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -420,13 +422,27 @@ public ResponseEntity> searchResponses( schema = @Schema(type = "string", format = "date-time", example = "2026-01-01T00:00:00Z") ) @RequestParam(value = "recordedBefore", required = false) Instant recordedBefore, + @Parameter( + description = "Filter responses recorded before or at the same time of the given timestamp in Europe/Paris local time", + schema = @Schema( + type = "string", + format = "date-time", + example = "2026-01-01T01:00:00" + ) + ) + @RequestParam(value = "localRecordedBefore", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + LocalDateTime localRecordedBefore, @RequestBody List interrogationIds) { + Instant resolvedRecordedBefore = DateTimeUtils.resolveInstant( + recordedBefore, + localRecordedBefore + ); return ResponseEntity.ok( surveyUnitService.findSimplifiedList( collectionInstrumentId, interrogationIds, - recordedBefore + resolvedRecordedBefore ) ); } diff --git a/src/main/java/fr/insee/genesis/controller/sources/xml/LunaticXmlDataParser.java b/src/main/java/fr/insee/genesis/controller/sources/xml/LunaticXmlDataParser.java index f09e3f4d9..5250ec43a 100644 --- a/src/main/java/fr/insee/genesis/controller/sources/xml/LunaticXmlDataParser.java +++ b/src/main/java/fr/insee/genesis/controller/sources/xml/LunaticXmlDataParser.java @@ -248,7 +248,7 @@ private static void setValues(LunaticXmlCollectedData varData, Node value, List< varData.setPrevious(valueTypes); break; default: - throw new GenesisException(HttpStatus.LOCKED, "Tag not recognized: " + valueElement.getTagName()); + throw new GenesisException(HttpStatus.DESTINATION_LOCKED, "Tag not recognized: " + valueElement.getTagName()); } } diff --git a/src/main/java/fr/insee/genesis/controller/utils/DateTimeUtils.java b/src/main/java/fr/insee/genesis/controller/utils/DateTimeUtils.java new file mode 100644 index 000000000..b3a8b73a2 --- /dev/null +++ b/src/main/java/fr/insee/genesis/controller/utils/DateTimeUtils.java @@ -0,0 +1,43 @@ +package fr.insee.genesis.controller.utils; + +import fr.insee.genesis.exceptions.InvalidDateIntervalException; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +public final class DateTimeUtils { + + private static final ZoneId FRANCE_ZONE = ZoneId.of("Europe/Paris"); + + private DateTimeUtils() { + } + + public static Instant resolveInstant( + Instant utcDate, + LocalDateTime localDate + ) { + if (utcDate != null && localDate != null) { + throw new InvalidDateIntervalException( + "Use either UTC date or local date, not both" + ); + } + + if (localDate != null) { + return localDate + .atZone(FRANCE_ZONE) + .toInstant(); + } + + return utcDate; + } + + public static ZonedDateTime toFranceDateTime(Instant instant) { + if (instant == null) { + return null; + } + + return instant.atZone(FRANCE_ZONE); + } +} diff --git a/src/main/java/fr/insee/genesis/controller/utils/ExtendedJsonNormalizer.java b/src/main/java/fr/insee/genesis/controller/utils/ExtendedJsonNormalizer.java index 58226bfc4..6bb1c4bdf 100644 --- a/src/main/java/fr/insee/genesis/controller/utils/ExtendedJsonNormalizer.java +++ b/src/main/java/fr/insee/genesis/controller/utils/ExtendedJsonNormalizer.java @@ -1,9 +1,9 @@ package fr.insee.genesis.controller.utils; -import tools.jackson.databind.node.StringNode; -import tools.jackson.databind.JsonNode; -import tools.jackson.databind.node.ArrayNode; -import tools.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; public class ExtendedJsonNormalizer { public static final String $_DATE = "$date"; @@ -23,8 +23,8 @@ public static JsonNode normalize(JsonNode node) { ObjectNode obj = (ObjectNode) node; if (obj.size() == 1) { - if (obj.has($_DATE) && obj.get($_DATE).isString()) { - return StringNode.valueOf(obj.get($_DATE).asString()); + if (obj.has($_DATE) && obj.get($_DATE).isTextual()) { + return TextNode.valueOf(obj.get($_DATE).asText()); } // if (obj.has("$oid") && obj.get("$oid").isTextual()) { // return TextNode.valueOf(obj.get("$oid").asText()); @@ -32,11 +32,9 @@ public static JsonNode normalize(JsonNode node) { } ObjectNode copy = obj.objectNode(); - - obj.properties().forEach(e -> + obj.fields().forEachRemaining(e -> copy.set(e.getKey(), normalize(e.getValue())) ); - return copy; } diff --git a/src/main/java/fr/insee/genesis/domain/converter/rawdata/RawResponseConverter.java b/src/main/java/fr/insee/genesis/domain/converter/rawdata/RawResponseConverter.java index 199865827..63c10d8a5 100644 --- a/src/main/java/fr/insee/genesis/domain/converter/rawdata/RawResponseConverter.java +++ b/src/main/java/fr/insee/genesis/domain/converter/rawdata/RawResponseConverter.java @@ -164,19 +164,16 @@ private static void convertListVar( VariablesMap variablesMap, List destination ) { - if (!(valuesForState instanceof List values)) { - throw new IllegalArgumentException("Object is not a List"); - } - - int iteration = 1; - for (Object rawValue : values) { - String value = rawValue == null ? null : getValueString(rawValue); + List values = JsonUtils.asStringList(valuesForState); - if (value != null && !value.isEmpty()) { - convertOneVar(variableEntry, value, variablesMap, iteration, destination); + if (!values.isEmpty()) { + int iteration = 1; + for (String value : values) { + if (value != null && !value.isEmpty()) { + convertOneVar(variableEntry, value, variablesMap, iteration, destination); + } + iteration++; } - - iteration++; } } diff --git a/src/main/java/fr/insee/genesis/domain/service/context/DataProcessingContextService.java b/src/main/java/fr/insee/genesis/domain/service/context/DataProcessingContextService.java index b0cb90801..d257bf97b 100644 --- a/src/main/java/fr/insee/genesis/domain/service/context/DataProcessingContextService.java +++ b/src/main/java/fr/insee/genesis/domain/service/context/DataProcessingContextService.java @@ -1,6 +1,7 @@ package fr.insee.genesis.domain.service.context; -import tools.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import fr.insee.genesis.Constants; import fr.insee.genesis.controller.dto.KraftwerkExecutionScheduleInput; import fr.insee.genesis.controller.dto.rawdata.ScheduleResponseDto; @@ -282,9 +283,8 @@ public void deleteExpiredSchedules(String logFolder) throws GenesisException { scheduleName + ".json" ); - JsonMapper objectMapper = JsonMapper.builder() - .findAndAddModules() - .build(); + ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); + objectMapper.registerModule(new JavaTimeModule()); String jsonToWrite = objectMapper.writeValueAsString(deletedSchedules); diff --git a/src/main/java/fr/insee/genesis/domain/service/contextualvariable/external/ContextualExternalVariableJsonService.java b/src/main/java/fr/insee/genesis/domain/service/contextualvariable/external/ContextualExternalVariableJsonService.java index 93acc58ba..140a367bf 100644 --- a/src/main/java/fr/insee/genesis/domain/service/contextualvariable/external/ContextualExternalVariableJsonService.java +++ b/src/main/java/fr/insee/genesis/domain/service/contextualvariable/external/ContextualExternalVariableJsonService.java @@ -1,10 +1,9 @@ package fr.insee.genesis.domain.service.contextualvariable.external; -import fr.insee.genesis.exceptions.JsonParsingException; -import tools.jackson.core.JacksonException; -import tools.jackson.core.JsonParser; -import tools.jackson.core.json.JsonFactory; -import tools.jackson.core.JsonToken; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import fr.insee.genesis.domain.model.contextualvariable.ContextualExternalVariableModel; import fr.insee.genesis.domain.ports.api.ContextualExternalVariableApiPort; import fr.insee.genesis.domain.ports.spi.ContextualExternalVariablePersistancePort; @@ -75,7 +74,7 @@ public boolean readContextualExternalFile(String collectionInstrumentId, String contextualExternalVariablePersistancePort.deleteBackup(collectionInstrumentId); return true; } - }catch (JacksonException jpe){ + }catch (JsonParseException jpe){ contextualExternalVariablePersistancePort.restoreBackup(collectionInstrumentId); throw new GenesisException(HttpStatus.BAD_REQUEST, "JSON Parsing exception : %s".formatted(jpe.toString())); }catch (IOException ioe){ @@ -91,7 +90,7 @@ public ContextualExternalVariableModel findByCollectionInstrumentIdAndInterrogat private static boolean goToContextualExternalToken(JsonParser jsonParser) throws IOException { while (jsonParser.nextToken() != null) { - if (jsonParser.currentToken() == JsonToken.PROPERTY_NAME + if (jsonParser.currentToken() == JsonToken.FIELD_NAME && "editedExternal".equals(jsonParser.currentName())) { return true; } @@ -128,7 +127,7 @@ private ContextualExternalVariableModel readNextContextualExternal(JsonParser js String collectionInstrumentId ) throws IOException { if(jsonParser.currentToken() != JsonToken.START_OBJECT){ - throw new JsonParsingException("Expected { on line %d, got token %s".formatted(jsonParser.currentLocation().getLineNr(), jsonParser.currentToken())); + throw new JsonParseException("Expected { on line %d, got token %s".formatted(jsonParser.currentLocation().getLineNr(), jsonParser.currentToken())); } ContextualExternalVariableModel contextualExternalVariableModel = ContextualExternalVariableModel.builder() .collectionInstrumentId(collectionInstrumentId) @@ -136,7 +135,7 @@ private ContextualExternalVariableModel readNextContextualExternal(JsonParser js .build(); jsonParser.nextToken(); while (!jsonParser.currentToken().equals(JsonToken.END_OBJECT)){ - if(jsonParser.currentToken().equals(JsonToken.PROPERTY_NAME) && jsonParser.currentName().equals("interrogationId")){ + if(jsonParser.currentToken().equals(JsonToken.FIELD_NAME) && jsonParser.currentName().equals("interrogationId")){ jsonParser.nextToken(); contextualExternalVariableModel.setInterrogationId(jsonParser.getText()); jsonParser.nextToken(); diff --git a/src/main/java/fr/insee/genesis/domain/service/contextualvariable/previous/ContextualPreviousVariableJsonService.java b/src/main/java/fr/insee/genesis/domain/service/contextualvariable/previous/ContextualPreviousVariableJsonService.java index 4a7cd4c35..2599d843f 100644 --- a/src/main/java/fr/insee/genesis/domain/service/contextualvariable/previous/ContextualPreviousVariableJsonService.java +++ b/src/main/java/fr/insee/genesis/domain/service/contextualvariable/previous/ContextualPreviousVariableJsonService.java @@ -1,10 +1,9 @@ package fr.insee.genesis.domain.service.contextualvariable.previous; -import tools.jackson.core.JacksonException; -import tools.jackson.core.json.JsonFactory; -import fr.insee.genesis.exceptions.JsonParsingException; -import tools.jackson.core.JsonParser; -import tools.jackson.core.JsonToken; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import fr.insee.genesis.domain.model.contextualvariable.ContextualPreviousVariableModel; import fr.insee.genesis.domain.ports.api.ContextualPreviousVariableApiPort; import fr.insee.genesis.domain.ports.spi.ContextualPreviousVariablePersistancePort; @@ -42,7 +41,7 @@ public boolean readContextualPreviousFile(String collectionInstrumentId, try(FileInputStream inputStream = new FileInputStream(filePath)){ checkSourceStateLength(sourceState); - JsonFactory jsonFactory = JsonFactory.builder().build(); + JsonFactory jsonFactory = new JsonFactory(); try (JsonParser jsonParser = jsonFactory.createParser(inputStream)) { if (!goToEditedPreviousToken(jsonParser)) { log.warn("No EditedPrevious part found in file {}", filePath); @@ -80,7 +79,7 @@ public boolean readContextualPreviousFile(String collectionInstrumentId, contextualPreviousVariablePersistancePort.deleteBackup(collectionInstrumentId); return true; } - }catch (JacksonException jpe){ + }catch (JsonParseException jpe){ contextualPreviousVariablePersistancePort.restoreBackup(collectionInstrumentId); throw new GenesisException(HttpStatus.BAD_REQUEST, "JSON Parsing exception : %s".formatted(jpe.toString())); }catch (IOException _){ @@ -117,8 +116,8 @@ private static void checkSourceStateLength(String sourceState) throws GenesisExc private boolean goToEditedPreviousToken(JsonParser jsonParser) throws IOException { while (jsonParser.nextToken() != null) { - if (jsonParser.currentToken() == JsonToken.PROPERTY_NAME - && "editedPrevious".equals(jsonParser.currentName())) { + if (jsonParser.currentToken() == JsonToken.FIELD_NAME + && "editedPrevious".equals(jsonParser.getCurrentName())) { return true; } } @@ -130,7 +129,7 @@ private ContextualPreviousVariableModel readNextContextualPrevious(JsonParser js String sourceState ) throws IOException { if(jsonParser.currentToken() != JsonToken.START_OBJECT){ - throw new JsonParsingException("Expected { on line %d, got token %s".formatted(jsonParser.currentLocation().getLineNr(), jsonParser.currentToken())); + throw new JsonParseException("Expected { on line %d, got token %s".formatted(jsonParser.currentLocation().getLineNr(), jsonParser.currentToken())); } ContextualPreviousVariableModel contextualPreviousVariableModel = ContextualPreviousVariableModel.builder() .collectionInstrumentId(collectionInstrumentId) @@ -139,7 +138,7 @@ private ContextualPreviousVariableModel readNextContextualPrevious(JsonParser js .build(); jsonParser.nextToken(); while (!jsonParser.currentToken().equals(JsonToken.END_OBJECT)){ - if(jsonParser.currentToken().equals(JsonToken.PROPERTY_NAME) && jsonParser.currentName().equals("interrogationId")){ + if(jsonParser.currentToken().equals(JsonToken.FIELD_NAME) && jsonParser.currentName().equals("interrogationId")){ jsonParser.nextToken(); contextualPreviousVariableModel.setInterrogationId(jsonParser.getText()); jsonParser.nextToken(); @@ -175,7 +174,7 @@ private Object readValue(JsonParser jsonParser) throws IOException{ case START_ARRAY -> { return readArray(jsonParser); } - case null, default -> throw new JsonParsingException("Unexpected token %s on line %d".formatted( + case null, default -> throw new JsonParseException("Unexpected token %s on line %d".formatted( jsonParser.currentToken(), jsonParser.currentLocation().getLineNr()) ); } diff --git a/src/main/java/fr/insee/genesis/domain/utils/JsonUtils.java b/src/main/java/fr/insee/genesis/domain/utils/JsonUtils.java index add5531da..e2b91c803 100644 --- a/src/main/java/fr/insee/genesis/domain/utils/JsonUtils.java +++ b/src/main/java/fr/insee/genesis/domain/utils/JsonUtils.java @@ -1,11 +1,11 @@ package fr.insee.genesis.domain.utils; -import fr.insee.genesis.exceptions.JsonParsingException; -import tools.jackson.core.JsonParser; -import tools.jackson.core.JacksonException; -import tools.jackson.core.JsonToken; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.experimental.UtilityClass; -import tools.jackson.databind.json.JsonMapper; import java.io.IOException; import java.util.ArrayList; @@ -14,11 +14,9 @@ @UtilityClass public class JsonUtils { - JsonMapper objectMapper = JsonMapper.builder() - .findAndAddModules() - .build(); + private static final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); - public static Map jsonToMap(String json) throws JacksonException { + public static Map jsonToMap(String json) throws JsonProcessingException { return objectMapper.readValue(json, Map.class); } @@ -56,7 +54,7 @@ public static Object readValue(JsonParser jsonParser) throws IOException { case START_ARRAY -> { return readArray(jsonParser); } - case null, default -> throw new JsonParsingException("Unexpected token %s on line %d".formatted( + case null, default -> throw new JsonParseException("Unexpected token %s on line %d".formatted( jsonParser.currentToken(), jsonParser.currentLocation().getLineNr()) ); } diff --git a/src/main/java/fr/insee/genesis/exceptions/JsonParsingException.java b/src/main/java/fr/insee/genesis/exceptions/JsonParsingException.java deleted file mode 100644 index b42d78a40..000000000 --- a/src/main/java/fr/insee/genesis/exceptions/JsonParsingException.java +++ /dev/null @@ -1,9 +0,0 @@ -package fr.insee.genesis.exceptions; - -import java.io.IOException; - -public class JsonParsingException extends IOException { - public JsonParsingException(String message) { - super(message); - } -} diff --git a/src/main/java/fr/insee/genesis/infrastructure/adapter/SurveyUnitMongoAdapter.java b/src/main/java/fr/insee/genesis/infrastructure/adapter/SurveyUnitMongoAdapter.java index 99a01737a..f74b8d18a 100644 --- a/src/main/java/fr/insee/genesis/infrastructure/adapter/SurveyUnitMongoAdapter.java +++ b/src/main/java/fr/insee/genesis/infrastructure/adapter/SurveyUnitMongoAdapter.java @@ -1,7 +1,8 @@ package fr.insee.genesis.infrastructure.adapter; -import tools.jackson.core.JacksonException; -import tools.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.mongodb.client.MongoCollection; import fr.insee.genesis.Constants; import fr.insee.genesis.domain.model.surveyunit.InterrogationInfo; @@ -18,7 +19,6 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Service; -import tools.jackson.databind.json.JsonMapper; import java.time.Instant; import java.time.LocalDateTime; @@ -142,13 +142,11 @@ public long count() { private static @NotNull Set extractQuestionnaireIdsFromJson(Set mongoResponse) { Set questionnaireIds = new HashSet<>(); for(String line : mongoResponse){ - JsonMapper objectMapper = JsonMapper.builder() - .findAndAddModules() - .build(); + ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); try{ JsonNode jsonNode = objectMapper.readTree(line); questionnaireIds.add(jsonNode.get(QUESTIONNAIRE_ID).asText()); - }catch (JacksonException e){ + }catch (JsonProcessingException e){ log.error(e.getMessage()); } } diff --git a/src/main/java/fr/insee/genesis/infrastructure/document/surveyunit/SurveyUnitDocument.java b/src/main/java/fr/insee/genesis/infrastructure/document/surveyunit/SurveyUnitDocument.java index cdf2b7b9d..c51d9cfe7 100644 --- a/src/main/java/fr/insee/genesis/infrastructure/document/surveyunit/SurveyUnitDocument.java +++ b/src/main/java/fr/insee/genesis/infrastructure/document/surveyunit/SurveyUnitDocument.java @@ -1,5 +1,6 @@ package fr.insee.genesis.infrastructure.document.surveyunit; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import fr.insee.genesis.Constants; import fr.insee.modelefiliere.RawResponseDto; diff --git a/src/main/java/fr/insee/genesis/infrastructure/repository/RawResponseInputRepository.java b/src/main/java/fr/insee/genesis/infrastructure/repository/RawResponseInputRepository.java index fac05f343..3b324e87e 100644 --- a/src/main/java/fr/insee/genesis/infrastructure/repository/RawResponseInputRepository.java +++ b/src/main/java/fr/insee/genesis/infrastructure/repository/RawResponseInputRepository.java @@ -1,7 +1,7 @@ package fr.insee.genesis.infrastructure.repository; -import tools.jackson.core.JacksonException; -import tools.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import fr.insee.modelefiliere.RawResponseDto; import lombok.RequiredArgsConstructor; import org.bson.Document; @@ -35,7 +35,7 @@ public void saveAsRawJson(RawResponseDto dto) { document.put("recordDate", Instant.now()); document.put("payload", payload); mongoTemplate.save(document, "rawResponses"); - } catch (JacksonException e) { + } catch (JsonProcessingException e) { throw new RuntimeException(e); } diff --git a/src/main/java/fr/insee/genesis/infrastructure/utils/FileUtils.java b/src/main/java/fr/insee/genesis/infrastructure/utils/FileUtils.java index 445f61858..1e6952291 100644 --- a/src/main/java/fr/insee/genesis/infrastructure/utils/FileUtils.java +++ b/src/main/java/fr/insee/genesis/infrastructure/utils/FileUtils.java @@ -1,5 +1,6 @@ package fr.insee.genesis.infrastructure.utils; +import com.fasterxml.jackson.databind.ObjectMapper; import fr.insee.genesis.Constants; import fr.insee.genesis.configuration.Config; import fr.insee.genesis.domain.model.surveyunit.Mode; @@ -9,13 +10,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; -import tools.jackson.databind.json.JsonMapper; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.UncheckedIOException; +import java.io.*; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; @@ -230,9 +226,8 @@ public void writeFile(Path filePath, String fileContent) { */ public void writeSuUpdatesInFile(Path filePath, Stream responsesStream) throws IOException { Files.createDirectories(filePath.getParent()); - JsonMapper objectMapper = JsonMapper.builder() - .findAndAddModules() - .build(); + ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); + objectMapper.findAndRegisterModules(); try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath.toFile(), true))) { writer.write("["); responsesStream.forEach(response -> { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a8cd56ab0..15d29f604 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -45,6 +45,6 @@ management.endpoint.health.show-details=always #-------------------------------------------------------------------------- # Database MongoDB #-------------------------------------------------------------------------- -spring.mongodb.auto-index-creation=true -spring.mongodb.uri=mongodb://${fr.insee.genesis.persistence.database.mongodb.username}:${fr.insee.genesis.persistence.database.mongodb.password}@${fr.insee.genesis.persistence.database.mongodb.host1}:${fr.insee.genesis.persistence.database.mongodb.port},${fr.insee.genesis.persistence.database.mongodb.host2}:${fr.insee.genesis.persistence.database.mongodb.port},${fr.insee.genesis.persistence.database.mongodb.host3}:${fr.insee.genesis.persistence.database.mongodb.port}/${fr.insee.genesis.persistence.database.mongodb.database} +spring.data.mongodb.auto-index-creation=true +spring.data.mongodb.uri=mongodb://${fr.insee.genesis.persistence.database.mongodb.username}:${fr.insee.genesis.persistence.database.mongodb.password}@${fr.insee.genesis.persistence.database.mongodb.host1}:${fr.insee.genesis.persistence.database.mongodb.port},${fr.insee.genesis.persistence.database.mongodb.host2}:${fr.insee.genesis.persistence.database.mongodb.port},${fr.insee.genesis.persistence.database.mongodb.host3}:${fr.insee.genesis.persistence.database.mongodb.port}/${fr.insee.genesis.persistence.database.mongodb.database} server.compression.enabled=true \ No newline at end of file diff --git a/src/test/java/fr/insee/genesis/controller/IntegrationTestAbstract.java b/src/test/java/fr/insee/genesis/controller/IntegrationTestAbstract.java index 338e14f00..27e255f50 100644 --- a/src/test/java/fr/insee/genesis/controller/IntegrationTestAbstract.java +++ b/src/test/java/fr/insee/genesis/controller/IntegrationTestAbstract.java @@ -14,10 +14,10 @@ import fr.insee.genesis.infrastructure.repository.SurveyUnitMongoDBRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.data.mongodb.autoconfigure.DataMongoAutoConfiguration; -import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; @@ -34,7 +34,7 @@ @TestPropertySource(properties = { "logging.level.=DEBUG" }) -@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, DataMongoAutoConfiguration.class}) +@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) public abstract class IntegrationTestAbstract { @Autowired protected MockMvc mockMvc; diff --git a/src/test/java/fr/insee/genesis/controller/rest/DataProcessingContextControllerTest.java b/src/test/java/fr/insee/genesis/controller/rest/DataProcessingContextControllerTest.java index e5d595cc4..40abea177 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/DataProcessingContextControllerTest.java +++ b/src/test/java/fr/insee/genesis/controller/rest/DataProcessingContextControllerTest.java @@ -1,7 +1,7 @@ package fr.insee.genesis.controller.rest; -import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; -import tools.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import fr.insee.genesis.controller.dto.KraftwerkExecutionScheduleInput; import fr.insee.genesis.controller.dto.ScheduleRequestDto; import fr.insee.genesis.controller.dto.rawdata.ScheduleResponseDto; @@ -20,12 +20,12 @@ import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; -import tools.jackson.databind.json.JsonMapper; import java.time.LocalDateTime; import java.util.List; @@ -68,9 +68,8 @@ class DataProcessingContextControllerTest { @BeforeEach void setUp() { - objectMapper = JsonMapper.builder() - .findAndAddModules() - .build(); + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); } @Nested diff --git a/src/test/java/fr/insee/genesis/controller/rest/HealthCheckControllerTest.java b/src/test/java/fr/insee/genesis/controller/rest/HealthCheckControllerTest.java index 87cd2daa3..5a19d42f2 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/HealthCheckControllerTest.java +++ b/src/test/java/fr/insee/genesis/controller/rest/HealthCheckControllerTest.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.bean.override.mockito.MockitoBean; diff --git a/src/test/java/fr/insee/genesis/controller/rest/LunaticModelControllerIT.java b/src/test/java/fr/insee/genesis/controller/rest/LunaticModelControllerIT.java index 69687c07e..8f0f22558 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/LunaticModelControllerIT.java +++ b/src/test/java/fr/insee/genesis/controller/rest/LunaticModelControllerIT.java @@ -16,11 +16,12 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; import org.springframework.data.mongodb.core.ExecutableUpdateOperation; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.data.mongodb.core.query.Update; import org.springframework.http.MediaType; -import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import java.nio.file.Files; @@ -38,7 +39,6 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -46,13 +46,13 @@ class LunaticModelControllerIT extends IntegrationTestAbstract { - @MockitoBean + @Mock private ExecutableUpdateOperation.ExecutableUpdate executableUpdate; - @MockitoBean + @Mock private ExecutableUpdateOperation.UpdateWithUpdate updateWithUpdate; - @MockitoBean + @Mock private ExecutableUpdateOperation.TerminatingUpdate terminatingUpdate; @MockitoSpyBean @@ -76,6 +76,7 @@ class SaveLunaticModelTests{ "specs/LUNATIC-TEST/lunaticlog2021x21_web.json", "specs/RAWDATATESTCAMPAIGN/WEB/lunaticFAM2025X01.json" }) + @WithMockUser(roles = "USER_BACK_OFFICE") @DisplayName("Lunatic model saving test") @SneakyThrows void save_lunaticModel_test(String jsonFilePathString){ @@ -88,7 +89,6 @@ void save_lunaticModel_test(String jsonFilePathString){ //WHEN mockMvc.perform(put("/lunatic-model/save") - .with(user("test").roles("USER_BACK_OFFICE")) .with(csrf()) .param("questionnaireId", questionnaireId) .contentType(MediaType.APPLICATION_JSON) @@ -131,6 +131,7 @@ void save_lunaticModel_test(String jsonFilePathString){ //SAD PATH @Test + @WithMockUser(roles = "USER_BACK_OFFICE") @DisplayName("Lunatic model save invalid syntax") @SneakyThrows void save_lunaticModel_syntax_error_test(){ @@ -143,7 +144,6 @@ void save_lunaticModel_syntax_error_test(){ //WHEN + THEN mockMvc.perform(put("/lunatic-model/save") - .with(user("test").roles("USER_BACK_OFFICE")) .with(csrf()) .param("questionnaireId", questionnaireId) .contentType(MediaType.APPLICATION_JSON) @@ -159,6 +159,7 @@ class GetLunaticModelTests{ //HAPPY PATH @Test @DisplayName("Get Lunatic model test") + @WithMockUser(roles = "READER") @SneakyThrows void get_lunaticModel_test(){ //GIVEN @@ -179,7 +180,6 @@ void get_lunaticModel_test(){ //WHEN + THEN mockMvc.perform(get("/lunatic-model/get") - .with(user("test").roles("READER")) .with(csrf()) .param("questionnaireId", collectionInstrumentId)) .andExpect(status().isOk()) @@ -189,12 +189,14 @@ void get_lunaticModel_test(){ //BAD PATHS @Test @DisplayName("Get non existent Lunatic model") + @WithMockUser(roles = "READER") @SneakyThrows - void get_lunaticModel_not_found_test() { + void get_lunaticModel_not_found_test(){ + //GIVEN String collectionInstrumentId = "collectionInstrumentId"; + //WHEN + THEN mockMvc.perform(get("/lunatic-model/get") - .with(user("test").roles("READER")) .with(csrf()) .param("questionnaireId", collectionInstrumentId)) .andExpect(status().isNotFound()); diff --git a/src/test/java/fr/insee/genesis/controller/rest/LunaticModelControllerTest.java b/src/test/java/fr/insee/genesis/controller/rest/LunaticModelControllerTest.java index 09d688b50..5ab280608 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/LunaticModelControllerTest.java +++ b/src/test/java/fr/insee/genesis/controller/rest/LunaticModelControllerTest.java @@ -1,6 +1,6 @@ package fr.insee.genesis.controller.rest; -import tools.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectMapper; import fr.insee.genesis.domain.model.lunaticmodel.LunaticModelModel; import fr.insee.genesis.domain.ports.api.LunaticModelApiPort; import lombok.SneakyThrows; @@ -11,7 +11,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.ResponseEntity; -import tools.jackson.databind.json.JsonMapper; import java.time.LocalDateTime; import java.util.HashMap; @@ -51,9 +50,7 @@ void saveRawResponsesFromJsonBody() { void getLunaticModelFromQuestionnaireId() { //GIVEN String questionnaireId = "test"; - JsonMapper objectMapper = JsonMapper.builder() - .findAndAddModules() - .build(); + ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); Map lunaticModel = new HashMap<>(); String expected = objectMapper.writeValueAsString(lunaticModel); doReturn(LunaticModelModel.builder() diff --git a/src/test/java/fr/insee/genesis/controller/rest/responses/InterrogationControllerTest.java b/src/test/java/fr/insee/genesis/controller/rest/responses/InterrogationControllerTest.java index 7f79f0145..f885a79f0 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/responses/InterrogationControllerTest.java +++ b/src/test/java/fr/insee/genesis/controller/rest/responses/InterrogationControllerTest.java @@ -67,7 +67,8 @@ void getAllInterrogationIdsByQuestionnaire_date_test() { interrogationController.getAllInterrogationIdsByQuestionnaire( TestConstants.DEFAULT_COLLECTION_INSTRUMENT_ID, since, - null + null, + null,null ); //THEN diff --git a/src/test/java/fr/insee/genesis/controller/rest/responses/RawResponseControllerIT.java b/src/test/java/fr/insee/genesis/controller/rest/responses/RawResponseControllerIT.java index 782e87c7b..db58fdfb3 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/responses/RawResponseControllerIT.java +++ b/src/test/java/fr/insee/genesis/controller/rest/responses/RawResponseControllerIT.java @@ -1,7 +1,7 @@ package fr.insee.genesis.controller.rest.responses; -import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import fr.insee.bpm.metadata.model.MetadataModel; import fr.insee.bpm.metadata.model.Variable; import fr.insee.bpm.metadata.model.VariableType; @@ -32,7 +32,6 @@ import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; -import tools.jackson.databind.json.JsonMapper; import java.nio.file.Files; import java.nio.file.Path; @@ -70,9 +69,8 @@ class RawResponseControllerIT extends IntegrationTestAbstract { @BeforeEach void init(){ - objectMapper = JsonMapper.builder() - .findAndAddModules() - .build(); + objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); } diff --git a/src/test/java/fr/insee/genesis/controller/rest/responses/RawResponseControllerTest.java b/src/test/java/fr/insee/genesis/controller/rest/responses/RawResponseControllerTest.java index b32bad06f..50dc7c36d 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/responses/RawResponseControllerTest.java +++ b/src/test/java/fr/insee/genesis/controller/rest/responses/RawResponseControllerTest.java @@ -16,10 +16,10 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.data.mongodb.autoconfigure.DataMongoAutoConfiguration; -import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration; -import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; -import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.context.annotation.Import; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; @@ -62,7 +62,7 @@ @Import({DefaultSecurityConfig.class}) @ActiveProfiles("test") @AutoConfigureMockMvc -@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, DataMongoAutoConfiguration.class}) +@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) class RawResponseControllerTest { @Autowired diff --git a/src/test/java/fr/insee/genesis/controller/rest/responses/ResponseControllerTest.java b/src/test/java/fr/insee/genesis/controller/rest/responses/ResponseControllerTest.java index 5238e827b..460ba74b5 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/responses/ResponseControllerTest.java +++ b/src/test/java/fr/insee/genesis/controller/rest/responses/ResponseControllerTest.java @@ -25,10 +25,10 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.data.mongodb.autoconfigure.DataMongoAutoConfiguration; -import org.springframework.boot.mongodb.autoconfigure.MongoAutoConfiguration; -import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; -import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.context.annotation.Import; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -38,6 +38,9 @@ import org.springframework.test.web.servlet.MockMvc; import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneId; import java.util.List; import static org.hamcrest.Matchers.containsString; @@ -62,7 +65,7 @@ @Import({DefaultSecurityConfig.class}) @ActiveProfiles("test") @AutoConfigureMockMvc -@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, DataMongoAutoConfiguration.class}) +@EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) class ResponseControllerTest { @Autowired @@ -453,6 +456,44 @@ void getResponseList_shouldDelegateToService() throws Exception { } } + @Test + @DisplayName("Should convert localRecordedBefore to UTC before calling service") + void getResponseList_withLocalRecordedBefore_shouldConvertToUtc() throws Exception { + LocalDateTime localRecordedBefore = LocalDateTime.of(2026, Month.JANUARY, 1, 1, 0); + Instant expectedUtc = localRecordedBefore + .atZone(ZoneId.of("Europe/Paris")) + .toInstant(); + + when(surveyUnitApiPort.findSimplifiedList( + anyString(), any(), any())) + .thenReturn(List.of()); + + mockMvc.perform(post("/responses/QUEST01") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .param("localRecordedBefore", "2026-01-01T01:00:00") + .content("[{\"interrogationId\":\"INTERRO01\"}]")) + .andExpect(status().isOk()); + + verify(surveyUnitApiPort).findSimplifiedList( + eq("QUEST01"), + any(), + eq(expectedUtc) + ); + } + + @Test + @DisplayName("Should return bad request when both recordedBefore and localRecordedBefore are provided") + void getResponseList_withBothRecordedBeforeParams_shouldReturnBadRequest() throws Exception { + mockMvc.perform(post("/responses/QUEST01") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .param("recordedBefore", "2026-01-01T00:00:00Z") + .param("localRecordedBefore", "2026-01-01T01:00:00") + .content("[{\"interrogationId\":\"INTERRO01\"}]")) + .andExpect(status().isBadRequest()); + } + @Nested @DisplayName("POST /responses/save-edited tests") class SaveEditedVariablesTests { diff --git a/src/test/java/fr/insee/genesis/controller/utils/DateTimeUtilsTest.java b/src/test/java/fr/insee/genesis/controller/utils/DateTimeUtilsTest.java new file mode 100644 index 000000000..f63cd12e0 --- /dev/null +++ b/src/test/java/fr/insee/genesis/controller/utils/DateTimeUtilsTest.java @@ -0,0 +1,59 @@ +package fr.insee.genesis.controller.utils; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +class DateTimeUtilsTest { + + @Test + void shouldReturnUtcDateWhenUtcDateIsProvided() { + Instant utcDate = Instant.parse("2026-06-10T12:00:00Z"); + + Instant result = DateTimeUtils.resolveInstant(utcDate, null); + + assertEquals(utcDate, result); + } + + @Test + void shouldConvertLocalDateToInstantWhenLocalDateIsProvided() { + LocalDateTime localDate = LocalDateTime.of(2026, Month.JUNE, 10, 14, 0); + + Instant result = DateTimeUtils.resolveInstant(null, localDate); + + assertEquals( + localDate.atZone(ZoneId.of("Europe/Paris")).toInstant(), + result + ); + } + + @Test + void shouldReturnNullWhenNoDateIsProvided() { + Instant result = DateTimeUtils.resolveInstant(null, null); + + assertNull(result); + } + + @Test + void shouldConvertInstantToFranceDateTime() { + Instant instant = Instant.parse("2026-06-10T12:00:00Z"); + + ZonedDateTime result = DateTimeUtils.toFranceDateTime(instant); + + assertEquals( + instant.atZone(ZoneId.of("Europe/Paris")), + result + ); + } + + @Test + void shouldReturnNullWhenInstantIsNull() { + assertNull(DateTimeUtils.toFranceDateTime(null)); + } +} \ No newline at end of file diff --git a/src/test/java/fr/insee/genesis/domain/utils/JsonUtilsTest.java b/src/test/java/fr/insee/genesis/domain/utils/JsonUtilsTest.java index 0a934d73d..01d1a880d 100644 --- a/src/test/java/fr/insee/genesis/domain/utils/JsonUtilsTest.java +++ b/src/test/java/fr/insee/genesis/domain/utils/JsonUtilsTest.java @@ -1,6 +1,6 @@ package fr.insee.genesis.domain.utils; -import tools.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; @@ -31,7 +31,7 @@ void jsonToMap_shouldThrowExceptionForInvalidJson() { String invalidJson = "{key1: value1, key2: }"; assertThatThrownBy(() -> JsonUtils.jsonToMap(invalidJson)) - .isInstanceOf(JacksonException.class); + .isInstanceOf(JsonProcessingException.class); } @Test diff --git a/src/test/java/fr/insee/genesis/infrastructure/repository/RawResponseInputRepositoryTest.java b/src/test/java/fr/insee/genesis/infrastructure/repository/RawResponseInputRepositoryTest.java index a302c6ceb..27f778331 100644 --- a/src/test/java/fr/insee/genesis/infrastructure/repository/RawResponseInputRepositoryTest.java +++ b/src/test/java/fr/insee/genesis/infrastructure/repository/RawResponseInputRepositoryTest.java @@ -1,7 +1,7 @@ package fr.insee.genesis.infrastructure.repository; -import tools.jackson.core.JacksonException; -import tools.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import fr.insee.modelefiliere.ModeDto; import fr.insee.modelefiliere.RawResponseDto; import org.junit.jupiter.api.DisplayName; @@ -49,7 +49,7 @@ class SaveAsRawJsonTests { @Test @DisplayName("Should serialize the DTO to JSON and save to the correct collection") - void saveAsRawJson_shouldSerializeAndSave() throws JacksonException { + void saveAsRawJson_shouldSerializeAndSave() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)).thenReturn("{\"interrogationId\":\"interrogation-123\"}"); @@ -64,7 +64,7 @@ void saveAsRawJson_shouldSerializeAndSave() throws JacksonException { @Test @DisplayName("Should save a document containing interrogationId") - void saveAsRawJson_documentShouldContainInterrogationId() throws JacksonException { + void saveAsRawJson_documentShouldContainInterrogationId() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)).thenReturn("{}"); @@ -81,7 +81,7 @@ void saveAsRawJson_documentShouldContainInterrogationId() throws JacksonExceptio @Test @DisplayName("Should save a document containing collectionInstrumentId") - void saveAsRawJson_documentShouldContainCollectionInstrumentId() throws JacksonException { + void saveAsRawJson_documentShouldContainCollectionInstrumentId() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)).thenReturn("{}"); @@ -98,7 +98,7 @@ void saveAsRawJson_documentShouldContainCollectionInstrumentId() throws JacksonE @Test @DisplayName("Should save a document containing mode") - void saveAsRawJson_documentShouldContainMode() throws JacksonException { + void saveAsRawJson_documentShouldContainMode() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)).thenReturn("{}"); @@ -115,7 +115,7 @@ void saveAsRawJson_documentShouldContainMode() throws JacksonException { @Test @DisplayName("Should save a document containing a non-null recordDate") - void saveAsRawJson_documentShouldContainRecordDate() throws JacksonException { + void saveAsRawJson_documentShouldContainRecordDate() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)).thenReturn("{}"); @@ -133,7 +133,7 @@ void saveAsRawJson_documentShouldContainRecordDate() throws JacksonException { @Test @DisplayName("Should save a document containing a non-null payload") - void saveAsRawJson_documentShouldContainPayload() throws JacksonException { + void saveAsRawJson_documentShouldContainPayload() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)) @@ -152,7 +152,7 @@ void saveAsRawJson_documentShouldContainPayload() throws JacksonException { @Test @DisplayName("Should save to the 'rawResponses' collection exactly") - void saveAsRawJson_shouldSaveToCorrectCollection() throws JacksonException { + void saveAsRawJson_shouldSaveToCorrectCollection() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)).thenReturn("{}"); @@ -168,7 +168,7 @@ void saveAsRawJson_shouldSaveToCorrectCollection() throws JacksonException { @Test @DisplayName("Should call mongoTemplate.save() exactly once") - void saveAsRawJson_shouldCallSaveExactlyOnce() throws JacksonException { + void saveAsRawJson_shouldCallSaveExactlyOnce() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)).thenReturn("{}"); @@ -183,26 +183,26 @@ void saveAsRawJson_shouldCallSaveExactlyOnce() throws JacksonException { @Test @DisplayName("Should wrap JsonProcessingException in RuntimeException") - void saveAsRawJson_jsonProcessingException_shouldThrowRuntimeException() throws JacksonException { + void saveAsRawJson_jsonProcessingException_shouldThrowRuntimeException() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)) - .thenThrow(new JacksonException("serialization error") {}); + .thenThrow(new JsonProcessingException("serialization error") {}); // WHEN / THEN assertThatThrownBy(() -> repository.saveAsRawJson(dto)) .isInstanceOf(RuntimeException.class) - .hasCauseInstanceOf(JacksonException.class) + .hasCauseInstanceOf(JsonProcessingException.class) .hasRootCauseMessage("serialization error"); } @Test @DisplayName("Should not call mongoTemplate when serialization fails") - void saveAsRawJson_jsonProcessingException_shouldNotCallMongoTemplate() throws JacksonException { + void saveAsRawJson_jsonProcessingException_shouldNotCallMongoTemplate() throws JsonProcessingException { // GIVEN RawResponseDto dto = buildDto(); when(objectMapper.writeValueAsString(dto)) - .thenThrow(new JacksonException("serialization error") {}); + .thenThrow(new JsonProcessingException("serialization error") {}); // WHEN try {