Skip to content

Commit 3b023eb

Browse files
authored
Merge pull request DSpace#11967 from dataquest-dev/fix-openaire-integration
Fix OpenAIRE integration: null handling and HTTP client lifecycle
2 parents 6ff0abc + d7dae69 commit 3b023eb

5 files changed

Lines changed: 100 additions & 3 deletions

File tree

checkstyle-suppressions.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
on JMockIt Expectations blocks and similar. See https://github.com/checkstyle/checkstyle/issues/3739 -->
99
<suppress checks="Indentation" files="src[/\\]test[/\\]java"/>
1010
<suppress checks="Regexp" files="DSpaceHttpClientFactory\.java"/>
11+
<suppress checks="Regexp" files="OpenAIRERestConnectorTest\.java"/>
1112
</suppressions>

dspace-api/src/main/java/org/dspace/external/OpenaireRestConnector.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,11 @@ public InputStream get(String file, String accessToken) {
206206
break;
207207
}
208208

209-
// do not close this httpClient
210-
result = getResponse.getEntity().getContent();
209+
// the client will be closed, we need to copy the response stream to a new one that we can return
210+
try (InputStream is = getResponse.getEntity().getContent()) {
211+
byte[] bytes = is.readAllBytes();
212+
result = new java.io.ByteArrayInputStream(bytes);
213+
}
211214
}
212215
} catch (MalformedURLException e1) {
213216
getGotError(e1, url + '/' + file);

dspace-api/src/main/java/org/dspace/external/provider/impl/OpenaireFundingDataProvider.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import eu.openaire.oaf.model.base.FundingTreeType;
2929
import eu.openaire.oaf.model.base.FundingType;
3030
import eu.openaire.oaf.model.base.Project;
31+
import org.apache.commons.lang3.StringUtils;
3132
import org.apache.commons.lang3.Strings;
3233
import org.apache.logging.log4j.Logger;
3334
import org.dspace.content.dto.MetadataValueDTO;
@@ -169,7 +170,19 @@ public int getNumberOfResults(String query) {
169170
String encodedQuery = encodeValue(query);
170171

171172
Response projectResponse = connector.searchProjectByKeywords(0, 0, encodedQuery);
172-
return Integer.parseInt(projectResponse.getHeader().getTotal());
173+
if (projectResponse == null || projectResponse.getHeader() == null) {
174+
return 0;
175+
}
176+
String total = projectResponse.getHeader().getTotal();
177+
if (StringUtils.isBlank(total)) {
178+
return 0;
179+
}
180+
try {
181+
return Integer.parseInt(total);
182+
} catch (NumberFormatException e) {
183+
log.error("Failed to parse search result count from OpenAIRE: {}", e.getMessage());
184+
return 0;
185+
}
173186
}
174187

175188
/**
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
package org.dspace.external;
9+
10+
import static org.junit.Assert.assertTrue;
11+
import static org.mockito.Mockito.doReturn;
12+
import static org.mockito.Mockito.spy;
13+
import static org.mockito.Mockito.when;
14+
15+
import java.io.IOException;
16+
import java.io.InputStream;
17+
import java.nio.charset.StandardCharsets;
18+
19+
import eu.openaire.jaxb.model.Response;
20+
import okhttp3.mockwebserver.MockResponse;
21+
import okhttp3.mockwebserver.MockWebServer;
22+
import org.apache.http.client.methods.HttpGet;
23+
import org.apache.http.impl.client.CloseableHttpClient;
24+
import org.apache.http.impl.client.HttpClientBuilder;
25+
import org.dspace.app.client.DSpaceHttpClientFactory;
26+
import org.junit.Test;
27+
import org.mockito.MockedStatic;
28+
import org.mockito.Mockito;
29+
30+
31+
public class OpenAIRERestConnectorTest {
32+
33+
@Test
34+
public void searchProjectByKeywords() throws IOException {
35+
try (InputStream is = this.getClass().getResourceAsStream("openaire-projects.xml");
36+
MockWebServer mockServer = new MockWebServer()) {
37+
String projects = new String(is.readAllBytes(), StandardCharsets.UTF_8)
38+
.replaceAll("( mushroom)", "( DEADBEEF)");
39+
mockServer.enqueue(new MockResponse().setResponseCode(200).setBody(projects));
40+
41+
// setup mocks so we don't have to set whole DSpace kernel etc.
42+
// still, the idea is to test how the get method behaves
43+
CloseableHttpClient httpClient = spy(HttpClientBuilder.create().build());
44+
doReturn(httpClient.execute(new HttpGet(mockServer.url("").toString())))
45+
.when(httpClient).execute(Mockito.any());
46+
47+
DSpaceHttpClientFactory mock = Mockito.mock(DSpaceHttpClientFactory.class);
48+
when(mock.build()).thenReturn(httpClient);
49+
50+
try (MockedStatic<DSpaceHttpClientFactory> mockedFactory =
51+
Mockito.mockStatic(DSpaceHttpClientFactory.class)) {
52+
mockedFactory.when(DSpaceHttpClientFactory::getInstance).thenReturn(mock);
53+
OpenaireRestConnector connector = new OpenaireRestConnector(mockServer.url("").toString());
54+
Response response = connector.searchProjectByKeywords(0, 10, "keyword");
55+
// Basically check it doesn't throw UnmarshallerException and that we are getting our mocked response
56+
assertTrue("Expected the query to contain the replaced keyword",
57+
response.getHeader().getQuery().contains("DEADBEEF"));
58+
}
59+
}
60+
}
61+
}

dspace-api/src/test/java/org/dspace/external/provider/impl/OpenaireFundingDataProviderTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
import java.util.List;
1515
import java.util.Optional;
1616

17+
import eu.openaire.jaxb.model.Response;
1718
import org.dspace.AbstractDSpaceTest;
19+
import org.dspace.external.OpenaireRestConnector;
1820
import org.dspace.external.factory.ExternalServiceFactory;
1921
import org.dspace.external.model.ExternalDataObject;
2022
import org.dspace.external.provider.ExternalDataProvider;
@@ -102,4 +104,21 @@ public void testGetDataObjectWInvalidId() {
102104

103105
assertTrue("openaireFunding.getExternalDataObject.notExists:WRONGID", result.isEmpty());
104106
}
107+
108+
@Test
109+
public void testGetNumberOfResultsWhenResponseIsNull() {
110+
// Create a mock connector that returns null
111+
OpenaireFundingDataProvider provider = new OpenaireFundingDataProvider();
112+
provider.setSourceIdentifier("test");
113+
provider.setConnector(new OpenaireRestConnector("test") {
114+
@Override
115+
public Response searchProjectByKeywords(int page, int size, String... keywords) {
116+
return null;
117+
}
118+
});
119+
120+
// Should return 0 when response is null, not throw NullPointerException
121+
int result = provider.getNumberOfResults("test");
122+
assertEquals("Should return 0 when response is null", 0, result);
123+
}
105124
}

0 commit comments

Comments
 (0)