Skip to content

Commit ba13fed

Browse files
authored
feat: multimode crud (#386)
1 parent 08955ee commit ba13fed

18 files changed

+1245
-551
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<groupId>fr.insee</groupId>
1313
<artifactId>Pogues-BO</artifactId>
1414
<packaging>jar</packaging>
15-
<version>4.22.2</version>
15+
<version>4.23.0</version>
1616
<name>Pogues-Back-Office</name>
1717

1818
<properties>

src/main/java/fr/insee/pogues/controller/ArticulationController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ public ResponseEntity<ArticulationDTO> getQuestionnaireArticulation(
5353
return ResponseEntity.status(HttpStatus.OK).body(articulationDTO);
5454
}
5555

56-
@Operation(summary = "Get the articulation of a questionnaire, used for pogues frontend",
56+
@Operation(summary = "Get the articulation of a questionnaire's backup, used for pogues frontend",
5757
responses = { @ApiResponse(content = @Content(mediaType = "application/json")) })
5858
@ApiResponses(value = {
5959
@ApiResponse(responseCode = "200", description = "Success"),
6060
@ApiResponse(responseCode = "404", description = "Questionnaire not found"),
6161
@ApiResponse(responseCode = "422", description = "Questionnaire does not have a roundabout"),
6262
@ApiResponse(responseCode = "422", description = "Questionnaire is not in VTL") })
6363
@GetMapping("/questionnaire/{questionnaireId}/version/{versionId}/articulation")
64-
public ResponseEntity<ArticulationDTO> getQuestionnaireArticulation(
64+
public ResponseEntity<ArticulationDTO> getQuestionnaireVersionArticulation(
6565
@PathVariable(value = "questionnaireId") String ignoredQuestionnaireId,
6666
@PathVariable(value = "versionId") UUID versionId
6767
) throws Exception {
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package fr.insee.pogues.controller;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import fr.insee.pogues.mapper.MultimodeMapper;
5+
import fr.insee.pogues.model.Multimode;
6+
import fr.insee.pogues.model.dto.multimode.MultimodeDTO;
7+
import fr.insee.pogues.service.MultimodeService;
8+
import io.swagger.v3.oas.annotations.Operation;
9+
import io.swagger.v3.oas.annotations.media.Content;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
11+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
12+
import io.swagger.v3.oas.annotations.tags.Tag;
13+
import lombok.extern.slf4j.Slf4j;
14+
import org.springframework.http.HttpStatus;
15+
import org.springframework.http.ResponseEntity;
16+
import org.springframework.web.bind.annotation.*;
17+
18+
import java.util.UUID;
19+
20+
/**
21+
* <p>WebService class used to fetch and update the multimode of a questionnaire.</p>
22+
* <p>This is available only to questionnaires specified in VTL.</p>
23+
*/
24+
@RestController
25+
@RequestMapping("/api/persistence")
26+
@Tag(name = "Multimode Controller")
27+
@Slf4j
28+
public class MultimodeController {
29+
30+
private final MultimodeService multimodeService;
31+
32+
public MultimodeController(MultimodeService multimodeService) {
33+
this.multimodeService = multimodeService;
34+
}
35+
36+
@Operation(summary = "Get the multimode of a questionnaire, used for pogues frontend",
37+
responses = { @ApiResponse(content = @Content(mediaType = "application/json")) })
38+
@ApiResponses(value = {
39+
@ApiResponse(responseCode = "200", description = "Success"),
40+
@ApiResponse(responseCode = "404", description = "Questionnaire not found"),
41+
@ApiResponse(responseCode = "422", description = "Questionnaire is not in VTL") })
42+
@GetMapping("/questionnaire/{questionnaireId}/multimode")
43+
public ResponseEntity<MultimodeDTO> getQuestionnaireMultimode(
44+
@PathVariable(value = "questionnaireId") String questionnaireId
45+
) throws Exception {
46+
Multimode multimode = multimodeService.getQuestionnaireMultimode(questionnaireId);
47+
MultimodeDTO multimodeDTO = MultimodeMapper.toDTO(multimode);
48+
return ResponseEntity.status(HttpStatus.OK).body(multimodeDTO);
49+
}
50+
51+
@Operation(summary = "Get the multimode of a questionnaire's backup, used for pogues frontend",
52+
responses = { @ApiResponse(content = @Content(mediaType = "application/json")) })
53+
@ApiResponses(value = {
54+
@ApiResponse(responseCode = "200", description = "Success"),
55+
@ApiResponse(responseCode = "404", description = "Questionnaire not found"),
56+
@ApiResponse(responseCode = "422", description = "Questionnaire is not in VTL") })
57+
@GetMapping("/questionnaire/{questionnaireId}/version/{versionId}/multimode")
58+
public ResponseEntity<MultimodeDTO> getQuestionnaireVersionMultimode(
59+
@PathVariable(value = "questionnaireId") String ignoredQuestionnaireId,
60+
@PathVariable(value = "versionId") UUID versionId
61+
) throws Exception {
62+
Multimode multimode = multimodeService.getVersionMultimode(versionId);
63+
MultimodeDTO multimodeDTO = MultimodeMapper.toDTO(multimode);
64+
return ResponseEntity.status(HttpStatus.OK).body(multimodeDTO);
65+
}
66+
67+
@Operation(summary = "Update or create a multimode in a questionnaire")
68+
@ApiResponses(value = {
69+
@ApiResponse(responseCode = "201", description = "Successfully created"),
70+
@ApiResponse(responseCode = "204", description = "Successfully updated"),
71+
@ApiResponse(responseCode = "404", description = "Questionnaire not found"),
72+
@ApiResponse(responseCode = "422", description = "Questionnaire is not in VTL") })
73+
@PutMapping("/questionnaire/{questionnaireId}/multimode")
74+
public ResponseEntity<JsonNode> upsertQuestionnaireMultimode(
75+
@PathVariable(value = "questionnaireId") String questionnaireId,
76+
@RequestBody MultimodeDTO multimodeDTO
77+
) throws Exception {
78+
Multimode multimode = MultimodeMapper.toModel(multimodeDTO);
79+
boolean isCreated = multimodeService.upsertQuestionnaireMultimode(questionnaireId, multimode);
80+
if (isCreated) {
81+
return ResponseEntity.status(HttpStatus.CREATED).build();
82+
}
83+
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
84+
}
85+
86+
@Operation(summary = "Delete the multimode of a questionnaire")
87+
@ApiResponses(value = {
88+
@ApiResponse(responseCode = "204", description = "Successfully deleted"),
89+
@ApiResponse(responseCode = "404", description = "Questionnaire not found"),
90+
@ApiResponse(responseCode = "422", description = "Questionnaire is not in VTL") })
91+
@DeleteMapping("/questionnaire/{questionnaireId}/multimode")
92+
public ResponseEntity<JsonNode> deleteQuestionnaireMultimode(
93+
@PathVariable(value = "questionnaireId") String questionnaireId
94+
) throws Exception {
95+
multimodeService.deleteQuestionnaireMultimode(questionnaireId);
96+
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
97+
}
98+
99+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package fr.insee.pogues.mapper;
2+
3+
import fr.insee.pogues.model.*;
4+
import fr.insee.pogues.model.dto.multimode.MultimodeDTO;
5+
import fr.insee.pogues.model.dto.multimode.MultimodeItemDTO;
6+
import fr.insee.pogues.model.dto.multimode.MultimodeRuleDTO;
7+
import fr.insee.pogues.model.dto.multimode.MultimodeRuleNameDTOEnum;
8+
9+
import java.util.List;
10+
11+
public class MultimodeMapper {
12+
13+
/**
14+
* Compute the multimode into its Pogues model.
15+
* @param multimodeDTO Multimode to convert
16+
*/
17+
public static Multimode toModel(MultimodeDTO multimodeDTO) {
18+
Multimode multimode = new Multimode();
19+
Rules leafRules = new Rules();
20+
if (multimodeDTO.getLeaf() != null) {
21+
leafRules.getRules().addAll(toModel(multimodeDTO.getLeaf().getRules()));
22+
multimode.setLeaf(leafRules);
23+
}
24+
Rules questionnaireRules = new Rules();
25+
if (multimodeDTO.getQuestionnaire() != null) {
26+
questionnaireRules.getRules().addAll(toModel(multimodeDTO.getQuestionnaire().getRules()));
27+
multimode.setQuestionnaire(questionnaireRules);
28+
}
29+
return multimode;
30+
}
31+
32+
private static List<Rule> toModel(List<MultimodeRuleDTO> rulesDTO) {
33+
return rulesDTO.stream().map(MultimodeMapper::toModel).toList();
34+
}
35+
36+
private static Rule toModel(MultimodeRuleDTO ruleDTO) {
37+
Rule rule = new Rule();
38+
rule.setType(ValueTypeEnum.VTL);
39+
rule.setValue(ruleDTO.getValue());
40+
rule.setName(toModel(ruleDTO.getName()));
41+
return rule;
42+
}
43+
44+
private static MultimodeRuleNameEnum toModel(MultimodeRuleNameDTOEnum name) {
45+
return switch (name) {
46+
case MultimodeRuleNameDTOEnum.IS_MOVED -> MultimodeRuleNameEnum.IS_MOVED;
47+
case MultimodeRuleNameDTOEnum.IS_SPLIT -> MultimodeRuleNameEnum.IS_SPLIT;
48+
};
49+
}
50+
51+
/**
52+
* Compute the articulation into its DTO
53+
* @param multimode Multimode to convert
54+
*/
55+
public static MultimodeDTO toDTO(Multimode multimode) {
56+
if (multimode == null) return new MultimodeDTO();
57+
58+
MultimodeItemDTO multimodeQuestionnaireDTO = multimode.getQuestionnaire() == null ? null : new MultimodeItemDTO(toDTO(multimode.getQuestionnaire().getRules()));
59+
MultimodeItemDTO multimodeLeafDTO = multimode.getLeaf() == null ? null : new MultimodeItemDTO(toDTO(multimode.getLeaf().getRules()));
60+
61+
return new MultimodeDTO(multimodeQuestionnaireDTO, multimodeLeafDTO);
62+
}
63+
64+
private static List<MultimodeRuleDTO> toDTO(List<Rule> rules) {
65+
return rules.stream().map(MultimodeMapper::toDTO).toList();
66+
}
67+
68+
private static MultimodeRuleDTO toDTO(Rule rule) {
69+
return new MultimodeRuleDTO(toDTO(rule.getName()), rule.getValue());
70+
}
71+
72+
private static MultimodeRuleNameDTOEnum toDTO(MultimodeRuleNameEnum name) {
73+
return switch (name) {
74+
case MultimodeRuleNameEnum.IS_MOVED -> MultimodeRuleNameDTOEnum.IS_MOVED;
75+
case MultimodeRuleNameEnum.IS_SPLIT -> MultimodeRuleNameDTOEnum.IS_SPLIT;
76+
};
77+
}
78+
79+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package fr.insee.pogues.model.dto.multimode;
2+
3+
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.Setter;
9+
10+
/**
11+
* Multimode can be set to switch how we collect data from the respondent (e.g. from web to one-on-one).
12+
*/
13+
@Getter
14+
@Setter
15+
@NoArgsConstructor
16+
@AllArgsConstructor
17+
@JsonInclude(JsonInclude.Include.NON_NULL)
18+
public class MultimodeDTO {
19+
/** Rules that apply to the global variables of a questionnaire. */
20+
private MultimodeItemDTO questionnaire;
21+
/** Rules that apply to the variables of a roundabout. */
22+
private MultimodeItemDTO leaf;
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package fr.insee.pogues.model.dto.multimode;
2+
3+
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.Setter;
9+
10+
import java.util.List;
11+
12+
@Getter
13+
@Setter
14+
@NoArgsConstructor
15+
@AllArgsConstructor
16+
@JsonInclude(JsonInclude.Include.NON_NULL)
17+
public class MultimodeItemDTO {
18+
private List<MultimodeRuleDTO> rules;
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package fr.insee.pogues.model.dto.multimode;
2+
3+
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import lombok.Setter;
9+
10+
@Getter
11+
@Setter
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
@JsonInclude(JsonInclude.Include.NON_NULL)
15+
public class MultimodeRuleDTO {
16+
private MultimodeRuleNameDTOEnum name;
17+
/** VTL formula that will trigger the rule when true. */
18+
private String value;
19+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package fr.insee.pogues.model.dto.multimode;
2+
3+
import lombok.Getter;
4+
5+
@Getter
6+
public enum MultimodeRuleNameDTOEnum {
7+
/** Respondent has moved. */
8+
IS_MOVED,
9+
/** Respondent is no longer in this household. */
10+
IS_SPLIT
11+
}

src/main/java/fr/insee/pogues/service/ArticulationService.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
import java.util.UUID;
2020

2121
import static fr.insee.pogues.utils.json.JSONFunctions.jsonStringtoJsonNode;
22-
import static fr.insee.pogues.utils.model.PoguesModelUtils.getQuestionnaireRoundabout;
22+
import static fr.insee.pogues.utils.model.PoguesModelUtils.*;
2323

2424
/**
2525
* <p>Service used to fetch, update or delete the articulation of a questionnaire.</p>
26-
* <p>A questionnaire can only use this feature if its language formula is VTL.</p>
26+
* <p>A questionnaire can only use this feature if its language formula is VTL, and it has a roundabout.</p>
2727
*/
2828
@Service
2929
@Slf4j
@@ -53,7 +53,7 @@ private Questionnaire retrieveQuestionnaireByVersionId(UUID versionId) throws Ex
5353
* @throws QuestionnaireRoundaboutNotFoundException Questionnaire does not have a roundabout
5454
*/
5555
private void checkQuestionnaireCompatibility(Questionnaire questionnaire) throws QuestionnaireFormulaLanguageNotVTLException, QuestionnaireRoundaboutNotFoundException {
56-
if (!isQuestionnaireFormulaLanguageInVTL(questionnaire)) {
56+
if (!isQuestionnaireFormulaLanguageVTL(questionnaire)) {
5757
String message = String.format("Questionnaire with id %s has the formula language %s and not VTL", questionnaire.getId(), questionnaire.getFormulasLanguage());
5858
throw new QuestionnaireFormulaLanguageNotVTLException(message);
5959
}
@@ -63,14 +63,6 @@ private void checkQuestionnaireCompatibility(Questionnaire questionnaire) throws
6363
}
6464
}
6565

66-
/**
67-
* Check if the questionnaire's formula language is VTL.
68-
* @param questionnaire Questionnaire to check
69-
*/
70-
private boolean isQuestionnaireFormulaLanguageInVTL(Questionnaire questionnaire) {
71-
return FormulasLanguageEnum.VTL.equals(questionnaire.getFormulasLanguage());
72-
}
73-
7466
/**
7567
* Fetch the articulation of a questionnaire.
7668
* @param questionnaireId ID of the questionnaire to fetch the articulation from
@@ -110,7 +102,7 @@ private Articulation getQuestionnaireArticulation(Questionnaire questionnaire) {
110102
}
111103

112104
/**
113-
* Update or create a new variable in the questionnaire.
105+
* Update or create a new articulation in the questionnaire.
114106
* It will update the questionnaire's last updated date.
115107
* @param questionnaireId ID of the questionnaire to update
116108
* @param articulation Articulation to upsert

0 commit comments

Comments
 (0)