Skip to content

Commit

Permalink
feat: restore version (#331)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
laurentC35 authored Dec 11, 2024
1 parent a72d7d1 commit 759e081
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 31 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
<groupId>fr.insee</groupId>
<artifactId>Pogues-BO</artifactId>
<packaging>jar</packaging>
<version>4.9.4</version>
<version>4.9.5</version>
<name>Pogues-Back-Office</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>21</java.version>
<final.asset.name>pogues-bo</final.asset.name>
<pogues-model.version>1.4.0</pogues-model.version>
<pogues-model.version>1.4.2</pogues-model.version>
<fop.version>2.10</fop.version>
<springdoc-openapi-ui.version>2.7.0</springdoc-openapi-ui.version>
<jacoco.version>0.8.12</jacoco.version>
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/fr/insee/pogues/Pogues.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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());
}
Expand All @@ -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. ===============");
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/fr/insee/pogues/domain/entity/db/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import lombok.Setter;

import java.sql.Date;
import java.sql.Timestamp;
import java.time.ZonedDateTime;
import java.util.UUID;


Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -36,7 +41,12 @@ public List<Version> 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<Version> 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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Version> {
private boolean withData;
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> references = JSONFunctions.getChildReferencesFromQuestionnaire(jsonQuestionnaire);
List<String> references = questionnaire.getChildQuestionnaireRef();
deReference(references, questionnaire);
return questionnaire;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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);
Expand All @@ -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());
}
}
34 changes: 34 additions & 0 deletions src/main/java/fr/insee/pogues/utils/DateUtils.java
Original file line number Diff line number Diff line change
@@ -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());
}
}
12 changes: 0 additions & 12 deletions src/main/java/fr/insee/pogues/utils/json/JSONFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,4 @@ private static JsonNode renameKey(JsonNode input, String key, String replacement
inputNode.remove(key);
return inputNode;
}

public static List<String> getChildReferencesFromQuestionnaire(JsonNode questionnaire) {
ArrayNode references = (ArrayNode) questionnaire.get("childQuestionnaireRef");
return IntStream.range(0, references.size())
.mapToObj(references::get)
.map(JsonNode::asText)
.collect(Collectors.toList());
}




}
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
54 changes: 54 additions & 0 deletions src/test/java/fr/insee/pogues/utils/DateUtilsTest.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Questionnaire xmlns="http://xml.insee.fr/schema/applis/pogues" id="l13ob2fa" genericName="QUESTIONNAIRE" agency="fr.insee" final="false" flowLogic="REDIRECTION" formulasLanguage="VTL">
<Questionnaire xmlns="http://xml.insee.fr/schema/applis/pogues" id="l13ob2fa" genericName="QUESTIONNAIRE" agency="fr.insee" final="false" flowLogic="REDIRECTION" formulasLanguage="VTL" lastUpdatedDate="Wed Mar 23 2022 16:19:53 GMT+0100 (heure normale d’Europe centrale)" owner="DR59-SNDI">
<Name>SIMPL</Name>
<Label>Questionnaire simple</Label>
<TargetMode>CAPI</TargetMode>
Expand Down

0 comments on commit 759e081

Please sign in to comment.