diff --git a/.gitignore b/.gitignore index 253ca3b30..e6a4c24b8 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ src/test/resources/OUT/* /src/test/resources/DONE/* /src/test/resources/genesis_deleted_schedules/* /src/test/resources/IN/WEB/SAMPLETEST/ +/src/main/resources/application-mbg.properties 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 744096cb5..16bcb55ed 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 @@ -16,19 +16,26 @@ import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PagedModel; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import java.time.Instant; import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @@ -36,14 +43,14 @@ @Slf4j @Controller -@RequestMapping(path = "/responses/raw" ) +@RequestMapping(path = "/responses/raw") public class RawResponseController { private static final String SUCCESS_MESSAGE = "Interrogation %s saved"; private static final String PARTITION_ID = "partitionId"; private static final String INTERROGATION_ID = "interrogationId"; private final LunaticJsonRawDataApiPort lunaticJsonRawDataApiPort; - + public RawResponseController(LunaticJsonRawDataApiPort lunaticJsonRawDataApiPort) { this.lunaticJsonRawDataApiPort = lunaticJsonRawDataApiPort; } @@ -60,7 +67,7 @@ public ResponseEntity saveRawResponsesFromJsonBody( @RequestParam(value = "mode") Mode modeSpecified, @RequestBody Map dataJson ) { - log.info("Try to save interrogationId {} for campaign {}",interrogationId,campaignName); + log.info("Try to save interrogationId {} for campaign {}", interrogationId, campaignName); LunaticJsonRawDataModel rawData = LunaticJsonRawDataModel.builder() .campaignId(campaignName) .questionnaireId(questionnaireId) @@ -72,12 +79,12 @@ public ResponseEntity saveRawResponsesFromJsonBody( .build(); try { lunaticJsonRawDataApiPort.save(rawData); - } catch (Exception e){ + } catch (Exception e) { return ResponseEntity.status(500).body("Unexpected error"); } - log.info("Data saved for interrogationId {} and campaign {}",interrogationId, campaignName); + log.info("Data saved for interrogationId {} and campaign {}", interrogationId, campaignName); // Collect platform prefer code 201 in case of success - return ResponseEntity.status(201).body(String.format(SUCCESS_MESSAGE,interrogationId)); + return ResponseEntity.status(201).body(String.format(SUCCESS_MESSAGE, interrogationId)); } @Operation(summary = "Save lunatic json data from one interrogation in Genesis Database (with json " + @@ -90,8 +97,8 @@ public ResponseEntity saveRawResponsesFromJsonBodyWithValidation( JsonSchema jsonSchema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7).getSchema( RawResponseController.class.getResourceAsStream("/jsonSchemas/RawResponse.json") ); - try{ - if(jsonSchema == null){ + try { + if (jsonSchema == null) { throw new GenesisException(500, "No RawResponse json schema has been found"); } Set errors = jsonSchema.validate( @@ -103,9 +110,9 @@ public ResponseEntity saveRawResponsesFromJsonBodyWithValidation( validate(errors); //Check required ids checkRequiredIds(body); - }catch (JsonProcessingException jpe){ + } catch (JsonProcessingException jpe) { return ResponseEntity.status(400).body(jpe.toString()); - }catch (GenesisException ge){ + } catch (GenesisException ge) { return ResponseEntity.status(ge.getStatus()).body(ge.getMessage()); } @@ -124,29 +131,29 @@ public ResponseEntity saveRawResponsesFromJsonBodyWithValidation( return ResponseEntity.status(500).body("Unexpected error"); } - log.info("Data saved for interrogationId {} and partition {}",body.get(INTERROGATION_ID).toString(), + log.info("Data saved for interrogationId {} and partition {}", body.get(INTERROGATION_ID).toString(), body.get(PARTITION_ID).toString()); - return ResponseEntity.status(201).body(String.format(SUCCESS_MESSAGE,body.get(INTERROGATION_ID).toString())); + return ResponseEntity.status(201).body(String.format(SUCCESS_MESSAGE, body.get(INTERROGATION_ID).toString())); } //GET unprocessed @Operation(summary = "Get campaign id and interrogationId from all unprocessed raw json data") @GetMapping(path = "/lunatic-json/get/unprocessed") @PreAuthorize("hasRole('SCHEDULER')") - public ResponseEntity> getUnproccessedJsonRawData(){ + public ResponseEntity> getUnproccessedJsonRawData() { log.info("Try to get unprocessed raw JSON datas..."); return ResponseEntity.ok(lunaticJsonRawDataApiPort.getUnprocessedDataIds()); } @Hidden - @GetMapping(path= "/lunatic-json/get/by-interrogation-mode-and-campaign") + @GetMapping(path = "/lunatic-json/get/by-interrogation-mode-and-campaign") @PreAuthorize("hasRole('ADMIN')") - public ResponseEntity getJsonRawData( - @RequestParam(INTERROGATION_ID) String interrogationId, - @RequestParam("campaignName") String campaignName, - @RequestParam(value = "mode") Mode modeSpecified - ){ - List data = lunaticJsonRawDataApiPort.getRawData(campaignName,modeSpecified,List.of(interrogationId)); + public ResponseEntity getJsonRawData( + @RequestParam(INTERROGATION_ID) String interrogationId, + @RequestParam("campaignName") String campaignName, + @RequestParam(value = "mode") Mode modeSpecified + ) { + List data = lunaticJsonRawDataApiPort.getRawData(campaignName, modeSpecified, List.of(interrogationId)); return ResponseEntity.ok(data.getFirst()); } @@ -158,7 +165,7 @@ public ResponseEntity processJsonRawData( @RequestParam("campaignName") String campaignName, @RequestParam("questionnaireId") String questionnaireId, @RequestBody List interrogationIdList - ){ + ) { log.info("Try to process raw JSON datas for campaign {} and {} interrogationIds", campaignName, interrogationIdList.size()); List errors = new ArrayList<>(); @@ -168,13 +175,30 @@ public ResponseEntity processJsonRawData( ResponseEntity.ok("%d document(s) processed".formatted(result.dataCount())) : ResponseEntity.ok("%d document(s) processed, including %d FORMATTED after data verification" .formatted(result.dataCount(), result.formattedDataCount())); - }catch (GenesisException e){ + } catch (GenesisException e) { return ResponseEntity.status(e.getStatus()).body(e.getMessage()); } } + @Operation(summary = "Get lunatic JSON data from one campaign in Genesis Database, filtered by optional start and end dates") + @GetMapping(path = "/lunatic-json/{campaignId}") + @PreAuthorize("hasRole('USER_KRAFTWERK')") + 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 = "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={}", campaignId, startDate, endDate); + Pageable pageable = PageRequest.of(page, size); + Page rawResponses = lunaticJsonRawDataApiPort.findRawDataByCampaignIdAndDate(campaignId, startDate, endDate, pageable); + log.info("rawResponses=" + rawResponses.getContent().size()); + return ResponseEntity.status(HttpStatus.OK).body(new PagedModel<>(rawResponses)); + } + private void validate(Set errors) throws GenesisException { - if(!errors.isEmpty()){ + if (!errors.isEmpty()) { String errorMessage = errors.stream() .map(ValidationMessage::getMessage) .collect(Collectors.joining(System.lineSeparator() + " - ")); @@ -187,14 +211,14 @@ private void validate(Set errors) throws GenesisException { } private void checkRequiredIds(Map body) throws GenesisException { - for(String requiredKey : List.of( + for (String requiredKey : List.of( PARTITION_ID, "questionnaireModelId", INTERROGATION_ID, "surveyUnitId", "mode" - )){ - if(body.get(requiredKey) == null){ + )) { + if (body.get(requiredKey) == null) { throw new GenesisException(400, "No %s found in body".formatted(requiredKey)); } } diff --git a/src/main/java/fr/insee/genesis/domain/ports/api/LunaticJsonRawDataApiPort.java b/src/main/java/fr/insee/genesis/domain/ports/api/LunaticJsonRawDataApiPort.java index 5708c2904..6c7fb3088 100644 --- a/src/main/java/fr/insee/genesis/domain/ports/api/LunaticJsonRawDataApiPort.java +++ b/src/main/java/fr/insee/genesis/domain/ports/api/LunaticJsonRawDataApiPort.java @@ -8,7 +8,10 @@ import fr.insee.genesis.domain.model.surveyunit.rawdata.LunaticJsonRawDataModel; import fr.insee.genesis.exceptions.GenesisError; import fr.insee.genesis.exceptions.GenesisException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import java.time.Instant; import java.util.List; import java.util.Set; @@ -21,6 +24,7 @@ public interface LunaticJsonRawDataApiPort { void updateProcessDates(List surveyUnitModels); Set findDistinctQuestionnaireIds(); long countResponsesByQuestionnaireId(String campaignId); + Page findRawDataByCampaignIdAndDate(String campaignId, Instant startDt, Instant endDt, Pageable pageable); DataProcessResult processRawData(String campaignName, List interrogationIdList, List errors) throws GenesisException; } diff --git a/src/main/java/fr/insee/genesis/domain/ports/spi/LunaticJsonRawDataPersistencePort.java b/src/main/java/fr/insee/genesis/domain/ports/spi/LunaticJsonRawDataPersistencePort.java index 33c24ad11..8f3c8c3ce 100644 --- a/src/main/java/fr/insee/genesis/domain/ports/spi/LunaticJsonRawDataPersistencePort.java +++ b/src/main/java/fr/insee/genesis/domain/ports/spi/LunaticJsonRawDataPersistencePort.java @@ -2,7 +2,10 @@ import fr.insee.genesis.domain.model.surveyunit.Mode; import fr.insee.genesis.domain.model.surveyunit.rawdata.LunaticJsonRawDataModel; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import java.time.Instant; import java.util.List; import java.util.Set; @@ -13,5 +16,6 @@ public interface LunaticJsonRawDataPersistencePort { List getAllUnprocessedData(); void updateProcessDates(String campaignId, Set interrogationIds); Set findDistinctQuestionnaireIds(); + Page findByCampaignIdAndDate(String campaignId, Instant startDt, Instant endDt, Pageable pageable); long countResponsesByQuestionnaireId(String questionnaireId); } diff --git a/src/main/java/fr/insee/genesis/domain/service/rawdata/LunaticJsonRawDataService.java b/src/main/java/fr/insee/genesis/domain/service/rawdata/LunaticJsonRawDataService.java index 00ba32e3b..bc8e145d7 100644 --- a/src/main/java/fr/insee/genesis/domain/service/rawdata/LunaticJsonRawDataService.java +++ b/src/main/java/fr/insee/genesis/domain/service/rawdata/LunaticJsonRawDataService.java @@ -28,10 +28,13 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import java.io.IOException; +import java.time.Instant; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; @@ -387,4 +390,12 @@ public Set findDistinctQuestionnaireIds() { public long countResponsesByQuestionnaireId(String campaignId) { return lunaticJsonRawDataPersistencePort.countResponsesByQuestionnaireId(campaignId); } + + + @Override + public Page findRawDataByCampaignIdAndDate(String campaignId, Instant startDt, Instant endDt, Pageable pageable){ + return lunaticJsonRawDataPersistencePort.findByCampaignIdAndDate(campaignId,startDt, endDt,pageable); + + } + } diff --git a/src/main/java/fr/insee/genesis/infrastructure/adapter/LunaticJsonRawDataMongoAdapter.java b/src/main/java/fr/insee/genesis/infrastructure/adapter/LunaticJsonRawDataMongoAdapter.java index 4af9975f1..b32b4f60f 100644 --- a/src/main/java/fr/insee/genesis/infrastructure/adapter/LunaticJsonRawDataMongoAdapter.java +++ b/src/main/java/fr/insee/genesis/infrastructure/adapter/LunaticJsonRawDataMongoAdapter.java @@ -9,12 +9,16 @@ import fr.insee.genesis.infrastructure.repository.LunaticJsonMongoDBRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Service; +import java.time.Instant; import java.time.LocalDateTime; import java.util.HashSet; import java.util.List; @@ -68,6 +72,13 @@ public Set findDistinctQuestionnaireIds() { return questionnaireIds; } + @Override + public Page findByCampaignIdAndDate(String campaignId, Instant startDt, Instant endDt, Pageable pageable) { + Page rawDataDocsPage = repository.findByCampaignIdAndRecordDateBetween(campaignId,startDt,endDt,pageable); + List modelList = LunaticJsonRawDataDocumentMapper.INSTANCE.listDocumentToListModel(rawDataDocsPage.getContent()); + return new PageImpl<>(modelList, rawDataDocsPage.getPageable(), rawDataDocsPage.getTotalElements()); + } + @Override public long countResponsesByQuestionnaireId(String questionnaireId) { return repository.countByQuestionnaireId(questionnaireId); diff --git a/src/main/java/fr/insee/genesis/infrastructure/document/rundeck/RundeckExecutionDocument.java b/src/main/java/fr/insee/genesis/infrastructure/document/rundeck/RundeckExecutionDocument.java index 8e2b8ad57..c7313ae59 100644 --- a/src/main/java/fr/insee/genesis/infrastructure/document/rundeck/RundeckExecutionDocument.java +++ b/src/main/java/fr/insee/genesis/infrastructure/document/rundeck/RundeckExecutionDocument.java @@ -2,7 +2,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; import fr.insee.genesis.domain.model.rundeck.DateStarted; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.data.mongodb.core.mapping.Document; @Data diff --git a/src/main/java/fr/insee/genesis/infrastructure/repository/LunaticJsonMongoDBRepository.java b/src/main/java/fr/insee/genesis/infrastructure/repository/LunaticJsonMongoDBRepository.java index c07ff1dcc..62dc03ff2 100644 --- a/src/main/java/fr/insee/genesis/infrastructure/repository/LunaticJsonMongoDBRepository.java +++ b/src/main/java/fr/insee/genesis/infrastructure/repository/LunaticJsonMongoDBRepository.java @@ -2,21 +2,28 @@ import fr.insee.genesis.domain.model.surveyunit.Mode; import fr.insee.genesis.infrastructure.document.rawdata.LunaticJsonRawDataDocument; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.Query; import org.springframework.stereotype.Repository; +import java.time.Instant; import java.util.List; @Repository -public interface LunaticJsonMongoDBRepository extends MongoRepository { +public interface LunaticJsonMongoDBRepository extends MongoRepository { @Query("{\"processDate\" : null}") List findByNullProcessDate(); + @Query(value = "{ 'campaignId' : ?0 }", fields = "{ 'mode' : 1 }") List findModesByCampaignId(String campaignId); + @Query(value = "{ 'campaignId' : ?0, 'mode' : ?1, 'interrogationId': {$in: ?2} }") List findModesByCampaignIdAndByModeAndinterrogationIdIninterrogationIdList(String campaignName, Mode mode, List interrogationIdList); + + Page findByCampaignIdAndRecordDateBetween(String campagneId, Instant start, Instant end, Pageable pageable); long countByQuestionnaireId(String questionnaireId); diff --git a/src/test/java/fr/insee/genesis/controller/rest/ControllerAccessTest.java b/src/test/java/fr/insee/genesis/controller/rest/ControllerAccessTest.java index 90da43a09..4be72a65c 100644 --- a/src/test/java/fr/insee/genesis/controller/rest/ControllerAccessTest.java +++ b/src/test/java/fr/insee/genesis/controller/rest/ControllerAccessTest.java @@ -22,11 +22,13 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.http.HttpMethod; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import java.util.Date; import java.util.List; @@ -37,8 +39,12 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpMethod.POST; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @@ -47,12 +53,16 @@ @EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) class ControllerAccessTest { + // Constants for user roles + private static final String USER_KRAFTWERK = "USER_KRAFTWERK"; + private static final String USER_PLATINE = "USER_PLATINE"; + private static final String ADMIN = "ADMIN"; + private static final String READER = "READER"; // JWT claim properties loaded from application properties @Value("${fr.insee.genesis.security.token.oidc-claim-role}") private String claimRoleDotRoles; @Value("${fr.insee.genesis.security.token.oidc-claim-username}") private String claimName; - @Autowired private MockMvc mockMvc; // Simulates HTTP requests to the REST endpoints @MockitoBean @@ -78,16 +88,10 @@ class ControllerAccessTest { @MockitoBean private LunaticModelMongoDBRepository lunaticModelMongoDBRepository; - // Constants for user roles - private static final String USER_KRAFTWERK = "USER_KRAFTWERK"; - private static final String USER_PLATINE = "USER_PLATINE"; - private static final String ADMIN = "ADMIN"; - private static final String READER = "READER"; - /** * Provides a stream of URIs that are allowed for reader. */ - private static Stream endpointsReader(){ + private static Stream endpointsReader() { return Stream.of( Arguments.of("/questionnaires/with-campaigns"), Arguments.of("/questionnaires/by-campaign?campaignId=CAMPAIGNTEST"), @@ -101,6 +105,16 @@ private static Stream endpointsReader(){ ); } + + private static Stream responseEndpoint() { + return Stream.of( + Arguments.of(GET,"/response/lunatic-json/get/unprocessed"), + Arguments.of(GET,"/response//lunatic-json/get/by-interrogation-mode-and-campaign"), + Arguments.of(POST,"/response//lunatic-json/process"), + Arguments.of(GET,"/response//lunatic-json/campaignId=TOTO") + ); + } + /** * Tests that users with the "ADMIN" role can access read-only endpoints. */ @@ -120,11 +134,11 @@ void admin_should_access_reader_allowed_services(String endpointURI) throws Exce @ParameterizedTest @MethodSource("endpointsReader") @DisplayName("Kraftwerk users should access reader-allowed services") - void kraftwerk_users_should_access_reader_allowed_services(String endpointURI) throws Exception{ + void kraftwerk_users_should_access_reader_allowed_services(String endpointURI) throws Exception { Jwt jwt = generateJwt(List.of("utilisateur_Kraftwerk"), USER_KRAFTWERK); when(jwtDecoder.decode(anyString())).thenReturn(jwt); mockMvc.perform(get(endpointURI).header("Authorization", "bearer token_blabla")) - .andExpect(status().is(oneOf(200,404))); + .andExpect(status().is(oneOf(200, 404))); } /** @@ -133,11 +147,11 @@ void kraftwerk_users_should_access_reader_allowed_services(String endpointURI) t @ParameterizedTest @MethodSource("endpointsReader") @DisplayName("Platine users should access reader-allowed services") - void platine_users_should_access_reader_allowed_services(String endpointURI) throws Exception{ + void platine_users_should_access_reader_allowed_services(String endpointURI) throws Exception { Jwt jwt = generateJwt(List.of("utilisateur_Platine"), USER_PLATINE); when(jwtDecoder.decode(anyString())).thenReturn(jwt); mockMvc.perform(get(endpointURI).header("Authorization", "bearer token_blabla")) - .andExpect(status().is(oneOf(200,404))); + .andExpect(status().is(oneOf(200, 404))); } /** @@ -146,11 +160,11 @@ void platine_users_should_access_reader_allowed_services(String endpointURI) thr @ParameterizedTest @MethodSource("endpointsReader") @DisplayName("Readers should access reader-allowed services") - void reader_should_access_reader_allowed_services(String endpointURI) throws Exception{ + void reader_should_access_reader_allowed_services(String endpointURI) throws Exception { Jwt jwt = generateJwt(List.of("lecteur_traiter"), "reader"); when(jwtDecoder.decode(anyString())).thenReturn(jwt); mockMvc.perform(get(endpointURI).header("Authorization", "bearer token_blabla")) - .andExpect(status().is(oneOf(200,404))); + .andExpect(status().is(oneOf(200, 404))); } /** @@ -159,7 +173,7 @@ void reader_should_access_reader_allowed_services(String endpointURI) throws Exc @ParameterizedTest @MethodSource("endpointsReader") @DisplayName("User with invalid roles should not access reader-allowed services") - void invalid_user_should_not_access_reader_allowed_services(String endpointURI) throws Exception{ + void invalid_user_should_not_access_reader_allowed_services(String endpointURI) throws Exception { Jwt jwt = generateJwt(List.of("toto"), "invalid_role"); when(jwtDecoder.decode(anyString())).thenReturn(jwt); mockMvc.perform(get(endpointURI).header("Authorization", "bearer token_blabla")) @@ -171,11 +185,11 @@ void invalid_user_should_not_access_reader_allowed_services(String endpointURI) */ @Test @DisplayName("Reader should access schedule/all endpoint") - void reader_should_access_schedules_services() throws Exception{ + void reader_should_access_schedules_services() throws Exception { Jwt jwt = generateJwt(List.of("lecteur_traiter"), READER); when(jwtDecoder.decode(anyString())).thenReturn(jwt); mockMvc.perform(get("/context/schedules/all").header("Authorization", "bearer token_blabla")) - .andExpect(status().is(oneOf(200,404))); + .andExpect(status().is(oneOf(200, 404))); } /** @@ -183,7 +197,7 @@ void reader_should_access_schedules_services() throws Exception{ */ @Test @DisplayName("Reader should not access other schedule endpoints") - void reader_should_not_access_other_schedules_services() throws Exception{ + void reader_should_not_access_other_schedules_services() throws Exception { doNothing().when(dataProcessingContextApiPort).deleteSchedules(anyString()); Jwt jwt = generateJwt(List.of("lecteur_traiter"), READER); when(jwtDecoder.decode(anyString())).thenReturn(jwt); @@ -191,16 +205,44 @@ void reader_should_not_access_other_schedules_services() throws Exception{ .andExpect(status().isForbidden()); } + + /** + * Test that reader can not access other schedule endpoints. + */ + @ParameterizedTest + @MethodSource("responseEndpoint") + @DisplayName("Reader should not access /responses endpoints") + void reader_should_not_access_response_services(HttpMethod method,String endpointURI) throws Exception { + Jwt jwt = generateJwt(List.of("lecteur_traiter"), READER); + when(jwtDecoder.decode(anyString())).thenReturn(jwt); + MockHttpServletRequestBuilder requestBuilder; + if (method == HttpMethod.GET) { + requestBuilder = get(endpointURI); + } else if (method == HttpMethod.POST) { + requestBuilder = post(endpointURI); + } else if (method == HttpMethod.PUT) { + requestBuilder = put(endpointURI); + } else if (method == HttpMethod.DELETE) { + requestBuilder = delete(endpointURI); + } else { + throw new IllegalArgumentException("Unsupported HTTP method: " + method); + } + + mockMvc.perform(requestBuilder.header("Authorization", "bearer token_blabla")) + .andExpect(status().is(oneOf(200, 404))); + } + + /** * Test that kraftwerk users can't access the schedule endpoints. */ @Test @DisplayName("Kraftwerk users should access schedules service") - void kraftwerk_users_should_not_access_schedules_services() throws Exception{ + void kraftwerk_users_should_not_access_schedules_services() throws Exception { Jwt jwt = generateJwt(List.of("utilisateur_Kraftwerk"), USER_KRAFTWERK); when(jwtDecoder.decode(anyString())).thenReturn(jwt); mockMvc.perform(get("/context/schedules/all").header("Authorization", "bearer token_blabla")) - .andExpect(status().is(oneOf(200,404))); + .andExpect(status().is(oneOf(200, 404))); } /** @@ -208,11 +250,11 @@ void kraftwerk_users_should_not_access_schedules_services() throws Exception{ */ @Test @DisplayName("Admins should access schedules service") - void admins_should_access_schedules_services() throws Exception{ + void admins_should_access_schedules_services() throws Exception { Jwt jwt = generateJwt(List.of("administrateur_traiter"), ADMIN); when(jwtDecoder.decode(anyString())).thenReturn(jwt); mockMvc.perform(get("/context/schedules/all").header("Authorization", "bearer token_blabla")) - .andExpect(status().is(oneOf(200,404))); + .andExpect(status().is(oneOf(200, 404))); } /** @@ -220,7 +262,7 @@ void admins_should_access_schedules_services() throws Exception{ */ @Test @DisplayName("Invalid roles should not access schedules service") - void invalid_roles_should_access_schedules_services() throws Exception{ + void invalid_roles_should_access_schedules_services() throws Exception { Jwt jwt = generateJwt(List.of("invalid_role"), "invalid_role"); when(jwtDecoder.decode(anyString())).thenReturn(jwt); mockMvc.perform(get("/context/schedules").header("Authorization", "bearer token_blabla")) 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 3b4f48160..9624d6ece 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 @@ -7,6 +7,7 @@ import fr.insee.genesis.domain.model.context.DataProcessingContextModel; import fr.insee.genesis.domain.model.surveyunit.DataState; import fr.insee.genesis.domain.model.surveyunit.Mode; +import fr.insee.genesis.domain.model.surveyunit.rawdata.LunaticJsonRawDataModel; import fr.insee.genesis.domain.ports.api.LunaticJsonRawDataApiPort; import fr.insee.genesis.domain.service.context.DataProcessingContextService; import fr.insee.genesis.domain.service.rawdata.LunaticJsonRawDataService; @@ -23,9 +24,12 @@ import fr.insee.genesis.stubs.SurveyUnitQualityToolPerretAdapterStub; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import org.springframework.data.web.PagedModel; import org.springframework.http.ResponseEntity; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -136,7 +140,7 @@ void processJsonRawDataTest(){ String interrogationId = "testinterrogationId1"; String varName = "AVIS_MAIL"; String varValue = "TEST"; - addJsonRawDataDocumentToStub(campaignId, questionnaireId, interrogationId, null, varName, varValue); + addJsonRawDataDocumentToStub(campaignId, questionnaireId, interrogationId, null, LocalDateTime.now(),varName, varValue); dataProcessingContextPersistancePortStub.getMongoStub().add( DataProcessingContextMapper.INSTANCE.modelToDocument( @@ -183,6 +187,33 @@ void processJsonRawDataTest(){ } + @Test + void getRawResponsesFromJsonBody() { + //GIVEN + String campaignId = "getRawResponsesFromJsonBody"; + String questionnaireId = campaignId + "_quest"; + String interrogationId = "getRawResponsesFromJsonBody_id1"; + String varName = "VARName1"; + String varValue = "TEST"; + Instant recordDate = Instant.parse("2025-01-01T01:00:00.000Z"); + Instant processDate = Instant.parse("2025-01-02T01:00:00.000Z"); + + addJsonRawDataDocumentToStub(campaignId, questionnaireId, interrogationId, + LocalDateTime.ofInstant(processDate, ZoneOffset.UTC), + LocalDateTime.ofInstant(recordDate, ZoneOffset.UTC), + varName, varValue); + + Instant starDate= recordDate.minusSeconds(86400),endDate = recordDate.plusSeconds(86400); + int page=0, size= 10; + + //WHEN + ResponseEntity> response = rawResponseController.getRawResponsesFromJsonBody(campaignId, starDate, endDate, page, size); + + //THEN + Assertions.assertThat(response.getStatusCode().is2xxSuccessful()).isTrue(); + Assertions.assertThat(response.getBody().getContent().size()).isEqualTo(1); + } + //Utils private void addJsonRawDataDocumentToStub(String campaignId, String questionnaireId, String interrogationId, LocalDateTime processDate) { @@ -199,8 +230,9 @@ private void addJsonRawDataDocumentToStub(String campaignId, String questionnair } private void addJsonRawDataDocumentToStub(String campaignId, String questionnaireId, String interrogationId, - LocalDateTime processDate, - String variableName, String variableValue) { + LocalDateTime processDate, + LocalDateTime recordDate, + String variableName, String variableValue) { Map jsonMap = Map.of( "COLLECTED", Map.of(variableName, Map.of("COLLECTED", variableValue)), @@ -212,7 +244,7 @@ private void addJsonRawDataDocumentToStub(String campaignId, String questionnair .questionnaireId(questionnaireId) .mode(Mode.WEB) .interrogationId(interrogationId) - .recordDate(LocalDateTime.now()) + .recordDate(recordDate) .processDate(processDate) .data(jsonMap) .build(); diff --git a/src/test/java/fr/insee/genesis/stubs/LunaticJsonMongoDBRepositoryStub.java b/src/test/java/fr/insee/genesis/stubs/LunaticJsonMongoDBRepositoryStub.java index cf79105b1..9a48da8aa 100644 --- a/src/test/java/fr/insee/genesis/stubs/LunaticJsonMongoDBRepositoryStub.java +++ b/src/test/java/fr/insee/genesis/stubs/LunaticJsonMongoDBRepositoryStub.java @@ -10,6 +10,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.FluentQuery; +import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -50,6 +51,11 @@ public List findModesByCampaignIdAndByModeAndinterro .toList(); } + @Override + public Page findByCampaignIdAndRecordDateBetween(String campagneId, Instant start, Instant end, Pageable pageable){ + return Page.empty(pageable); + } + @Override public long countByQuestionnaireId(String questionnaireId) { return countByQuestionnaireIdMap.getOrDefault(questionnaireId, 0L); diff --git a/src/test/java/fr/insee/genesis/stubs/LunaticJsonRawDataPersistanceStub.java b/src/test/java/fr/insee/genesis/stubs/LunaticJsonRawDataPersistanceStub.java index bdde93877..0e01fd97c 100644 --- a/src/test/java/fr/insee/genesis/stubs/LunaticJsonRawDataPersistanceStub.java +++ b/src/test/java/fr/insee/genesis/stubs/LunaticJsonRawDataPersistanceStub.java @@ -6,8 +6,13 @@ import fr.insee.genesis.infrastructure.document.rawdata.LunaticJsonRawDataDocument; import fr.insee.genesis.infrastructure.mappers.LunaticJsonRawDataDocumentMapper; import lombok.Getter; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -78,6 +83,18 @@ public Set findDistinctQuestionnaireIds() { return questionnaireIds; } + @Override + public Page findByCampaignIdAndDate(String campaignId, Instant startDt, Instant endDt, Pageable pageable) { + List foundRaws = mongoStub.stream() + .filter(rawData -> rawData.campaignId().equals(campaignId)) + .filter(rawData -> rawData.recordDate().isAfter(LocalDateTime.ofInstant(startDt,ZoneOffset.UTC))) + .filter(rawData -> rawData.recordDate().isBefore(LocalDateTime.ofInstant(endDt,ZoneOffset.UTC))) + .toList(); + return new PageImpl<>(LunaticJsonRawDataDocumentMapper.INSTANCE.listDocumentToListModel(foundRaws), + pageable, + foundRaws.size()); + } + @Override public long countResponsesByQuestionnaireId(String questionnaireId) { return mongoStub.size();