diff --git a/flyway.conf b/flyway.conf
new file mode 100644
index 0000000..be3336f
--- /dev/null
+++ b/flyway.conf
@@ -0,0 +1,4 @@
+flyway.url=jdbc:postgresql://185.166.39.209:5432/vlib_db
+flyway.user=vlibadmin
+flyway.password=T5tUb8DTh2dtMaRymzMyRKwT5kg3xqamdZJjqkLr
+flyway.locations=filesystem:src/main/resources/db/migration
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 70f2782..cf02062 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,10 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
org.postgresql
postgresql
diff --git a/src/main/java/fr/host_dcode/vlib/config/WebClientConfig.java b/src/main/java/fr/host_dcode/vlib/config/WebClientConfig.java
new file mode 100644
index 0000000..77dd77a
--- /dev/null
+++ b/src/main/java/fr/host_dcode/vlib/config/WebClientConfig.java
@@ -0,0 +1,19 @@
+package fr.host_dcode.vlib.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+public class WebClientConfig {
+ @Value("${velib.api.base-url}")
+ private String baseUrl;
+
+ @Bean
+ public WebClient webClient() {
+ return WebClient.builder()
+ .baseUrl(java.util.Objects.requireNonNull(baseUrl, "velib.api.base-url must not be null"))
+ .build();
+ }
+}
diff --git a/src/main/java/fr/host_dcode/vlib/model/Station.java b/src/main/java/fr/host_dcode/vlib/model/Station.java
index 1859b59..8d72a86 100644
--- a/src/main/java/fr/host_dcode/vlib/model/Station.java
+++ b/src/main/java/fr/host_dcode/vlib/model/Station.java
@@ -1,10 +1,7 @@
package fr.host_dcode.vlib.model;
-
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
+import jakarta.persistence.*;
+import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@@ -13,65 +10,71 @@ public class Station {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
+ @Column(name = "recordid")
private String recordId;
private String name;
- private String station_code;
+ @Column(name = "station_code")
+ private String stationCode;
private double latitude;
private double longitude;
- private String address;
private String city;
private String description;
- private LocalDateTime last_update;
-
+ @UpdateTimestamp
+ @Column(name = "last_update")
+ private LocalDateTime lastUpdate;
- public Station(){}
+ public Station() {
+ }
- public Station(String name, String recordId, String stationCode, double latitude, double longitude, String description) {
+ public Station(String name, String recordId, String stationCode, String city, double latitude, double longitude,
+ String description) {
this.name = name;
this.recordId = recordId;
- this.station_code = stationCode;
+ this.stationCode = stationCode;
+ this.city = city;
this.latitude = latitude;
this.longitude = longitude;
this.description = description;
}
- public String getStationCode(){
- return this.station_code;
+ public String getStationCode() {
+ return this.stationCode;
}
- public String getName(){
+ public String getName() {
return this.name;
}
- public void setName(String name){
+ public void setName(String name) {
this.name = name;
}
- public String getRecordId(){
+ public String getRecordId() {
return this.recordId;
}
- public String getDescription(){
+ public String getDescription() {
return this.description;
}
- public void setDescription(String description){
+ public void setDescription(String description) {
this.description = description;
}
- public Double getLatitude(){
+
+ public Double getLatitude() {
return this.latitude;
}
- public Double getLongitude(){
+
+ public Double getLongitude() {
return this.longitude;
}
- public String getAddress(){
- return this.address;
+ public String getCity() {
+ return this.city;
}
- public void setAddress(String address){
- this.address = address;
+ public void setCity(String city) {
+ this.city = city;
}
-
}
diff --git a/src/main/java/fr/host_dcode/vlib/model/User.java b/src/main/java/fr/host_dcode/vlib/model/User.java
index f097a9d..cd015ad 100644
--- a/src/main/java/fr/host_dcode/vlib/model/User.java
+++ b/src/main/java/fr/host_dcode/vlib/model/User.java
@@ -20,7 +20,7 @@ public enum Status {
private String password;
@Enumerated(EnumType.STRING)
private Status status;
- private LocalDateTime created_at;
+ private LocalDateTime createdAt;
public User(){}
diff --git a/src/main/java/fr/host_dcode/vlib/model/VelibApiResponse.java b/src/main/java/fr/host_dcode/vlib/model/VelibApiResponse.java
new file mode 100644
index 0000000..e9f61c8
--- /dev/null
+++ b/src/main/java/fr/host_dcode/vlib/model/VelibApiResponse.java
@@ -0,0 +1,17 @@
+package fr.host_dcode.vlib.model;
+
+import java.util.List;
+
+public class VelibApiResponse {
+
+ private List records;
+
+ public List getRecords() {
+ return records;
+ }
+
+ public void setRecords(List records) {
+ this.records = records;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/fr/host_dcode/vlib/model/VelibFields.java b/src/main/java/fr/host_dcode/vlib/model/VelibFields.java
new file mode 100644
index 0000000..b83510e
--- /dev/null
+++ b/src/main/java/fr/host_dcode/vlib/model/VelibFields.java
@@ -0,0 +1,42 @@
+package fr.host_dcode.vlib.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.ZonedDateTime;
+import java.util.List;
+
+
+public class VelibFields {
+
+ @JsonProperty("recordid")
+ private String recordId;
+ private String name;
+ @JsonProperty("stationcode")
+ private String stationCode;
+ @JsonProperty("nom_arrondissement_communes")
+ private String city;
+ private List coordonnees_geo;
+ private ZonedDateTime duedate;
+
+
+ public String getName(){
+ return this.name;
+ }
+ public String getStationCode(){
+ return this.stationCode;
+ }
+
+ public String getDuedate(){
+ return this.duedate != null ? this.duedate.toString() : null;
+ }
+
+ public List getCoordonnees_geo(){
+ return this.coordonnees_geo;
+ }
+
+ public String getCity(){
+ return this.city;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/fr/host_dcode/vlib/model/VelibRecord.java b/src/main/java/fr/host_dcode/vlib/model/VelibRecord.java
new file mode 100644
index 0000000..d98d6ae
--- /dev/null
+++ b/src/main/java/fr/host_dcode/vlib/model/VelibRecord.java
@@ -0,0 +1,27 @@
+package fr.host_dcode.vlib.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class VelibRecord {
+
+ private VelibFields fields;
+ @JsonProperty("recordid")
+ private String recordId;
+
+ public VelibFields getFields() {
+ return fields;
+ }
+
+ public void setFields(VelibFields fields) {
+ this.fields = fields;
+ }
+
+ public String getRecordId(){
+ return this.recordId;
+ }
+
+ public void setRecordId(String recordId) {
+ this.recordId = recordId;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/fr/host_dcode/vlib/repository/StationRepository.java b/src/main/java/fr/host_dcode/vlib/repository/StationRepository.java
new file mode 100644
index 0000000..7c334f9
--- /dev/null
+++ b/src/main/java/fr/host_dcode/vlib/repository/StationRepository.java
@@ -0,0 +1,12 @@
+package fr.host_dcode.vlib.repository;
+
+import fr.host_dcode.vlib.model.Station;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface StationRepository extends JpaRepository {
+
+ Station findByStationCode(String station_code);
+
+}
diff --git a/src/main/java/fr/host_dcode/vlib/service/VelibService.java b/src/main/java/fr/host_dcode/vlib/service/VelibService.java
new file mode 100644
index 0000000..514d604
--- /dev/null
+++ b/src/main/java/fr/host_dcode/vlib/service/VelibService.java
@@ -0,0 +1,85 @@
+package fr.host_dcode.vlib.service;
+
+import fr.host_dcode.vlib.model.Station;
+import fr.host_dcode.vlib.model.VelibApiResponse;
+import fr.host_dcode.vlib.model.VelibFields;
+import fr.host_dcode.vlib.repository.StationRepository;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.util.List;
+
+@Service
+public class VelibService {
+ private final WebClient webClient;
+ private final StationRepository stationRepository;
+
+ public VelibService(WebClient webClient, StationRepository stationRepository) {
+ this.webClient = webClient;
+ this.stationRepository = stationRepository;
+ }
+
+ public void fetchAndSaveVelibData() {
+
+ String queryUri = "?dataset=velib-disponibilite-en-temps-reel&rows=50";
+
+ VelibApiResponse apiResponse = webClient.get()
+ .uri(queryUri)
+ .retrieve()
+ .bodyToMono(VelibApiResponse.class)
+ .block(java.time.Duration.ofSeconds(30));
+
+ if (apiResponse == null || apiResponse.getRecords() == null || apiResponse.getRecords().isEmpty()) {
+ System.out.println("Aucune donnée Velib reçue.");
+ return;
+ }
+
+ apiResponse.getRecords().stream()
+ .map(record -> {
+ VelibFields fields = record.getFields();
+
+ List coords = fields.getCoordonnees_geo();
+ double latitude = (coords != null && coords.size() >= 1) ? coords.get(0) : 0.0;
+ double longitude = (coords != null && coords.size() >= 2) ? coords.get(1) : 0.0;
+
+ String description = fields.getName() + ". Dernière mise à jour: " + (fields.getDuedate() != null ? fields.getDuedate() : "inconnue");
+
+ Station station = new Station(
+ fields.getName(),
+ record.getRecordId(),
+ fields.getStationCode(),
+ fields.getCity(),
+ latitude,
+ longitude,
+ description
+ );
+
+ return station;
+ })
+ .forEach(station ->{
+ Station existingStation = stationRepository.findByStationCode(station.getStationCode());
+ if(existingStation == null){
+ stationRepository.save(station);
+ } else {
+ existingStation.setName(station.getName());
+ //ADD ALL UPDATES
+ stationRepository.save(existingStation);
+ }
+ });
+ }
+
+
+ @EventListener(ApplicationReadyEvent.class)
+ public void runAfterStartup() {
+ System.out.println("🚀 Démarrage de l'application détecté. Lancement de la récupération des données Velib...");
+
+ try {
+ fetchAndSaveVelibData();
+ System.out.println("✅ Récupération et sauvegarde initiales terminées.");
+ } catch (Exception e) {
+ System.err.println("❌ Erreur critique lors de la récupération initiale des données Velib : " + e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V2__Alter_table_station_to_add_recordId_column.sql b/src/main/resources/db/migration/V2__Alter_table_station_to_add_recordId_column.sql
new file mode 100644
index 0000000..52033f8
--- /dev/null
+++ b/src/main/resources/db/migration/V2__Alter_table_station_to_add_recordId_column.sql
@@ -0,0 +1,2 @@
+ALTER TABLE station
+ ADD COLUMN recordId VARCHAR(100) NOT NULL;
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V3__Alter_tables_users_and_station_to_change_id_type.sql b/src/main/resources/db/migration/V3__Alter_tables_users_and_station_to_change_id_type.sql
new file mode 100644
index 0000000..7044121
--- /dev/null
+++ b/src/main/resources/db/migration/V3__Alter_tables_users_and_station_to_change_id_type.sql
@@ -0,0 +1,6 @@
+ALTER TABLE users
+ALTER COLUMN id TYPE VARCHAR(40) USING id::VARCHAR(40);
+
+
+ALTER TABLE station
+ALTER COLUMN id TYPE VARCHAR(40) USING id::VARCHAR(40);
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V4__Delete_address_column_in_table_station.sql b/src/main/resources/db/migration/V4__Delete_address_column_in_table_station.sql
new file mode 100644
index 0000000..1c111e0
--- /dev/null
+++ b/src/main/resources/db/migration/V4__Delete_address_column_in_table_station.sql
@@ -0,0 +1,2 @@
+ALTER TABLE station
+DROP COLUMN address;
\ No newline at end of file