Skip to content

Commit acf64ab

Browse files
committed
Merge branch 'main' into devScheduleKraftwerkEndPoint
2 parents 5bbf56c + 0b3ed5d commit acf64ab

49 files changed

Lines changed: 1006 additions & 171 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/create-release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ jobs:
153153
echo "EOF"
154154
} >> $GITHUB_OUTPUT
155155
156-
- uses: softprops/action-gh-release@v2
156+
- uses: softprops/action-gh-release@v3
157157
with:
158158
tag_name: ${{ needs.check-version.outputs.release-tag }}
159159
target_commitish: ${{ github.head_ref || github.ref }}

.github/workflows/docker.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ name: Build snapshot docker image
33
on:
44
push:
55
branches-ignore:
6-
- main
6+
- * # main
7+
# turned off for now (needs secrets renewal)
78

89
jobs:
910
build-snapshot:

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# Changelog
2+
3+
## 2.4.7 [2026-04-10]
4+
### Added
5+
- Reprocess endpoints for raw responses with optional sinceDate and endDate filters
6+
27
## 2.4.0 [2026-03-04]
38
### Added
49
- Restful endpoints to get latest responses without the logic in it (not to be confused with get latest states)

pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>fr.insee.genesis</groupId>
66
<artifactId>genesis-api</artifactId>
7-
<version>2.4.5</version>
7+
<version>2.4.6</version>
88
<packaging>jar</packaging>
99
<name>genesis-api</name>
1010

@@ -16,9 +16,9 @@
1616

1717
<properties>
1818
<java.version>25</java.version>
19-
<springdoc.version>2.8.16</springdoc.version>
19+
<springdoc.version>2.8.17</springdoc.version>
2020
<mapstruct.version>1.6.3</mapstruct.version>
21-
<cucumber.version>7.34.2</cucumber.version>
21+
<cucumber.version>7.34.3</cucumber.version>
2222
<junit-jupiter.version>5.13.4</junit-jupiter.version>
2323
<jackson-bom.version>2.21.1</jackson-bom.version>
2424

@@ -36,7 +36,7 @@
3636
<!-- Pi Test-->
3737
<pitest.version>1.23.0</pitest.version>
3838
<pitest.junit.version>1.2.3</pitest.junit.version>
39-
<bpm.version>1.1.1</bpm.version>
39+
<bpm.version>1.1.2</bpm.version>
4040
</properties>
4141
<dependencies>
4242
<dependency>
@@ -115,7 +115,7 @@
115115
<dependency>
116116
<groupId>com.networknt</groupId>
117117
<artifactId>json-schema-validator</artifactId>
118-
<version>3.0.1</version>
118+
<version>3.0.2</version>
119119
</dependency>
120120

121121
<!-- generate implementation auto -->
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package fr.insee.genesis.controller.exception;
2+
3+
import fr.insee.genesis.exceptions.GenesisException;
4+
import fr.insee.genesis.exceptions.InvalidDateIntervalException;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.http.ProblemDetail;
8+
import org.springframework.web.bind.annotation.ControllerAdvice;
9+
import org.springframework.web.bind.annotation.ExceptionHandler;
10+
11+
/**
12+
* This controller uses Spring's ControllerAdvice annotation to intercept exceptions.
13+
* It implements the <a href="https://www.rfc-editor.org/rfc/rfc9457.html">RFC 9457</a> by returning
14+
* Spring's <code>ProblemDetail</code> object.
15+
*/
16+
@ControllerAdvice
17+
@Slf4j
18+
public class ExceptionController {
19+
20+
// Note: No handler for uncaught Exception.class for now since it breaks soms tests.
21+
22+
@ExceptionHandler
23+
public ProblemDetail handleGenericGenesisException(GenesisException genesisException) {
24+
log.error("GenesisException: {}", genesisException.getMessage(), genesisException);
25+
return ProblemDetail.forStatusAndDetail(
26+
resolveHttpCode(genesisException.getStatus()),
27+
genesisException.getMessage());
28+
}
29+
30+
/** Returns the corresponding http status, or 500 if the given code does not match a http status. */
31+
private static HttpStatus resolveHttpCode(int statusCode) {
32+
HttpStatus httpStatus = HttpStatus.resolve(statusCode);
33+
return httpStatus != null ? httpStatus : HttpStatus.INTERNAL_SERVER_ERROR;
34+
}
35+
36+
@ExceptionHandler(InvalidDateIntervalException.class)
37+
public ProblemDetail handleInvalidDateIntervalException(InvalidDateIntervalException e) {
38+
log.error("InvalidDateIntervalException: {}", e.getMessage());
39+
return ProblemDetail.forStatusAndDetail(
40+
HttpStatus.BAD_REQUEST,
41+
e.getMessage());
42+
}
43+
44+
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.springframework.web.bind.annotation.RequestMapping;
1616
import org.springframework.web.bind.annotation.RequestParam;
1717

18+
import java.time.Instant;
1819
import java.time.LocalDateTime;
1920
import java.util.List;
2021

@@ -55,17 +56,17 @@ public ResponseEntity<List<InterrogationId>> getAllInterrogationIdsByCollectionI
5556
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
5657
@Parameter(
5758
description = "sinceDate",
58-
schema = @Schema(type = "string", format = "date-time", example = "2026-01-01T00:00:00")
59+
schema = @Schema(type = "string", format = "date-time", example = "2026-01-01T00:00:00Z")
5960
)
60-
LocalDateTime start,
61+
Instant start,
6162

6263
@RequestParam("end")
6364
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
6465
@Parameter(
6566
description = "untilDate",
66-
schema = @Schema(type = "string", format = "date-time", example = "2026-01-31T23:59:59")
67+
schema = @Schema(type = "string", format = "date-time", example = "2026-01-31T23:59:59Z")
6768
)
68-
LocalDateTime end) {
69+
Instant end) {
6970
List<InterrogationId> responses = surveyUnitService.findDistinctInterrogationIdsByCollectionInstrumentIdAndRecordDateBetween(collectionInstrumentId, start,end);
7071
return ResponseEntity.ok(responses);
7172
}

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

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import io.swagger.v3.oas.annotations.Operation;
1616
import io.swagger.v3.oas.annotations.Parameter;
1717
import jakarta.validation.Valid;
18+
import lombok.RequiredArgsConstructor;
1819
import lombok.extern.slf4j.Slf4j;
1920
import org.springframework.data.domain.Page;
2021
import org.springframework.data.domain.PageRequest;
@@ -43,23 +44,16 @@
4344

4445
@Slf4j
4546
@Controller
47+
@RequiredArgsConstructor
4648
public class RawResponseController {
4749

4850
private static final String SUCCESS_MESSAGE = "Interrogation %s saved";
4951
private static final String INTERROGATION_ID = "interrogationId";
50-
public static final String NB_DOCS_WITH_FORMATTED = "%d document(s) processed, including %d FORMATTED after data verification for collectionInstrumentId %s";
51-
public static final String NB_DOCS = "%d document(s) processed for collectionInstrumentId %s";
52+
5253
private final LunaticJsonRawDataApiPort lunaticJsonRawDataApiPort;
5354
private final RawResponseApiPort rawResponseApiPort;
5455
private final RawResponseInputRepository rawRepository;
5556

56-
57-
public RawResponseController(LunaticJsonRawDataApiPort lunaticJsonRawDataApiPort, RawResponseApiPort rawResponseApiPort, RawResponseInputRepository rawRepository) {
58-
this.lunaticJsonRawDataApiPort = lunaticJsonRawDataApiPort;
59-
this.rawResponseApiPort = rawResponseApiPort;
60-
this.rawRepository = rawRepository;
61-
}
62-
6357
@Operation(summary = "Save lunatic json data from one interrogation in Genesis Database")
6458
@PutMapping(path = "/responses/raw/lunatic-json/save")
6559
@PreAuthorize("hasRole('COLLECT_PLATFORM')")
@@ -118,11 +112,8 @@ public ResponseEntity<String> processRawResponses(
118112
log.info("Try to process raw responses for collectionInstrumentId {} and {} interrogationIds", collectionInstrumentId, interrogationIdList.size());
119113
List<GenesisError> errors = new ArrayList<>();
120114
try {
121-
DataProcessResult result = rawResponseApiPort.processRawResponses(collectionInstrumentId, interrogationIdList, errors);
122-
return result.formattedDataCount() == 0 ?
123-
ResponseEntity.ok(NB_DOCS.formatted(result.dataCount(), collectionInstrumentId))
124-
: ResponseEntity.ok(NB_DOCS_WITH_FORMATTED
125-
.formatted(result.dataCount(), result.formattedDataCount(), collectionInstrumentId));
115+
DataProcessResult result = rawResponseApiPort.processRawResponsesByInterrogationIds(collectionInstrumentId, interrogationIdList, errors);
116+
return ResponseEntity.ok(result.message(collectionInstrumentId));
126117
} catch (GenesisException e) {
127118
return ResponseEntity.status(e.getStatus()).body(e.getMessage());
128119
}
@@ -140,11 +131,8 @@ public ResponseEntity<String> processRawResponsesByCollectionInstrumentId(
140131
) {
141132
log.info("Try to process raw responses for collectionInstrumentId {}", collectionInstrumentId);
142133
try {
143-
DataProcessResult result = rawResponseApiPort.processRawResponses(collectionInstrumentId);
144-
return result.formattedDataCount() == 0 ?
145-
ResponseEntity.ok(NB_DOCS.formatted(result.dataCount(), collectionInstrumentId))
146-
: ResponseEntity.ok(NB_DOCS_WITH_FORMATTED
147-
.formatted(result.dataCount(), result.formattedDataCount(), collectionInstrumentId));
134+
DataProcessResult result = rawResponseApiPort.processRawResponsesByInterrogationIds(collectionInstrumentId);
135+
return ResponseEntity.ok(result.message(collectionInstrumentId));
148136
} catch (GenesisException e) {
149137
return ResponseEntity.status(e.getStatus()).body(e.getMessage());
150138
}
@@ -183,7 +171,7 @@ public ResponseEntity<LunaticJsonRawDataModel> getJsonRawData(
183171
@RequestParam("campaignName") String campaignName,
184172
@RequestParam(value = "mode") Mode modeSpecified
185173
) {
186-
List<LunaticJsonRawDataModel> data = lunaticJsonRawDataApiPort.getRawData(campaignName, modeSpecified, List.of(interrogationId));
174+
List<LunaticJsonRawDataModel> data = lunaticJsonRawDataApiPort.getRawDataByQuestionnaireId(campaignName, modeSpecified, List.of(interrogationId));
187175
return ResponseEntity.ok(data.getFirst());
188176
}
189177

@@ -201,7 +189,7 @@ public ResponseEntity<String> processJsonRawData(
201189
List<GenesisError> errors = new ArrayList<>();
202190

203191
try {
204-
DataProcessResult result = lunaticJsonRawDataApiPort.processRawData(campaignName, interrogationIdList, errors);
192+
DataProcessResult result = lunaticJsonRawDataApiPort.processRawDataByInterrogationIds(campaignName, interrogationIdList, errors);
205193
return result.formattedDataCount() == 0 ?
206194
ResponseEntity.ok("%d document(s) processed".formatted(result.dataCount()))
207195
: ResponseEntity.ok("%d document(s) processed, including %d FORMATTED after data verification"
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package fr.insee.genesis.controller.rest.responses;
2+
3+
import fr.insee.genesis.domain.model.surveyunit.rawdata.DataProcessResult;
4+
import fr.insee.genesis.domain.model.surveyunit.rawdata.RawDataModelType;
5+
import fr.insee.genesis.domain.ports.api.ReprocessRawResponseApiPort;
6+
import fr.insee.genesis.exceptions.GenesisException;
7+
import io.swagger.v3.oas.annotations.Operation;
8+
import io.swagger.v3.oas.annotations.Parameter;
9+
import io.swagger.v3.oas.annotations.media.Schema;
10+
import lombok.RequiredArgsConstructor;
11+
import lombok.extern.slf4j.Slf4j;
12+
import org.springframework.format.annotation.DateTimeFormat;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.security.access.prepost.PreAuthorize;
15+
import org.springframework.stereotype.Controller;
16+
import org.springframework.web.bind.annotation.PathVariable;
17+
import org.springframework.web.bind.annotation.PostMapping;
18+
import org.springframework.web.bind.annotation.RequestParam;
19+
20+
import java.time.Instant;
21+
22+
@Controller
23+
@RequiredArgsConstructor
24+
@Slf4j
25+
public class RawResponseReprocessController {
26+
27+
private final ReprocessRawResponseApiPort reprocessRawResponseApiPort;
28+
29+
@Operation(summary = "Reprocess raw response of a collection instrument.")
30+
@PostMapping(path = "/raw-responses/{collectionInstrumentId}/reprocess")
31+
@PreAuthorize("hasRole('ADMIN')")
32+
public ResponseEntity<String> reProcessRawResponsesByCollectionInstrumentId(
33+
@Parameter(
34+
description = "Id of the collection instrument (old questionnaireId)",
35+
example = "ENQTEST2025X00")
36+
@PathVariable("collectionInstrumentId")
37+
String collectionInstrumentId,
38+
39+
@Parameter(
40+
description = "Extract since",
41+
schema = @Schema(type = "string", format = "date-time", example = "2026-01-01T00:00:00Z")
42+
)
43+
@RequestParam(value = "sinceDate", required = false)
44+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
45+
Instant sinceDate,
46+
47+
@Parameter(
48+
description = "Extract until",
49+
schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T00:00:00Z")
50+
)
51+
@RequestParam(value = "endDate", required = false)
52+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
53+
Instant endDate
54+
) throws GenesisException {
55+
56+
DataProcessResult result = reprocessRawResponseApiPort.reprocessRawResponses(
57+
RawDataModelType.FILIERE,
58+
collectionInstrumentId,
59+
sinceDate,
60+
endDate);
61+
62+
return ResponseEntity.ok(result.message(collectionInstrumentId));
63+
}
64+
65+
@Operation(summary = "Reprocess Lunatic raw data for a questionnaire model. " +
66+
"**Note**: Lunatic raw data is the legacy format of raw responses.")
67+
@PostMapping(path = "/responses/raw/lunatic-json/{questionnaireId}/reprocess")
68+
@PreAuthorize("hasRole('ADMIN')")
69+
public ResponseEntity<String> reProcessJsonRawDataByQuestionnaireId(
70+
@Parameter(
71+
description = "Questionnaire model id (old name for collection instrument id).",
72+
example = "ENQTEST2025X00")
73+
@PathVariable("questionnaireId")
74+
String collectionInstrumentId, // 'questionnaireId' is the legacy name for 'collectionInstrumentId'
75+
76+
@Parameter(
77+
description = "Extract since",
78+
schema = @Schema(type = "string", format = "date-time", example = "2026-01-01T00:00:00Z")
79+
)
80+
@RequestParam(value = "sinceDate", required = false)
81+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
82+
Instant sinceDate,
83+
84+
@Parameter(
85+
description = "Extract until",
86+
schema = @Schema(type = "string", format = "date-time", example = "2026-02-02T00:00:00Z")
87+
)
88+
@RequestParam(value = "endDate", required = false)
89+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
90+
Instant endDate
91+
) throws GenesisException {
92+
93+
DataProcessResult result = reprocessRawResponseApiPort.reprocessRawResponses(
94+
RawDataModelType.LEGACY,
95+
collectionInstrumentId,
96+
sinceDate,
97+
endDate);
98+
99+
return ResponseEntity.ok(result.message(collectionInstrumentId));
100+
}
101+
102+
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,19 @@ public ControllerUtils(FileUtils fileUtils) {
2626

2727
/**
2828
* If a mode is specified, we treat only this mode.
29-
* If no mode is specified, we treat all modes in the campaign.
29+
* If no mode is specified, we treat all modes in the questionnaireId.
3030
* If no mode is specified and no specs are found, we return an error
31-
* @param campaign campaign id to get modes
31+
* @param questionnaireId questionnaireId id to get modes
3232
* @param modeSpecified a Mode to use, null if we want all modes available
3333
* @return a list with the mode in modeSpecified or all modes if null
3434
* @throws GenesisException if error in specs structure
3535
*/
36-
public List<Mode> getModesList(String campaign, Mode modeSpecified) throws GenesisException {
36+
public List<Mode> getModesList(String questionnaireId, Mode modeSpecified) throws GenesisException {
3737
if (modeSpecified != null){
3838
return Collections.singletonList(modeSpecified);
3939
}
4040
List<Mode> modes = new ArrayList<>();
41-
String specFolder = fileUtils.getSpecFolder(campaign);
41+
String specFolder = fileUtils.getSpecFolder(questionnaireId);
4242
List<String> modeSpecFolders = fileUtils.listFolders(specFolder);
4343
if (modeSpecFolders.isEmpty()) {
4444
throw new GenesisException(404, "No specification folder found " + specFolder);

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

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)