From 759e0810a6ee99ed1dfb7dd3875a5eea1e7f225b Mon Sep 17 00:00:00 2001
From: Laurent Caouissin <38245508+laurentC35@users.noreply.github.com>
Date: Wed, 11 Dec 2024 17:20:52 +0100
Subject: [PATCH] feat: restore version (#331)
* bump: pogues-model to 1.4.2-SNAPSHOT
* chore(Version): switch from Timestamp to ZonedDateTime
* test: DateUtils (use in many cases)
* chore: manage error when there is no result from db
* feat: implementation of restoring version
* chore: configure timeZone in props
* bump: .9.5-SNAPSHOT
* fix: owner missing (bump pogues-model to 1.4.2)
* bump: 4.9.5-SNAPSHOT.1
* bump: 4.9.5
---
pom.xml | 4 +-
src/main/java/fr/insee/pogues/Pogues.java | 13 +++++
.../pogues/domain/entity/db/Version.java | 4 +-
.../persistence/impl/VersionPostgresql.java | 29 ++++++++--
.../persistence/impl/VersionRowMapper.java | 4 +-
.../service/QuestionnairesServiceImpl.java | 2 +-
.../service/VersionServiceImpl.java | 22 +++++---
.../java/fr/insee/pogues/utils/DateUtils.java | 34 ++++++++++++
.../pogues/utils/json/JSONFunctions.java | 12 -----
.../webservice/rest/VersionController.java | 2 +-
src/main/resources/application.yaml | 2 +
.../fr/insee/pogues/utils/DateUtilsTest.java | 54 +++++++++++++++++++
.../transforms/PoguesJSONToPoguesXML/out.xml | 2 +-
13 files changed, 153 insertions(+), 31 deletions(-)
create mode 100644 src/main/java/fr/insee/pogues/utils/DateUtils.java
create mode 100644 src/test/java/fr/insee/pogues/utils/DateUtilsTest.java
diff --git a/pom.xml b/pom.xml
index 2e4ea835..b24fab77 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,14 +13,14 @@
fr.insee
Pogues-BO
jar
- 4.9.4
+ 4.9.5
Pogues-Back-Office
UTF-8
21
pogues-bo
- 1.4.0
+ 1.4.2
2.10
2.7.0
0.8.12
diff --git a/src/main/java/fr/insee/pogues/Pogues.java b/src/main/java/fr/insee/pogues/Pogues.java
index 0dfcf9c5..aa483bd3 100644
--- a/src/main/java/fr/insee/pogues/Pogues.java
+++ b/src/main/java/fr/insee/pogues/Pogues.java
@@ -1,7 +1,9 @@
package fr.insee.pogues;
import fr.insee.pogues.configuration.PropertiesLogger;
+import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.event.ApplicationReadyEvent;
@@ -10,12 +12,17 @@
import org.springframework.context.event.EventListener;
import org.springframework.transaction.annotation.EnableTransactionManagement;
+import java.util.TimeZone;
+
@SpringBootApplication(scanBasePackages = "fr.insee.pogues")
@EnableTransactionManagement
@ConfigurationPropertiesScan
@Slf4j
public class Pogues extends SpringBootServletInitializer {
+ @Value("${application.timezoneId}")
+ private String applicationTimeZoneId;
+
public static SpringApplicationBuilder configureApplicationBuilder(SpringApplicationBuilder springApplicationBuilder){
return springApplicationBuilder.sources(Pogues.class).listeners(new PropertiesLogger());
}
@@ -24,6 +31,12 @@ public static void main(String[] args) {
configureApplicationBuilder(new SpringApplicationBuilder()).build().run(args);
}
+ @PostConstruct
+ public void executeAfterMain() {
+ log.info("Timezone is set to '{}'", applicationTimeZoneId);
+ TimeZone.setDefault(TimeZone.getTimeZone(applicationTimeZoneId));
+ }
+
@EventListener
public void handleApplicationReady(ApplicationReadyEvent event) {
log.info("=============== Pogues Back-Office has successfully started. ===============");
diff --git a/src/main/java/fr/insee/pogues/domain/entity/db/Version.java b/src/main/java/fr/insee/pogues/domain/entity/db/Version.java
index 177c70bf..fcafa103 100644
--- a/src/main/java/fr/insee/pogues/domain/entity/db/Version.java
+++ b/src/main/java/fr/insee/pogues/domain/entity/db/Version.java
@@ -9,7 +9,7 @@
import lombok.Setter;
import java.sql.Date;
-import java.sql.Timestamp;
+import java.time.ZonedDateTime;
import java.util.UUID;
@@ -22,7 +22,7 @@ public class Version {
private UUID id;
private String poguesId;
- private Timestamp timestamp;
+ private ZonedDateTime timestamp;
private Date day;
private JsonNode data;
private String author;
diff --git a/src/main/java/fr/insee/pogues/persistence/impl/VersionPostgresql.java b/src/main/java/fr/insee/pogues/persistence/impl/VersionPostgresql.java
index 1269eed4..bf33f87c 100644
--- a/src/main/java/fr/insee/pogues/persistence/impl/VersionPostgresql.java
+++ b/src/main/java/fr/insee/pogues/persistence/impl/VersionPostgresql.java
@@ -2,16 +2,21 @@
import fr.insee.pogues.domain.entity.db.Version;
import fr.insee.pogues.persistence.repository.QuestionnaireVersionRepository;
+import fr.insee.pogues.webservice.rest.PoguesException;
import lombok.extern.slf4j.Slf4j;
import org.postgresql.util.PGobject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
+import static fr.insee.pogues.utils.DateUtils.convertZonedDateTimeToTimestamp;
+
@Service
@Slf4j
public class VersionPostgresql implements QuestionnaireVersionRepository {
@@ -36,7 +41,12 @@ public List getVersionsByQuestionnaireId(String poguesId, boolean withD
String qString =
"SELECT " + columns +
" FROM pogues_version pv WHERE pv.pogues_id = ? ORDER BY timestamp DESC;";
- return jdbcTemplate.query(qString, new VersionRowMapper(withData), poguesId);
+
+ List versions = jdbcTemplate.query(qString, new VersionRowMapper(withData), poguesId);
+ if(versions.isEmpty()){
+ throw new PoguesException(404, "Not found", "No version for poguesId "+ poguesId);
+ }
+ return versions;
}
@Override
@@ -45,7 +55,11 @@ public Version getLastVersionByQuestionnaireId(String poguesId, boolean withData
String qString =
"SELECT " + columns +
" FROM pogues_version pv WHERE pv.pogues_id = ? ORDER BY timestamp DESC LIMIT 1;";
- return jdbcTemplate.queryForObject(qString, new VersionRowMapper(withData), poguesId);
+ try {
+ return jdbcTemplate.queryForObject(qString, new VersionRowMapper(withData), poguesId);
+ } catch (EmptyResultDataAccessException e) {
+ throw new PoguesException(404, "Not found", "No version for poguesId "+ poguesId);
+ }
}
@Override
@@ -54,7 +68,11 @@ public Version getVersionByVersionId(UUID versionId, boolean withData) throws Ex
String qString =
"SELECT " + columns +
" FROM pogues_version pv WHERE pv.id = ?;";
- return jdbcTemplate.queryForObject(qString, new VersionRowMapper(withData), versionId);
+ try {
+ return jdbcTemplate.queryForObject(qString, new VersionRowMapper(withData), versionId);
+ } catch (EmptyResultDataAccessException e) {
+ throw new PoguesException(404, "Not found", "No version with id "+ versionId);
+ }
}
@Override
@@ -84,7 +102,7 @@ SELECT day, pogues_id, MAX(timestamp)
jsonData.setValue(version.getData().toString());
jdbcTemplate.update(qString,
// insert request
- version.getId(), jsonData, version.getTimestamp(), version.getDay(), version.getPoguesId(), version.getAuthor(),
+ version.getId(), jsonData, convertZonedDateTimeToTimestamp(version.getTimestamp()), version.getDay(), version.getPoguesId(), version.getAuthor(),
// Delete request: we keep last ${maxCurrentVersions} for the current day
version.getPoguesId(), version.getDay(), maxCurrentVersions,
// Delete request: we keep only the last version for each edited day
@@ -95,6 +113,7 @@ SELECT day, pogues_id, MAX(timestamp)
@Override
public void deleteVersionsByQuestionnaireId(String poguesId) throws Exception {
String qString = "DELETE from pogues_version pv WHERE pv.pogues_id = ?;";
- jdbcTemplate.update(qString, poguesId);
+ int nbVersionsDeleted = jdbcTemplate.update(qString, poguesId);
+ if(nbVersionsDeleted == 0) throw new PoguesException(404, "Not found", "No version to delete for poguesId "+ poguesId);
}
}
diff --git a/src/main/java/fr/insee/pogues/persistence/impl/VersionRowMapper.java b/src/main/java/fr/insee/pogues/persistence/impl/VersionRowMapper.java
index c12dd5ab..c6a3395e 100644
--- a/src/main/java/fr/insee/pogues/persistence/impl/VersionRowMapper.java
+++ b/src/main/java/fr/insee/pogues/persistence/impl/VersionRowMapper.java
@@ -10,6 +10,8 @@
import java.sql.SQLException;
import java.util.UUID;
+import static fr.insee.pogues.utils.DateUtils.convertTimestampToZonedDateTime;
+
@Slf4j
public class VersionRowMapper implements RowMapper {
private boolean withData;
@@ -24,7 +26,7 @@ public Version mapRow(ResultSet rs, int rowNum) throws SQLException {
version.setId(UUID.fromString(rs.getString("id")));
version.setPoguesId(rs.getString("pogues_id"));
version.setDay(rs.getDate("day"));
- version.setTimestamp(rs.getTimestamp("timestamp"));
+ version.setTimestamp(convertTimestampToZonedDateTime(rs.getTimestamp("timestamp")));
version.setAuthor(rs.getString("author"));
if(withData){
try {
diff --git a/src/main/java/fr/insee/pogues/persistence/service/QuestionnairesServiceImpl.java b/src/main/java/fr/insee/pogues/persistence/service/QuestionnairesServiceImpl.java
index d57f9263..55bcd31e 100644
--- a/src/main/java/fr/insee/pogues/persistence/service/QuestionnairesServiceImpl.java
+++ b/src/main/java/fr/insee/pogues/persistence/service/QuestionnairesServiceImpl.java
@@ -146,7 +146,7 @@ public void updateJsonLunatic(String id, JsonNode dataLunatic) throws Exception
public Questionnaire deReference(JsonNode jsonQuestionnaire) throws Exception {
Questionnaire questionnaire = PoguesDeserializer.questionnaireToJavaObject(jsonQuestionnaire);
- List references = JSONFunctions.getChildReferencesFromQuestionnaire(jsonQuestionnaire);
+ List references = questionnaire.getChildQuestionnaireRef();
deReference(references, questionnaire);
return questionnaire;
}
diff --git a/src/main/java/fr/insee/pogues/persistence/service/VersionServiceImpl.java b/src/main/java/fr/insee/pogues/persistence/service/VersionServiceImpl.java
index 1d6db0c9..bd695b4a 100644
--- a/src/main/java/fr/insee/pogues/persistence/service/VersionServiceImpl.java
+++ b/src/main/java/fr/insee/pogues/persistence/service/VersionServiceImpl.java
@@ -2,17 +2,23 @@
import com.fasterxml.jackson.databind.JsonNode;
import fr.insee.pogues.domain.entity.db.Version;
+import fr.insee.pogues.model.Questionnaire;
import fr.insee.pogues.persistence.repository.QuestionnaireRepository;
import fr.insee.pogues.persistence.repository.QuestionnaireVersionRepository;
+import fr.insee.pogues.utils.DateUtils;
+import fr.insee.pogues.utils.PoguesDeserializer;
+import fr.insee.pogues.utils.PoguesSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.Date;
-import java.sql.Timestamp;
import java.time.Instant;
+import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
+import static fr.insee.pogues.utils.json.JSONFunctions.jsonStringtoJsonNode;
+
@Service
public class VersionServiceImpl implements VersionService {
@@ -50,7 +56,7 @@ public void createVersionOfQuestionnaire(String poguesId, JsonNode data, String
Version versionToStore = new Version(
UUID.randomUUID(),
poguesId,
- Timestamp.from(now),
+ ZonedDateTime.now(),
new Date(now.toEpochMilli()),
data,
author);
@@ -66,9 +72,13 @@ public void deleteVersionsByQuestionnaireId(String poguesId) throws Exception {
public void restoreVersion(UUID versionId) throws Exception {
// (1) Retrieve desired version
Version version = questionnaireVersionRepository.getVersionByVersionId(versionId, true);
- // (2) Update questionnaire in pogues table
- questionnaireRepository.updateQuestionnaire(version.getPoguesId(), version.getData());
- // (3) Create new version
- this.createVersionOfQuestionnaire(version.getPoguesId(), version.getData(), version.getAuthor());
+ // (2) Update lastUpdatedDate in Pogues-Model
+ Questionnaire questionnaire = PoguesDeserializer.questionnaireToJavaObject(version.getData());
+ questionnaire.setLastUpdatedDate(DateUtils.getIsoDateFromInstant(Instant.now()));
+ JsonNode newQuestionnaire = jsonStringtoJsonNode(PoguesSerializer.questionnaireJavaToString(questionnaire));
+ // (3) Update questionnaire in pogues table
+ questionnaireRepository.updateQuestionnaire(version.getPoguesId(), newQuestionnaire);
+ // (4) Create new version
+ this.createVersionOfQuestionnaire(version.getPoguesId(), newQuestionnaire, version.getAuthor());
}
}
diff --git a/src/main/java/fr/insee/pogues/utils/DateUtils.java b/src/main/java/fr/insee/pogues/utils/DateUtils.java
new file mode 100644
index 00000000..b3abe02c
--- /dev/null
+++ b/src/main/java/fr/insee/pogues/utils/DateUtils.java
@@ -0,0 +1,34 @@
+package fr.insee.pogues.utils;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class DateUtils {
+
+ public static ZoneId zoneId = ZoneId.systemDefault();
+ public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+ /**
+ * This function is used to get the date in ISO 8601 format Date
+ * @param instant (can be null)
+ * @return if date (Instant) is provided, it returns formated Date, if not returns formated of now.
+ */
+ public static String getIsoDateFromInstant(Instant instant){
+ if(instant != null) {
+ return instant.atZone(zoneId).format(formatter);
+ }
+ ZonedDateTime zonedDateTimeNow = ZonedDateTime.now(zoneId);
+ return zonedDateTimeNow.format(formatter);
+ }
+
+ public static Timestamp convertZonedDateTimeToTimestamp(ZonedDateTime zonedDateTime){
+ return Timestamp.from(zonedDateTime.toInstant());
+ }
+
+ public static ZonedDateTime convertTimestampToZonedDateTime(Timestamp timestamp){
+ return timestamp.toInstant().atZone(ZoneId.systemDefault());
+ }
+}
diff --git a/src/main/java/fr/insee/pogues/utils/json/JSONFunctions.java b/src/main/java/fr/insee/pogues/utils/json/JSONFunctions.java
index fb9682ad..22d01c31 100644
--- a/src/main/java/fr/insee/pogues/utils/json/JSONFunctions.java
+++ b/src/main/java/fr/insee/pogues/utils/json/JSONFunctions.java
@@ -47,16 +47,4 @@ private static JsonNode renameKey(JsonNode input, String key, String replacement
inputNode.remove(key);
return inputNode;
}
-
- public static List getChildReferencesFromQuestionnaire(JsonNode questionnaire) {
- ArrayNode references = (ArrayNode) questionnaire.get("childQuestionnaireRef");
- return IntStream.range(0, references.size())
- .mapToObj(references::get)
- .map(JsonNode::asText)
- .collect(Collectors.toList());
- }
-
-
-
-
}
diff --git a/src/main/java/fr/insee/pogues/webservice/rest/VersionController.java b/src/main/java/fr/insee/pogues/webservice/rest/VersionController.java
index 12078827..0a4013cb 100644
--- a/src/main/java/fr/insee/pogues/webservice/rest/VersionController.java
+++ b/src/main/java/fr/insee/pogues/webservice/rest/VersionController.java
@@ -55,7 +55,7 @@ public Version getLastVersionByQuestionnaireId(
return versionService.getLastVersionByQuestionnaireId(poguesId, withData);
}
- @PostMapping("questionnaire/restore")
+ @PostMapping("questionnaire/restore/{versionId}")
@Operation(
operationId = "restoreVersionByVersion",
summary = "Restore an old version according to its id",
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 5b87152c..01b0bf8c 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -8,6 +8,8 @@ logging:
application:
host: localhost:${server.port}
+ # https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html
+ timezoneId: "Europe/Paris"
name: ''
scheme: http
public-urls:
diff --git a/src/test/java/fr/insee/pogues/utils/DateUtilsTest.java b/src/test/java/fr/insee/pogues/utils/DateUtilsTest.java
new file mode 100644
index 00000000..1ecef4df
--- /dev/null
+++ b/src/test/java/fr/insee/pogues/utils/DateUtilsTest.java
@@ -0,0 +1,54 @@
+package fr.insee.pogues.utils;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.sql.Timestamp;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.TimeZone;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DateUtilsTest {
+
+ @BeforeEach
+ void setup(){
+ TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Europe/Paris")));
+ }
+
+ @Test
+ void testConversionOfChristmasDate(){
+ ZonedDateTime zonedDateTime = ZonedDateTime.of(
+ 2024,12,25,
+ 20,45,50,0,
+ ZoneId.systemDefault());
+ Instant instant = zonedDateTime.toInstant();
+ assertEquals("2024-12-25T20:45:50.000+0100", DateUtils.getIsoDateFromInstant(instant));
+ }
+
+ @Test
+ void timestampToZonedDateTime(){
+ String dateString = "2024-12-25 20:45:50";
+ Timestamp timestamp = Timestamp.valueOf(dateString);
+ ZonedDateTime zonedDateTimeConverted = DateUtils.convertTimestampToZonedDateTime(timestamp);
+ assertEquals(2024,zonedDateTimeConverted.getYear());
+ assertEquals(12,zonedDateTimeConverted.getMonthValue());
+ assertEquals(25,zonedDateTimeConverted.getDayOfMonth());
+ assertEquals(20, zonedDateTimeConverted.getHour());
+ assertEquals(45, zonedDateTimeConverted.getMinute());
+ assertEquals(50, zonedDateTimeConverted.getSecond());
+ }
+
+ @Test
+ void zonedDateTimeToTimestamp(){
+ ZonedDateTime zonedDateTime = ZonedDateTime.of(
+ 2024,12,25,
+ 20,45,50,0,
+ ZoneId.systemDefault());
+ long longZone = zonedDateTime.toInstant().toEpochMilli();
+ Timestamp timestamp = DateUtils.convertZonedDateTimeToTimestamp(zonedDateTime);
+ long longTimestamp = timestamp.getTime();
+ assertEquals(longZone, longTimestamp);
+ }
+}
diff --git a/src/test/resources/transforms/PoguesJSONToPoguesXML/out.xml b/src/test/resources/transforms/PoguesJSONToPoguesXML/out.xml
index c5f88265..9a81df26 100644
--- a/src/test/resources/transforms/PoguesJSONToPoguesXML/out.xml
+++ b/src/test/resources/transforms/PoguesJSONToPoguesXML/out.xml
@@ -1,5 +1,5 @@
-
+
SIMPL
CAPI