getSeriesInstances(String seriesId) throws OHServiceException,OHRestClientException {
+ return getClient().getSeriesInstances(seriesId);
+ }
+
+ /**
+ * Get an instance using instance's UUID
+ *
+ * @param instanceId Instance UUID
+ * @return the instance that matched the provided UUID
+ * @throws OHServiceException see when {@link #getClient()} throws this exception
+ * @throws OHRestClientException when 4xx or 5xx errors is thrown by Feign
+ */
+ public InstanceResponse getInstanceById(String instanceId) throws OHServiceException,OHRestClientException {
+ return getClient().getInstanceById(instanceId);
+ }
+
+ /**
+ * Get the preview of an instance using instance's UUID
+ *
+ * @param instanceId Instance UUID
+ * @return the instance preview image in PNG format
+ * @throws OHServiceException see when {@link #getClient()} throws this exception
+ * @throws OHRestClientException when 4xx or 5xx errors is thrown by Feign
+ */
+ public byte[] getInstancePreview(String instanceId) throws OHServiceException, OHRestClientException {
+ // TODO: Better handle the file download to avoid {@link OutOfMemoryError} error
+
+ try (Response response = getClient().getInstancePreview(instanceId)) {
+ InputStream inputStream = response.body().asInputStream();
+
+ return inputStream.readAllBytes();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/org/isf/orthanc/utils/CustomErrorDecoder.java b/src/main/java/org/isf/orthanc/utils/CustomErrorDecoder.java
new file mode 100644
index 000000000..0bedeb0f8
--- /dev/null
+++ b/src/main/java/org/isf/orthanc/utils/CustomErrorDecoder.java
@@ -0,0 +1,57 @@
+/*
+ * Open Hospital (www.open-hospital.org)
+ * Copyright © 2006-2024 Informatici Senza Frontiere (info@informaticisenzafrontiere.org)
+ *
+ * Open Hospital is a free and open source software for healthcare data management.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0-standalone.html
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.isf.orthanc.utils;
+
+import org.isf.utils.exception.OHInternalServerException;
+import org.isf.utils.exception.OHNotFoundException;
+import org.isf.utils.exception.model.OHExceptionMessage;
+import org.springframework.http.HttpStatus;
+
+import feign.Response;
+import feign.codec.ErrorDecoder;
+
+/**
+ * Custom Error Decoder for Feign Exceptions
+ *
+ * @author Silevester D.
+ * @since v1.16
+ */
+public class CustomErrorDecoder implements ErrorDecoder {
+
+ private final ErrorDecoder defaultErrorDecoder = new Default();
+
+ @Override
+ public Exception decode(String methodKey, Response response) {
+ HttpStatus status = HttpStatus.valueOf(response.status());
+
+ if (status.is4xxClientError()) {
+ return new OHNotFoundException(new OHExceptionMessage(response.reason()));
+ }
+
+ if (status.is5xxServerError()) {
+ return new OHInternalServerException(new OHExceptionMessage(response.reason()));
+ }
+
+ return defaultErrorDecoder.decode(methodKey, response);
+ }
+}
+
diff --git a/src/main/java/org/isf/utils/exception/OHInternalServerException.java b/src/main/java/org/isf/utils/exception/OHInternalServerException.java
new file mode 100644
index 000000000..e868f493d
--- /dev/null
+++ b/src/main/java/org/isf/utils/exception/OHInternalServerException.java
@@ -0,0 +1,37 @@
+/*
+ * Open Hospital (www.open-hospital.org)
+ * Copyright © 2006-2024 Informatici Senza Frontiere (info@informaticisenzafrontiere.org)
+ *
+ * Open Hospital is a free and open source software for healthcare data management.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0-standalone.html
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.isf.utils.exception;
+
+import org.isf.utils.exception.model.OHExceptionMessage;
+
+/**
+ * Exception to throw when a REST client throws a 500 error
+ *
+ * @author Silevester D.
+ * @since v1.16
+ */
+public class OHInternalServerException extends OHRestClientException {
+
+ public OHInternalServerException(OHExceptionMessage message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/isf/utils/exception/OHNotFoundException.java b/src/main/java/org/isf/utils/exception/OHNotFoundException.java
new file mode 100644
index 000000000..239d51b54
--- /dev/null
+++ b/src/main/java/org/isf/utils/exception/OHNotFoundException.java
@@ -0,0 +1,37 @@
+/*
+ * Open Hospital (www.open-hospital.org)
+ * Copyright © 2006-2023 Informatici Senza Frontiere (info@informaticisenzafrontiere.org)
+ *
+ * Open Hospital is a free and open source software for healthcare data management.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0-standalone.html
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.isf.utils.exception;
+
+import org.isf.utils.exception.model.OHExceptionMessage;
+
+/**
+ * Exception to throw when a REST client throws a 404 error
+ *
+ * @author Silevester D.
+ * @since v1.16
+ */
+public class OHNotFoundException extends OHRestClientException {
+
+ public OHNotFoundException(OHExceptionMessage message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/isf/utils/exception/OHRestClientException.java b/src/main/java/org/isf/utils/exception/OHRestClientException.java
new file mode 100644
index 000000000..58c17a9d2
--- /dev/null
+++ b/src/main/java/org/isf/utils/exception/OHRestClientException.java
@@ -0,0 +1,57 @@
+/*
+ * Open Hospital (www.open-hospital.org)
+ * Copyright © 2006-2024 Informatici Senza Frontiere (info@informaticisenzafrontiere.org)
+ *
+ * Open Hospital is a free and open source software for healthcare data management.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0-standalone.html
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.isf.utils.exception;
+
+import org.isf.utils.exception.model.OHExceptionMessage;
+
+/**
+ * Base Exception for exceptions thrown by REST clients
+ *
+ * This exception class extends {@link RuntimeException} rather than {@link OHServiceException}
+ * because REST clients like Feign proxies their client interfaces. So if custom exceptions
+ * are not runtime exceptions, they cannot handle them correctly.
+ *
+ *
+ * @author Silevester D.
+ * @since v1.16
+ */
+public class OHRestClientException extends RuntimeException {
+ private OHExceptionMessage message;
+
+ public OHRestClientException(OHExceptionMessage message) {
+ super(message.getMessage());
+ this.message = message;
+ }
+
+ public OHRestClientException(Throwable cause, OHExceptionMessage message) {
+ super(message.getMessage(), cause);
+ this.message = message;
+ }
+
+ public void setMessage(OHExceptionMessage message) {
+ this.message = message;
+ }
+
+ public OHExceptionMessage getExceptionMessage() {
+ return message;
+ }
+}
diff --git a/src/test/java/org/isf/orthanc/service/OrthancAPIClientServiceTest.java b/src/test/java/org/isf/orthanc/service/OrthancAPIClientServiceTest.java
new file mode 100644
index 000000000..bb556c114
--- /dev/null
+++ b/src/test/java/org/isf/orthanc/service/OrthancAPIClientServiceTest.java
@@ -0,0 +1,156 @@
+/*
+ * Open Hospital (www.open-hospital.org)
+ * Copyright © 2006-2024 Informatici Senza Frontiere (info@informaticisenzafrontiere.org)
+ *
+ * Open Hospital is a free and open source software for healthcare data management.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * https://www.gnu.org/licenses/gpl-3.0-standalone.html
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.isf.orthanc.service;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import org.isf.OHCoreTestCase;
+import org.isf.orthanc.model.InstanceResponse;
+import org.isf.orthanc.model.SeriesResponse;
+import org.isf.orthanc.model.StudyResponse;
+import org.isf.utils.exception.OHNotFoundException;
+import org.isf.utils.exception.OHServiceException;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Orthanc APIs call testing
+ *
+ * These tests are disabled by default because a valid running instance of
+ * ORTHANC may be missing. So you need to configure one using the SQL script
+ * loaded by this test before tests can run smoothly. The script is located at
+ * src/test/resources/org/isf/orthanc/service/LoadORTHANCSettings.sql
+ *
+ *
+ * @author Silevester D.
+ * @since 1.15
+ */
+@Disabled("Disabled because ORTHANC may not be properly configured")
+public class OrthancAPIClientServiceTest extends OHCoreTestCase {
+
+ @Autowired
+ private OrthancAPIClientService service;
+
+ @Test
+ @DisplayName("Should successfully establish connection to ORTHANC server")
+ void testTestConnection() throws OHServiceException {
+ assertThat(service.testConnection()).isTrue();
+ }
+
+ @Test
+ @DisplayName("Should successfully get all studies")
+ void testGetAllStudies() throws OHServiceException {
+ List studies = service.getAllStudies();
+
+ assertThat(studies).isNotNull();
+ assertThat(studies).isNotEmpty();
+ assertThat(studies.get(0).getStudy()).isNotNull();
+ }
+
+ @Test
+ @DisplayName("Should successfully get patient's studies using ID")
+ void testGetPatientStudiesById() throws OHServiceException {
+ List studies = service.getPatientStudiesById("TCGA-CS-5396");
+
+ assertThat(studies).isNotNull();
+ assertThat(studies).isNotEmpty();
+ assertThat(studies.get(0).getStudy()).isNotNull();
+ }
+
+ @Test
+ @DisplayName("Should successfully get patient's studies using UUID")
+ void testGetPatientStudiesByUuid() throws OHServiceException {
+ List studies = service.getPatientStudiesByUuid("2eb243f6-71d716cd-f195ef0b-09e7c680-9937a931");
+
+ assertThat(studies).isNotNull();
+ assertThat(studies).isNotEmpty();
+ assertThat(studies.get(0).getStudy()).isNotNull();
+ }
+
+ @Test
+ @DisplayName("Should successfully get study using study's ID")
+ void testGetStudyById() throws OHServiceException {
+ StudyResponse studies = service.getStudyById("8fb3d973-4449cad4-c21bb79d-81c41b56-b9412373");
+
+ assertThat(studies).isNotNull();
+ assertThat(studies.getStudy()).isNotNull();
+ assertThat(studies.lastUpdateToLocalDateTime()).isInstanceOf(LocalDateTime.class);
+ }
+
+ @Test
+ @DisplayName("Should throw OHServiceException when trying to get study using a wrong ID")
+ void testGetStudyWithWrongIdThrowsException() {
+ assertThatThrownBy(() -> service.getStudyById("8fb3d973-4449ad4-c21bb79d-81c41b56-b9412373"))
+ .isInstanceOf(OHNotFoundException.class);
+ }
+
+ @Test
+ @DisplayName("Should successfully get study's series using study's ID")
+ void testGetStudySeries() throws OHServiceException {
+ List series = service.getStudySeries("8fb3d973-4449cad4-c21bb79d-81c41b56-b9412373");
+
+ assertThat(series).isNotNull();
+ assertThat(series).isNotEmpty();
+ assertThat(series.get(0).getSeries()).isNotNull();
+ }
+
+ @Test
+ @DisplayName("Should successfully get series using series' ID")
+ void testGetSeriesById() throws OHServiceException {
+ SeriesResponse series = service.getSeriesById("61af62b9-54162615-e473cdc1-e3676d55-0b5d7a4b");
+
+ assertThat(series).isNotNull();
+ assertThat(series.getInstancesIds()).isNotEmpty();
+ }
+
+ @Test
+ @DisplayName("Should successfully get series' instances using series' ID")
+ void testGetSeriesInstances() throws OHServiceException {
+ List instances = service.getSeriesInstances("61af62b9-54162615-e473cdc1-e3676d55-0b5d7a4b");
+
+ assertThat(instances).isNotNull();
+ assertThat(instances.get(0).getInstance()).isNotNull();
+ }
+
+ @Test
+ @DisplayName("Should successfully get instance using instance's ID")
+ void testGetInstanceById() throws OHServiceException {
+ InstanceResponse instance = service.getInstanceById("d4ea97a3-dad85671-26c5ffaf-6b9ed334-6a2fad91");
+
+ assertThat(instance).isNotNull();
+ assertThat(instance.getInstance()).isNotNull();
+ }
+
+ @Test
+ @DisplayName("Should successfully get instance preview")
+ void testGetInstancePreview() throws OHServiceException {
+ byte[] pngImage = service.getInstancePreview("d4ea97a3-dad85671-26c5ffaf-6b9ed334-6a2fad91");
+
+ assertThat(pngImage).isNotNull();
+ }
+}
diff --git a/src/test/resources/orthanc.properties b/src/test/resources/orthanc.properties
new file mode 100644
index 000000000..fa159526b
--- /dev/null
+++ b/src/test/resources/orthanc.properties
@@ -0,0 +1,5 @@
+orthanc.base-url=https://orthanc.uni2growcameroun.com/
+orthanc.explorer-url=https://orthanc.uni2growcameroun.com/app/explorer.html
+orthanc.username=admin
+orthanc.password=adminADMIN!123#
+orthanc.enabled=true
\ No newline at end of file