Skip to content

Commit 648843f

Browse files
committed
Merge branch 'devModeleFiliere' of https://github.com/InseeFr/Genesis-API into devModeleFiliere
2 parents 00d942a + 6571774 commit 648843f

File tree

14 files changed

+86
-163
lines changed

14 files changed

+86
-163
lines changed

src/main/java/fr/insee/genesis/controller/dto/SurveyUnitSimplified.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class SurveyUnitSimplified {
1515
/**
1616
* @deprecated We will not reveive this piece of information anymore
1717
*/
18-
@Deprecated(forRemoval = true)
18+
@Deprecated(forRemoval = true, since = "2026-01-01")
1919
private String campaignId;
2020
private String interrogationId;
2121
private String usualSurveyUnitId;

src/main/java/fr/insee/genesis/controller/rest/responses/RawResponseController.java

Lines changed: 1 addition & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,12 @@
4646
import java.util.ArrayList;
4747
import java.util.List;
4848
import java.util.Map;
49-
import java.util.stream.Collectors;
5049

5150
@Slf4j
5251
@Controller
5352
public class RawResponseController {
5453

5554
private static final String SUCCESS_MESSAGE = "Interrogation %s saved";
56-
private static final String PARTITION_ID = "partitionId";
5755
private static final String INTERROGATION_ID = "interrogationId";
5856
private final LunaticJsonRawDataApiPort lunaticJsonRawDataApiPort;
5957
private final RawResponseApiPort rawResponseApiPort;
@@ -98,57 +96,6 @@ public ResponseEntity<String> saveRawResponsesFromJsonBody(
9896
return ResponseEntity.status(201).body(String.format(SUCCESS_MESSAGE, interrogationId));
9997
}
10098

101-
/* @Operation(summary = "Deprecated")
102-
@PutMapping(path="/lunatic-json")
103-
@PreAuthorize("hasRole('COLLECT_PLATFORM')")
104-
// Check version when merging
105-
@Deprecated(since="1.13.0", forRemoval=true)
106-
public ResponseEntity<String> saveRawResponsesFromJsonBodyWithValidationDeprecated(
107-
@RequestBody Map<String, Object> body
108-
) {
109-
110-
SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(Dialects.getDraft202012(), SchemaRegistry.Builder::build);
111-
Schema jsonSchema = schemaRegistry
112-
.getSchema(RawResponseController.class.getResourceAsStream("/modele-filiere-spec/RawResponse.json")
113-
);
114-
try {
115-
if (jsonSchema == null) {
116-
throw new GenesisException(500, "No RawResponse json schema has been found");
117-
}
118-
List<Error> errors = jsonSchema.validate(
119-
new ObjectMapper().readTree(
120-
new ObjectMapper().writeValueAsString(body)
121-
)
122-
);
123-
// Throw Genesis exception if errors are present
124-
validate(errors);
125-
//Check required ids
126-
checkRequiredIds(body);
127-
} catch (JsonProcessingException jpe) {
128-
return ResponseEntity.status(400).body(jpe.toString());
129-
} catch (GenesisException ge) {
130-
return ResponseEntity.status(ge.getStatus()).body(ge.getMessage());
131-
}
132-
133-
LunaticJsonRawDataModel rawData = LunaticJsonRawDataModel.builder()
134-
.campaignId(body.get(PARTITION_ID).toString())
135-
.questionnaireId(body.get("questionnaireModelId").toString().toUpperCase())
136-
.interrogationId(body.get(INTERROGATION_ID).toString())
137-
.idUE(body.get("surveyUnitId").toString())
138-
.mode(Mode.getEnumFromJsonName(body.get("mode").toString()))
139-
.data(body)
140-
.recordDate(LocalDateTime.now())
141-
.build();
142-
try {
143-
lunaticJsonRawDataApiPort.save(rawData);
144-
} catch (Exception e) {
145-
return ResponseEntity.status(500).body("Unexpected error");
146-
}
147-
148-
log.info("Data saved for interrogationId {} and partition {}", body.get(INTERROGATION_ID).toString(),
149-
body.get(PARTITION_ID).toString());
150-
return ResponseEntity.status(201).body(String.format(SUCCESS_MESSAGE, body.get(INTERROGATION_ID).toString()));
151-
}*/
15299

153100
@Operation(summary = "Save lunatic json data from one interrogation in Genesis Database (with json " +
154101
"schema validation)")
@@ -249,7 +196,7 @@ public ResponseEntity<List<String>> getUnprocessedCollectionInstrument(){
249196
@Operation(summary = "Get campaign id and interrogationId from all unprocessed raw json data")
250197
@GetMapping(path = "/responses/raw/lunatic-json/get/unprocessed")
251198
@PreAuthorize("hasRole('SCHEDULER')")
252-
public ResponseEntity<List<LunaticJsonRawDataUnprocessedDto>> getUnproccessedJsonRawData() {
199+
public ResponseEntity<List<LunaticJsonRawDataUnprocessedDto>> getUnprocessedJsonRawData() {
253200
log.info("Try to get unprocessed raw JSON datas...");
254201
return ResponseEntity.ok(lunaticJsonRawDataApiPort.getUnprocessedDataIds());
255202
}
@@ -337,31 +284,5 @@ public ResponseEntity<PagedModel<LunaticJsonRawDataModel>> getRawResponsesFromJs
337284
return ResponseEntity.status(HttpStatus.OK).body(new PagedModel<>(rawResponses));
338285
}
339286

340-
private void validate(List<Error> errors) throws GenesisException {
341-
if (!errors.isEmpty()) {
342-
String errorMessage = errors.stream()
343-
.map(Error::getMessage)
344-
.collect(Collectors.joining(System.lineSeparator() + " - "));
345-
346-
throw new GenesisException(
347-
400,
348-
"Input data JSON is not valid: %n - %s".formatted(errorMessage)
349-
);
350-
}
351-
}
352-
353-
private void checkRequiredIds(Map<String, Object> body) throws GenesisException {
354-
for (String requiredKey : List.of(
355-
PARTITION_ID,
356-
"questionnaireModelId",
357-
INTERROGATION_ID,
358-
"surveyUnitId",
359-
"mode"
360-
)) {
361-
if (body.get(requiredKey) == null) {
362-
throw new GenesisException(400, "No %s found in body".formatted(requiredKey));
363-
}
364-
}
365-
}
366287

367288
}

src/main/java/fr/insee/genesis/controller/rest/responses/ResponseController.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.stereotype.Controller;
4444
import org.springframework.web.bind.annotation.DeleteMapping;
4545
import org.springframework.web.bind.annotation.GetMapping;
46+
import org.springframework.web.bind.annotation.PathVariable;
4647
import org.springframework.web.bind.annotation.PostMapping;
4748
import org.springframework.web.bind.annotation.PutMapping;
4849
import org.springframework.web.bind.annotation.RequestBody;
@@ -67,7 +68,7 @@
6768

6869
@RequestMapping(path = "/responses" )
6970
@Controller
70-
@Tag(name = "Response services", description = "A **response** is considered the entire set of data associated with an interrogation (survey unit x questionnaireId). \n\n These data may have different state (collected, edited, external, ...) ")
71+
@Tag(name = "Response services", description = "A **response** is considered the entire set of data associated with an interrogation (survey unit x collecton Intrument Id [ex questionnaire]). \n\n These data may have different state (collected, edited, external, ...) ")
7172
@Slf4j
7273
public class ResponseController implements CommonApiResponse {
7374

@@ -101,14 +102,15 @@ public ResponseController(SurveyUnitApiPort surveyUnitService,
101102
}
102103

103104
//SAVE
105+
@Deprecated(since = "2026-01-01")
104106
@Operation(summary = "Save one file of responses to Genesis Database, passing its path as a parameter")
105107
@PutMapping(path = "/lunatic-xml/save-one")
106108
@PreAuthorize("hasRole('ADMIN')")
107109
public ResponseEntity<Object> saveResponsesFromXmlFile(@RequestParam("pathLunaticXml") String xmlFile,
108110
@RequestParam(value = "pathSpecFile") String metadataFilePath,
109111
@RequestParam(value = "mode") Mode modeSpecified
110112
)throws Exception {
111-
log.info("Try to read Xml file : {}", xmlFile);
113+
log.info("Try to read one Xml file : {}", xmlFile);
112114
Path filepath = Paths.get(xmlFile);
113115

114116
if (getFileSizeInMB(filepath) <= Constants.MAX_FILE_SIZE_UNTIL_SEQUENTIAL) {
@@ -117,6 +119,7 @@ public ResponseEntity<Object> saveResponsesFromXmlFile(@RequestParam("pathLunati
117119
return processXmlFileSequentially(filepath, modeSpecified, metadataFilePath);
118120
}
119121

122+
@Deprecated(since = "2026-01-01")
120123
@Operation(summary = "Save multiple files to Genesis Database from the campaign root folder")
121124
@PutMapping(path = "/lunatic-xml/save-folder")
122125
@PreAuthorize("hasRole('ADMIN')")
@@ -149,6 +152,7 @@ public ResponseEntity<Object> saveResponsesFromXmlCampaignFolder(@RequestParam("
149152
}
150153

151154
//SAVE ALL
155+
@Deprecated(since = "2026-01-01")
152156
@Operation(summary = "Save all files to Genesis Database (differential data folder only), regardless of the campaign")
153157
@PutMapping(path = "/lunatic-xml/save-all-campaigns")
154158
@PreAuthorize("hasRole('SCHEDULER')")
@@ -186,9 +190,9 @@ public ResponseEntity<Object> saveResponsesFromAllCampaignFolders(){
186190

187191
//DELETE
188192
@Operation(summary = "Delete all responses associated with a collection instrument (formerly questionnaire)")
189-
@DeleteMapping(path = "/delete/by-collection-instrument")
193+
@DeleteMapping(path = "/delete/{collectionInstrumentId}")
190194
@PreAuthorize("hasRole('ADMIN')")
191-
public ResponseEntity<Object> deleteAllResponsesByCollectionInstrument(@RequestParam("collectionInstrumentId") String collectionInstrumentId) {
195+
public ResponseEntity<Object> deleteAllResponsesByCollectionInstrument(@PathVariable("collectionInstrumentId") String collectionInstrumentId) {
192196
log.info("Try to delete all responses of collection instrument : {}", collectionInstrumentId);
193197
Long ndDocuments = surveyUnitService.deleteByCollectionInstrumentId(collectionInstrumentId);
194198
log.info("{} responses deleted", ndDocuments);
@@ -211,14 +215,13 @@ public ResponseEntity<List<SurveyUnitModel>> findResponsesByInterrogationAndColl
211215
* @deprecated
212216
* This endpoint is deprecated because the parameter `questionnaireId` has been renamed
213217
* to `collectionInstrumentId` in the Information System (modeled in the modelefiliere library).
214-
*
215218
* A new endpoint using the updated parameter names will be provided to remain compliant with
216219
* the current data model. This endpoint will be removed once all dependent APIs have adopted
217220
* the new naming convention.
218221
*
219222
* Use the new endpoint with `collectionInstrumentId` for future implementations.
220223
*/
221-
@Deprecated(forRemoval = true)
224+
@Deprecated(forRemoval = true, since= "2026-01-01")
222225
@Operation(summary = "Retrieve responses for an interrogation, using interrogationId and questionnaireId from Genesis Database with the latest value for each available state of every variable",
223226
description = "use /by-interrogation-and-collection-instrument/latest-states instead")
224227
@GetMapping(path = "/by-ue-and-questionnaire/latest-states",
@@ -248,8 +251,7 @@ public ResponseEntity<Object> findResponsesByInterrogationAndCollectionInstrumen
248251
@RequestParam("interrogationId") String interrogationId,
249252
@RequestParam("collectionInstrumentId") String collectionInstrumentId) throws GenesisException {
250253
//Check context
251-
DataProcessingContextModel dataProcessingContextModel =
252-
contextService.getContext(interrogationId);
254+
DataProcessingContextModel dataProcessingContextModel = contextService.getContext(interrogationId);
253255

254256
if(dataProcessingContextModel == null || !dataProcessingContextModel.isWithReview()){
255257
return ResponseEntity.status(403).body(new ApiError("Review is disabled for that partition"));
@@ -347,7 +349,7 @@ public ResponseEntity<List<SurveyUnitSimplified>> getLatestForInterrogationListV
347349
//!!!WARNING!!! : FOR PERFORMANCES PURPOSES, WE DONT'MAKE REQUESTS ON INDIVIDUAL ELEMENTS ANYMORE, BUT ON A SUBLIST OF THE INPUTLIST
348350
final int SUBBLOCK_SIZE = 100;
349351
int offset = 0;
350-
List<InterrogationId> interrogationIdsSubList = null;
352+
List<InterrogationId> interrogationIdsSubList;
351353

352354
for(String mode : modes) {
353355

src/main/java/fr/insee/genesis/controller/utils/ExtendedJsonNormalizer.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package fr.insee.genesis.controller.utils;
22

33
import com.fasterxml.jackson.databind.JsonNode;
4-
import com.fasterxml.jackson.databind.node.*;
5-
6-
import java.util.Iterator;
7-
import java.util.Map;
4+
import com.fasterxml.jackson.databind.node.ArrayNode;
5+
import com.fasterxml.jackson.databind.node.ObjectNode;
6+
import com.fasterxml.jackson.databind.node.TextNode;
87
public class ExtendedJsonNormalizer {
98

9+
public static final String $_DATE = "$date";
10+
1011
private ExtendedJsonNormalizer() {}
1112

1213
/**
1314
* Recursively converts Mongo Extended JSON objects into simple types expected by the schema:
1415
* - {"$date": "..."} -> TextNode("...")
15-
*
1616
* Leaves all other values untouched. Returns a structural copy of the node (does not mutate the original).
1717
*/
1818

@@ -23,20 +23,18 @@ public static JsonNode normalize(JsonNode node) {
2323
ObjectNode obj = (ObjectNode) node;
2424

2525
if (obj.size() == 1) {
26-
if (obj.has("$date") && obj.get("$date").isTextual()) {
27-
return TextNode.valueOf(obj.get("$date").asText());
26+
if (obj.has($_DATE) && obj.get($_DATE).isTextual()) {
27+
return TextNode.valueOf(obj.get($_DATE).asText());
2828
}
2929
// if (obj.has("$oid") && obj.get("$oid").isTextual()) {
3030
// return TextNode.valueOf(obj.get("$oid").asText());
3131
// }
3232
}
3333

3434
ObjectNode copy = obj.objectNode();
35-
Iterator<Map.Entry<String, JsonNode>> it = obj.fields();
36-
while (it.hasNext()) {
37-
Map.Entry<String, JsonNode> e = it.next();
38-
copy.set(e.getKey(), normalize(e.getValue()));
39-
}
35+
obj.fields().forEachRemaining(e ->
36+
copy.set(e.getKey(), normalize(e.getValue()))
37+
);
4038
return copy;
4139
}
4240

src/main/java/fr/insee/genesis/controller/utils/JsonSchemaValidator.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,23 @@ public static JsonSchema loadSchemaFromClasspath(String resourcePath, ObjectMapp
4242
}
4343
String cp = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath;
4444
try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(cp)) {
45-
if (in == null) throw new IOException("Schema not found on classpath: " + resourcePath);
45+
if (in == null){ throw new IOException("Schema not found on classpath: " + resourcePath);}
4646
JsonNode schemaNode = mapper.readTree(in);
4747
return JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012).getSchema(schemaNode);
4848
}
4949
}
5050

5151
private static void ensureValid(JsonNode root, JsonSchema schema) throws SchemaValidationException {
52-
log.info("Schéma Apply : " + schema.getSchemaNode().get("title"));
52+
log.info("Schéma Apply : {}", schema.getSchemaNode().get("title"));
5353
java.util.Set<ValidationMessage> errors = schema.validate(root);
5454
if (!errors.isEmpty()) {
5555
String formatted = errors.stream()
5656
.sorted(java.util.Comparator.comparing(ValidationMessage::getEvaluationPath))
57-
.map(err -> err.getMessage())
57+
.map(ValidationMessage::getMessage)
5858
.collect(java.util.stream.Collectors.joining("\n"));
5959
throw new SchemaValidationException(
6060
"Uploaded JSON is not correct according to the json-schema:\n" + formatted, errors);
6161
}
62-
log.info("Schema-compliant JSON : " + schema.getSchemaNode().get("title"));
62+
log.info("Schema-compliant JSON : {}", schema.getSchemaNode().get("title"));
6363
}
6464
}

src/main/java/fr/insee/genesis/domain/model/surveyunit/Mode.java

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import lombok.Getter;
66
import org.springframework.lang.Nullable;
77

8+
import java.util.HashMap;
9+
import java.util.Map;
10+
811
@Getter
912
public enum Mode {
1013

@@ -28,41 +31,42 @@ public enum Mode {
2831
this.jsonName = jsonName;
2932
}
3033

31-
@JsonCreator
32-
public static Mode fromString(String value) {
33-
if (value == null) return null;
34-
35-
for (Mode m : values()) {
36-
if (value.equalsIgnoreCase(m.modeName) ||
37-
value.equalsIgnoreCase(m.jsonName)) {
38-
return m;
39-
}
40-
}
41-
throw new IllegalArgumentException("Invalid Mode: " + value);
42-
}
34+
// lookup Maps
35+
private static final Map<String, Mode> BY_MODE_NAME = new HashMap<>();
36+
private static final Map<String, Mode> BY_JSON_NAME = new HashMap<>();
37+
private static final Map<String, Mode> BY_ANY_NAME = new HashMap<>();
4338

44-
public static Mode getEnumFromModeName(String modeName) {
45-
if (modeName == null){
46-
return null;
47-
}
48-
for (Mode mode : Mode.values()) {
49-
if (modeName.equals(mode.getModeName())) {
50-
return mode;
51-
}
52-
}
53-
return null;
54-
}
39+
static {
40+
for (Mode mode : values()) {
41+
if (mode.modeName != null && !mode.modeName.isBlank()) {
42+
BY_MODE_NAME.put(mode.modeName.toUpperCase(), mode);
43+
BY_ANY_NAME.put(mode.modeName.toUpperCase(), mode);
44+
}
45+
if (mode.jsonName != null && !mode.jsonName.isBlank()) {
46+
BY_JSON_NAME.put(mode.jsonName.toUpperCase(), mode);
47+
BY_ANY_NAME.put(mode.jsonName.toUpperCase(), mode);
48+
}
49+
}
50+
}
5551

56-
public static Mode getEnumFromJsonName(String modeName) {
57-
if (modeName == null){
58-
return null;
59-
}
60-
for (Mode mode : Mode.values()) {
61-
if (modeName.equals(mode.getJsonName())) {
62-
return mode;
63-
}
64-
}
65-
return null;
66-
}
52+
@JsonCreator
53+
public static Mode fromString(String value) {
54+
if (value == null){ return null;}
55+
56+
Mode mode = BY_ANY_NAME.get(value.trim().toUpperCase());
57+
if (mode != null) { return mode; }
58+
59+
throw new IllegalArgumentException("Invalid Mode: " + value);
60+
}
61+
62+
public static Mode getEnumFromModeName(@Nullable String modeName) {
63+
if (modeName == null) { return null; }
64+
return BY_MODE_NAME.get(modeName.trim().toUpperCase());
65+
}
66+
67+
public static Mode getEnumFromJsonName(@Nullable String jsonName) {
68+
if (jsonName == null) { return null; }
69+
return BY_JSON_NAME.get(jsonName.trim().toUpperCase());
70+
}
6771

6872
}

src/main/java/fr/insee/genesis/domain/model/surveyunit/SurveyUnitModel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class SurveyUnitModel {
2727
/**
2828
* @deprecated We will not receive this identifier anymore
2929
*/
30-
@Deprecated(forRemoval = true)
30+
@Deprecated(forRemoval = true, since = "2026-01-01")
3131
private String campaignId;
3232
private String interrogationId;
3333
// New name of idUE

0 commit comments

Comments
 (0)