From dc359cf39766b532de60faeed7a8449417da9b57 Mon Sep 17 00:00:00 2001 From: leslieduan Date: Wed, 4 Feb 2026 11:14:36 +1100 Subject: [PATCH 1/3] feature: add new endpoint to get latest wave buoy date --- .../core/model/enumeration/FeatureId.java | 1 + .../ogcapi/server/core/service/DasService.java | 13 +++++++++++++ .../aodn/ogcapi/server/features/RestApi.java | 3 +++ .../ogcapi/server/features/RestServices.java | 17 +++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/FeatureId.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/FeatureId.java index 75ed6fa2..99d2e267 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/FeatureId.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/FeatureId.java @@ -3,6 +3,7 @@ public enum FeatureId { summary("summary"), first_data_available("first_data_available"), + latest_date("latest_date"), timeseries("timeseries"), wfs_downloadable_fields("wfs_downloadable_fields"), // Query field based on pure wfs and given layer wms_downloadable_fields("wms_downloadable_fields"), // Query field based on value from wms describe layer query diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/DasService.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/DasService.java index 0645e83c..282780ab 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/DasService.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/DasService.java @@ -43,6 +43,19 @@ public byte[] getWaveBuoys(String from, String to){ return httpClient.exchange(waveBuoysUrlTemplate, HttpMethod.GET,entity,byte[].class,params).getBody(); } + public byte[] getWaveBuoysLatestDate(){ + HttpHeaders headers = new HttpHeaders(); + headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); + headers.set("X-API-KEY", dasConfig.secret); + HttpEntity entity = new HttpEntity<>(headers); + + String waveBuoysUrlTemplate = UriComponentsBuilder.fromUriString(dasConfig.host + "/api/v1/das/data/feature-collection/wave-buoy/latest") + .encode() + .toUriString(); + + return httpClient.exchange(waveBuoysUrlTemplate, HttpMethod.GET,entity,byte[].class).getBody(); + } + public byte[] getWaveBuoyData(String from, String to, String buoy){ String encodedBuoy = URLEncoder.encode(buoy, java.nio.charset.StandardCharsets.UTF_8); diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/features/RestApi.java b/server/src/main/java/au/org/aodn/ogcapi/server/features/RestApi.java index d2c106c8..7c768559 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/features/RestApi.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/features/RestApi.java @@ -118,6 +118,9 @@ public ResponseEntity getFeature( case first_data_available -> { return featuresService.getWaveBuoys(collectionId, request.getDatetime()); } + case latest_date -> { + return featuresService.getWaveBuoysLatestDate(collectionId); + } case timeseries -> { return featuresService.getWaveBuoyData(collectionId, request.getDatetime(), request.getWaveBuoy()); } diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/features/RestServices.java b/server/src/main/java/au/org/aodn/ogcapi/server/features/RestServices.java index 422b8b1d..e577067d 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/features/RestServices.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/features/RestServices.java @@ -194,6 +194,23 @@ public ResponseEntity getWaveBuoys(String collectionID, String from) { } } + public ResponseEntity getWaveBuoysLatestDate(String collectionID) { + if (!dasService.isCollectionSupported(collectionID)) { + return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).build(); + } + + try { + return ResponseEntity + .ok() + .header("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .body(dasService.getWaveBuoysLatestDate()); + + } catch (Exception e) { + log.error("Error fetching wave buoys latest date: {}", e.getMessage()); + return ResponseEntity.internalServerError().build(); + } + } + public ResponseEntity getWaveBuoyData(String collectionID, String datetime, String buoy) { if (!dasService.isCollectionSupported(collectionID)) { return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED).build(); From 1f16410b053d3c4de11110cee8fbfaf44e37f175 Mon Sep 17 00:00:00 2001 From: leslieduan Date: Wed, 4 Feb 2026 12:01:53 +1100 Subject: [PATCH 2/3] refactor: rename wave buoys endpoint and minor refactors --- .../core/model/enumeration/FeatureId.java | 6 ++-- .../server/core/service/DasService.java | 29 ++++++++----------- .../aodn/ogcapi/server/features/RestApi.java | 6 ++-- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/FeatureId.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/FeatureId.java index 99d2e267..650c59b6 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/FeatureId.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/FeatureId.java @@ -2,9 +2,9 @@ public enum FeatureId { summary("summary"), - first_data_available("first_data_available"), - latest_date("latest_date"), - timeseries("timeseries"), + wave_buoy_first_data_available("wave_buoy_first_data_available"), + wave_buoy_latest_date("wave_buoy_latest_date"), + wave_buoy_timeseries("wave_buoy_timeseries"), wfs_downloadable_fields("wfs_downloadable_fields"), // Query field based on pure wfs and given layer wms_downloadable_fields("wms_downloadable_fields"), // Query field based on value from wms describe layer query wms_map_tile("wms_map_tile"), diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/DasService.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/DasService.java index 282780ab..314587cf 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/service/DasService.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/service/DasService.java @@ -1,7 +1,5 @@ package au.org.aodn.ogcapi.server.core.service; -import au.org.aodn.ogcapi.features.model.FeatureCollectionGeoJSON; -import au.org.aodn.ogcapi.features.model.FeatureGeoJSON; import au.org.aodn.ogcapi.server.core.configuration.DASConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; @@ -12,6 +10,8 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import jakarta.annotation.PostConstruct; + import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; @@ -25,12 +25,17 @@ public class DasService { @Autowired protected RestTemplate httpClient; - public byte[] getWaveBuoys(String from, String to){ + private HttpEntity httpEntity; + + @PostConstruct + public void init() { HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); headers.set("X-API-KEY", dasConfig.secret); - HttpEntity entity = new HttpEntity<>(headers); + httpEntity = new HttpEntity<>(headers); + } + public byte[] getWaveBuoys(String from, String to){ String waveBuoysUrlTemplate = UriComponentsBuilder.fromUriString(dasConfig.host + "/api/v1/das/data/feature-collection/wave-buoy") .queryParam("start_date","{start_date}") .queryParam("end_date","{end_date}") @@ -40,30 +45,20 @@ public byte[] getWaveBuoys(String from, String to){ params.put("start_date", from); params.put("end_date",to); - return httpClient.exchange(waveBuoysUrlTemplate, HttpMethod.GET,entity,byte[].class,params).getBody(); + return httpClient.exchange(waveBuoysUrlTemplate, HttpMethod.GET,httpEntity,byte[].class,params).getBody(); } public byte[] getWaveBuoysLatestDate(){ - HttpHeaders headers = new HttpHeaders(); - headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); - headers.set("X-API-KEY", dasConfig.secret); - HttpEntity entity = new HttpEntity<>(headers); - String waveBuoysUrlTemplate = UriComponentsBuilder.fromUriString(dasConfig.host + "/api/v1/das/data/feature-collection/wave-buoy/latest") .encode() .toUriString(); - return httpClient.exchange(waveBuoysUrlTemplate, HttpMethod.GET,entity,byte[].class).getBody(); + return httpClient.exchange(waveBuoysUrlTemplate, HttpMethod.GET,httpEntity,byte[].class).getBody(); } public byte[] getWaveBuoyData(String from, String to, String buoy){ String encodedBuoy = URLEncoder.encode(buoy, java.nio.charset.StandardCharsets.UTF_8); - HttpHeaders headers = new HttpHeaders(); - headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); - headers.set("X-API-KEY", dasConfig.secret); - HttpEntity entity = new HttpEntity<>(headers); - String waveBuoyDataUrlTemplate = UriComponentsBuilder.fromUriString(dasConfig.host + "/api/v1/das/data/feature-collection/wave-buoy/" + encodedBuoy) .queryParam("start_date","{start_date}") .queryParam("end_date","{end_date}") @@ -73,7 +68,7 @@ public byte[] getWaveBuoyData(String from, String to, String buoy){ params.put("start_date", from); params.put("end_date",to); - return httpClient.exchange(waveBuoyDataUrlTemplate, HttpMethod.GET,entity,byte[].class,params).getBody(); + return httpClient.exchange(waveBuoyDataUrlTemplate, HttpMethod.GET,httpEntity,byte[].class,params).getBody(); } public boolean isCollectionSupported(String collectionId){ diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/features/RestApi.java b/server/src/main/java/au/org/aodn/ogcapi/server/features/RestApi.java index 7c768559..9137160b 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/features/RestApi.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/features/RestApi.java @@ -115,13 +115,13 @@ public ResponseEntity getFeature( return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } - case first_data_available -> { + case wave_buoy_first_data_available -> { return featuresService.getWaveBuoys(collectionId, request.getDatetime()); } - case latest_date -> { + case wave_buoy_latest_date -> { return featuresService.getWaveBuoysLatestDate(collectionId); } - case timeseries -> { + case wave_buoy_timeseries -> { return featuresService.getWaveBuoyData(collectionId, request.getDatetime(), request.getWaveBuoy()); } case wfs_downloadable_fields -> { From 636231d2f7836fc147a15f403cbce548099a3869 Mon Sep 17 00:00:00 2001 From: leslieduan Date: Wed, 4 Feb 2026 15:26:17 +1100 Subject: [PATCH 3/3] feature: add testing for wave buoys --- mvnw | 0 .../server/features/RestServicesTest.java | 68 +++++++++++++++++++ 2 files changed, 68 insertions(+) mode change 100644 => 100755 mvnw create mode 100644 server/src/test/java/au/org/aodn/ogcapi/server/features/RestServicesTest.java diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/server/src/test/java/au/org/aodn/ogcapi/server/features/RestServicesTest.java b/server/src/test/java/au/org/aodn/ogcapi/server/features/RestServicesTest.java new file mode 100644 index 00000000..63500c64 --- /dev/null +++ b/server/src/test/java/au/org/aodn/ogcapi/server/features/RestServicesTest.java @@ -0,0 +1,68 @@ +package au.org.aodn.ogcapi.server.features; + +import au.org.aodn.ogcapi.server.core.service.DasService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +public class RestServicesTest { + + @Mock + private DasService dasService; + + @InjectMocks + private RestServices restServices; + + private AutoCloseable closeableMock; + + private static final String SUPPORTED_COLLECTION_ID = "b299cdcd-3dee-48aa-abdd-e0fcdbb9cadc"; + + @BeforeEach + public void setUp() { + closeableMock = MockitoAnnotations.openMocks(this); + } + + @AfterEach + void cleanUp() throws Exception { + closeableMock.close(); + } + + @Test + public void testGetWaveBuoysLatestDateSuccess() { + byte[] mockResponse = "{\"latest_date\":\"2024-01-01\"}".getBytes(); + when(dasService.isCollectionSupported(SUPPORTED_COLLECTION_ID)).thenReturn(true); + when(dasService.getWaveBuoysLatestDate()).thenReturn(mockResponse); + + ResponseEntity response = restServices.getWaveBuoysLatestDate(SUPPORTED_COLLECTION_ID); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(mockResponse, response.getBody()); + } + + @Test + public void testGetWaveBuoysLatestDateUnsupportedCollection() { + when(dasService.isCollectionSupported("unsupported-id")).thenReturn(false); + + ResponseEntity response = restServices.getWaveBuoysLatestDate("unsupported-id"); + + assertEquals(HttpStatus.NOT_IMPLEMENTED, response.getStatusCode()); + } + + @Test + public void testGetWaveBuoysLatestDateServiceError() { + when(dasService.isCollectionSupported(SUPPORTED_COLLECTION_ID)).thenReturn(true); + when(dasService.getWaveBuoysLatestDate()).thenThrow(new RuntimeException("Connection refused")); + + ResponseEntity response = restServices.getWaveBuoysLatestDate(SUPPORTED_COLLECTION_ID); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + } +}