Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add update for differential collected data #212

Merged
merged 3 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
<description>Modules for queen back-office</description>

<properties>
<revision>4.1.5</revision>
<revision>4.2.0</revision>
<changelist></changelist>
<java.version>21</java.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- maven-surefire-plugin setting.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ public String getDataBySurveyUnit(@IdValid @PathVariable(value = "id") String su
public void updateData(@NotNull @RequestBody ObjectNode dataValue,
@IdValid @PathVariable(value = "id") String surveyUnitId) {
pilotageComponent.checkHabilitations(surveyUnitId, PilotageRole.INTERVIEWER);
dataService.updateData(surveyUnitId, dataValue);
dataService.saveData(surveyUnitId, dataValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package fr.insee.queen.application.surveyunit.controller;

import com.fasterxml.jackson.databind.node.ObjectNode;
import fr.insee.queen.application.configuration.auth.AuthorityPrivileges;
import fr.insee.queen.application.pilotage.controller.PilotageComponent;
import fr.insee.queen.application.surveyunit.dto.input.StateDataInput;
import fr.insee.queen.application.surveyunit.dto.input.SurveyUnitDataStateDataUpdateInput;
import fr.insee.queen.application.web.validation.IdValid;
import fr.insee.queen.domain.pilotage.service.PilotageRole;
import fr.insee.queen.domain.surveyunit.model.StateData;
import fr.insee.queen.domain.surveyunit.service.DataService;
import fr.insee.queen.domain.surveyunit.service.SurveyUnitService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@Tag(name = "06. Survey units", description = "Endpoints for survey units")
@RequestMapping(path = "/api")
@Slf4j
@RequiredArgsConstructor
@Validated
@ConditionalOnExpression(value = "${feature.perfdata.enabled} == true")
public class PerfDataController {
private final SurveyUnitService surveyUnitService;
private final PilotageComponent pilotageComponent;
private final DataService dataService;

/**
* Update a survey unit data/state-data
*
* @param surveyUnitId survey unit id
* @param surveyUnitUpdateInput survey unit form data/state data
*/
@Operation(summary = "Update survey-unit data/state-data")
@PatchMapping(path = {"/survey-unit/{id}"})
@PreAuthorize(AuthorityPrivileges.HAS_USER_PRIVILEGES)
public void updateSurveyUnitDataStateDataById(@IdValid @PathVariable(value = "id") String surveyUnitId,
@Valid @RequestBody SurveyUnitDataStateDataUpdateInput surveyUnitUpdateInput) {
pilotageComponent.checkHabilitations(surveyUnitId, PilotageRole.INTERVIEWER, PilotageRole.REVIEWER);
StateData stateData = StateDataInput.toModel(surveyUnitUpdateInput.stateData());
surveyUnitService.updateSurveyUnit(surveyUnitId, surveyUnitUpdateInput.data(), stateData);
}

/**
* Update the collected data of a survey unit
*
* @param collectedDataValue the collected form data to update
* @param surveyUnitId the id of the survey unit
*/
@Operation(summary = "Update collected data for a survey unit")
@PatchMapping(path = "/survey-unit/{id}/data")
@PreAuthorize(AuthorityPrivileges.HAS_USER_PRIVILEGES)
public void updateCollectedData(@NotNull @RequestBody ObjectNode collectedDataValue,
@IdValid @PathVariable(value = "id") String surveyUnitId) {
pilotageComponent.checkHabilitations(surveyUnitId, PilotageRole.INTERVIEWER);
dataService.updateCollectedData(surveyUnitId, collectedDataValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@

import fr.insee.queen.application.configuration.auth.AuthorityPrivileges;
import fr.insee.queen.application.pilotage.controller.PilotageComponent;
import fr.insee.queen.application.surveyunit.dto.input.StateDataInput;
import fr.insee.queen.application.surveyunit.dto.input.SurveyUnitCreationInput;
import fr.insee.queen.application.surveyunit.dto.input.SurveyUnitDataStateDataUpdateInput;
import fr.insee.queen.application.surveyunit.dto.input.SurveyUnitUpdateInput;
import fr.insee.queen.application.surveyunit.dto.output.SurveyUnitDto;
import fr.insee.queen.application.web.validation.IdValid;
import fr.insee.queen.domain.pilotage.service.PilotageRole;
import fr.insee.queen.domain.surveyunit.model.StateData;
import fr.insee.queen.domain.surveyunit.model.SurveyUnit;
import fr.insee.queen.domain.surveyunit.service.SurveyUnitService;
import fr.insee.queen.domain.surveyunit.service.exception.StateDataInvalidDateException;
Expand Down Expand Up @@ -81,22 +78,6 @@ public void updateSurveyUnitById(@IdValid @PathVariable(value = "id") String sur
surveyUnitService.updateSurveyUnit(surveyUnit);
}

/**
* Update a survey unit data/state-data
*
* @param surveyUnitId survey unit id
* @param surveyUnitUpdateInput survey unit form data/state data
*/
@Operation(summary = "Update survey-unit data/state-data")
@PatchMapping(path = {"/survey-unit/{id}"})
@PreAuthorize(AuthorityPrivileges.HAS_USER_PRIVILEGES)
public void updateSurveyUnitDataStateDataById(@IdValid @PathVariable(value = "id") String surveyUnitId,
@Valid @RequestBody SurveyUnitDataStateDataUpdateInput surveyUnitUpdateInput) {
pilotageComponent.checkHabilitations(surveyUnitId, PilotageRole.INTERVIEWER, PilotageRole.REVIEWER);
StateData stateData = StateDataInput.toModel(surveyUnitUpdateInput.stateData());
surveyUnitService.updateSurveyUnit(surveyUnitId, surveyUnitUpdateInput.data(), stateData);
}

/**
* Create or update a survey unit
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import fr.insee.queen.domain.pilotage.service.exception.HabilitationException;
import fr.insee.queen.domain.pilotage.service.exception.PilotageApiException;
import fr.insee.queen.domain.surveyunit.service.exception.StateDataInvalidDateException;
import fr.insee.queen.infrastructure.db.surveyunit.repository.exception.UpdateCollectedDataException;
import fr.insee.queen.infrastructure.depositproof.exception.DepositProofException;
import jakarta.validation.ConstraintViolationException;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -122,6 +123,11 @@ public ResponseEntity<ApiError> noEntityFoundException(EntityNotFoundException e
return generateResponseError(e, HttpStatus.NOT_FOUND, request);
}

@ExceptionHandler(UpdateCollectedDataException.class)
public ResponseEntity<ApiError> updateCollectedDataException(UpdateCollectedDataException e, WebRequest request) {
return generateResponseError(e, HttpStatus.INTERNAL_SERVER_ERROR, request);
}

@ExceptionHandler(AuthenticationTokenException.class)
public ResponseEntity<ApiError> authenticationTokenExceptionException(AuthenticationTokenException e, WebRequest request) {
log.error(e.getMessage(), e);
Expand Down
4 changes: 4 additions & 0 deletions queen-application/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ feature:
enabled: false
interviewer-mode:
enabled: false
perfdata:
enabled: false
collected:
native-insert: false

spring:
main:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,47 @@
import fr.insee.queen.application.surveyunit.dto.input.StateDataInput;
import fr.insee.queen.application.surveyunit.dto.input.StateDataTypeInput;
import fr.insee.queen.application.surveyunit.dto.input.SurveyUnitDataStateDataUpdateInput;
import fr.insee.queen.application.surveyunit.service.dummy.DataFakeService;
import fr.insee.queen.application.surveyunit.service.dummy.SurveyUnitFakeService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class SurveyUnitControllerTest {
class PerfDataControllerTest {
private PilotageFakeComponent pilotageFakeComponent;
private SurveyUnitController surveyUnitController;
private PerfDataController perfDataController;
private SurveyUnitFakeService surveyUnitFakeService;
private DataFakeService dataFakeService;

@BeforeEach
void init() {
surveyUnitFakeService = new SurveyUnitFakeService();
pilotageFakeComponent = new PilotageFakeComponent();
surveyUnitController = new SurveyUnitController(surveyUnitFakeService, pilotageFakeComponent);
dataFakeService = new DataFakeService();
perfDataController = new PerfDataController(surveyUnitFakeService, pilotageFakeComponent, dataFakeService);
}

@Test
@DisplayName("when updating data/state data, verify habilitation")
@DisplayName("When updating data/state data, then verify habilitation")
void testSurveyUnitUpdateDataStateData01() {
String surveyUnitId = "11";
ObjectNode data = JsonNodeFactory.instance.objectNode();
StateDataInput stateData = new StateDataInput(StateDataTypeInput.INIT, 0L, "2.3");
SurveyUnitDataStateDataUpdateInput su = new SurveyUnitDataStateDataUpdateInput(data, stateData);
surveyUnitController.updateSurveyUnitDataStateDataById(surveyUnitId, su);
perfDataController.updateSurveyUnitDataStateDataById(surveyUnitId, su);
assertThat(pilotageFakeComponent.isChecked()).isTrue();
assertThat(surveyUnitFakeService.isCheckSurveyUnitUpdate()).isTrue();
}

@Test
@DisplayName("When updating collected data, then verify habilitation")
void testSurveyUnitUpdateColllectedData01() {
String surveyUnitId = "11";
ObjectNode collectedData = JsonNodeFactory.instance.objectNode();
perfDataController.updateCollectedData(collectedData, surveyUnitId);
assertThat(pilotageFakeComponent.isChecked()).isTrue();
assertThat(dataFakeService.isCheckUpdateCollectedData()).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import fr.insee.queen.application.utils.AuthenticatedUserTestHelper;
import fr.insee.queen.application.utils.JsonTestHelper;
import io.zonky.test.db.AutoConfigureEmbeddedDatabase;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
Expand All @@ -17,10 +18,10 @@
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
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.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
Expand Down Expand Up @@ -145,9 +146,137 @@ void on_get_data_when_anonymous_user_return_401() throws Exception {
@Test
void on_update_data_when_anonymous_user_return_401() throws Exception {
mockMvc.perform(put("/api/survey-unit/12/data")
.content("{}")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.with(authentication(authenticatedUserTestHelper.getNotAuthenticatedUser()))
)
.andExpect(status().isUnauthorized());
}

@Test
@DisplayName("Given survey unit data with collected data, when inserting partial collected data, then merge collected datas")
@Sql(value = ScriptConstants.REINIT_SQL_SCRIPT, executionPhase = AFTER_TEST_METHOD)
void updateCollectedData01() throws Exception {
String surveyUnitId = "11";
String collectedDataJson = """
{
"hey": "ho",
"obj": {"plip": "plop"},
"numb": 2,
"READY": {
"EDITED": true,
"COLLECTED":false
},
"COMMENT": null
}""";

mockMvc.perform(patch("/api/survey-unit/" + surveyUnitId + "/data")
.content(collectedDataJson)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.with(authentication(authenticatedUserTestHelper.getSurveyUnitUser()))
)
.andExpect(status().isOk());

MvcResult result = mockMvc.perform(get("/api/survey-unit/" + surveyUnitId + "/data")
.accept(MediaType.APPLICATION_JSON)
.with(authentication(authenticatedUserTestHelper.getSurveyUnitUser()))
)
.andExpect(status().isOk())
.andReturn();

String content = result.getResponse().getContentAsString();
String expectedResult = """
{
"EXTERNAL":{
"LAST_BROADCAST":"12/07/1998"
},
"COLLECTED":{
"hey":"ho",
"obj":{
"plip":"plop"
},
"numb":2,
"READY":{
"EDITED":true,
"COLLECTED":false
},
"COMMENT": null,
"PRODUCER":{
"EDITED":null,
"FORCED":null,
"INPUTED":null,
"PREVIOUS":null,
"COLLECTED":"Matt Groening"
}
}
}
""";
JSONAssert.assertEquals(expectedResult, content, JSONCompareMode.STRICT);
}

@Test
@DisplayName("Given survey unit with no collected json data, when updating data then insert partial data as collected data")
@Sql(value = ScriptConstants.REINIT_SQL_SCRIPT, executionPhase = AFTER_TEST_METHOD)
void updateCollectedData02() throws Exception {
String surveyUnitId = "21";
String collectedDataJson = """
{
"COMMENT": null
}""";

mockMvc.perform(patch("/api/survey-unit/" + surveyUnitId + "/data")
.content(collectedDataJson)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.with(authentication(authenticatedUserTestHelper.getSurveyUnitUser()))
)
.andExpect(status().isOk());

MvcResult result = mockMvc.perform(get("/api/survey-unit/" + surveyUnitId + "/data")
.accept(MediaType.APPLICATION_JSON)
.with(authentication(authenticatedUserTestHelper.getSurveyUnitUser()))
)
.andExpect(status().isOk())
.andReturn();

String content = result.getResponse().getContentAsString();
String expectedResult = "{\"COLLECTED\":" + collectedDataJson + "}";
JSONAssert.assertEquals(expectedResult, content, JSONCompareMode.STRICT);
}

@Test
@DisplayName("Given invalid survey unit id, when updating collected data then throw bad request")
void updateCollectedDataError02() throws Exception {
mockMvc.perform(patch("/api/survey-unit/invalid$identifier/data")
.content("{}")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.with(authentication(authenticatedUserTestHelper.getSurveyUnitUser()))
)
.andExpect(status().isBadRequest());
}

@Test
@DisplayName("Given invalid json collected input data, when updating collected data then throw bad request")
void updateCollectedDataError03() throws Exception {
mockMvc.perform(patch("/api/survey-unit/12/data")
.content("[]")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.with(authentication(authenticatedUserTestHelper.getSurveyUnitUser()))
)
.andExpect(status().isBadRequest());
}

@Test
@DisplayName("Given an anoymous user, when updating collected data then return unauthenticated error")
void updateCollectedDataError04() throws Exception {
mockMvc.perform(patch("/api/survey-unit/12/data")
.content("{}")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.with(authentication(authenticatedUserTestHelper.getNotAuthenticatedUser()))
)
.andExpect(status().isUnauthorized());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package fr.insee.queen.application.surveyunit.service.dummy;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import fr.insee.queen.domain.surveyunit.service.DataService;
import lombok.Getter;

public class DataFakeService implements DataService {
@Getter
private boolean checkUpdateCollectedData = false;

@Override
public String getData(String surveyUnitId) {
return null;
}

@Override
public void saveData(String surveyUnitId, JsonNode dataValue) {

}

@Override
public void updateCollectedData(String surveyUnitId, ObjectNode collectedData) {
checkUpdateCollectedData = true;
}
}
Loading