diff --git a/tmf-extension/src/main/java/org/seamware/edc/tmf/OrganizationApiClient.java b/tmf-extension/src/main/java/org/seamware/edc/tmf/OrganizationApiClient.java index cec60ad..5dc7778 100644 --- a/tmf-extension/src/main/java/org/seamware/edc/tmf/OrganizationApiClient.java +++ b/tmf-extension/src/main/java/org/seamware/edc/tmf/OrganizationApiClient.java @@ -35,6 +35,7 @@ public class OrganizationApiClient extends ApiClient { private static final String PARTY_CHARACTERISTIC_NAME = "partyCharacteristic.name"; private static final String ORGANIZATION_PATH = "organization"; + private static final int PAGE_SIZE = 100; private final String baseUrl; private final ObjectMapper objectMapper; @@ -61,28 +62,37 @@ public OrganizationVO getOrganization(String tmfId) { } public Optional getByDid(String did) { - HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder(); - urlBuilder.addPathSegment(ORGANIZATION_PATH); - urlBuilder.addQueryParameter(PARTY_CHARACTERISTIC_NAME, PARTY_CHARACTERISTIC_DID); - Request request = new Request.Builder().url(urlBuilder.build()).build(); - try (ResponseBody responseBody = executeRequest(request)) { - return objectMapper - .readValue(responseBody.bytes(), new TypeReference>() {}) - .stream() - .filter( - organizationVO -> - Optional.ofNullable(organizationVO.getPartyCharacteristic()) - .orElse(List.of()) - .stream() - .filter( - characteristicVO -> - characteristicVO.getName().equals(PARTY_CHARACTERISTIC_DID)) - .anyMatch(characteristicVO -> characteristicVO.getValue().equals(did))) - .findAny(); - } catch (IOException e) { - monitor.warning("Was not able to get the organization by did.", e); - throw new BadGatewayException("Was not able to get the organization by did."); + // Page through the organizations: the TMForum query only filters by characteristic name, + // so the matching org may sit beyond the first page. Stop on the first match or when a + // short page signals the end, otherwise a present org could be missed and re-created. + Optional match = Optional.empty(); + boolean morePages = true; + int offset = 0; + while (match.isEmpty() && morePages) { + HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder(); + urlBuilder.addPathSegment(ORGANIZATION_PATH); + urlBuilder.addQueryParameter(PARTY_CHARACTERISTIC_NAME, PARTY_CHARACTERISTIC_DID); + urlBuilder.addQueryParameter(OFFSET_PARAM, String.valueOf(offset)); + urlBuilder.addQueryParameter(LIMIT_PARAM, String.valueOf(PAGE_SIZE)); + Request request = new Request.Builder().url(urlBuilder.build()).build(); + try (ResponseBody responseBody = executeRequest(request)) { + List organizations = + objectMapper.readValue(responseBody.bytes(), new TypeReference<>() {}); + match = organizations.stream().filter(org -> hasDid(org, did)).findAny(); + morePages = organizations.size() == PAGE_SIZE; + offset += PAGE_SIZE; + } catch (IOException e) { + monitor.warning("Was not able to get the organization by did.", e); + throw new BadGatewayException("Was not able to get the organization by did."); + } } + return match; + } + + private static boolean hasDid(OrganizationVO organizationVO, String did) { + return Optional.ofNullable(organizationVO.getPartyCharacteristic()).orElse(List.of()).stream() + .filter(characteristicVO -> characteristicVO.getName().equals(PARTY_CHARACTERISTIC_DID)) + .anyMatch(characteristicVO -> characteristicVO.getValue().equals(did)); } /** Creates the given organization */ diff --git a/tmf-extension/src/main/java/org/seamware/edc/tmf/ParticipantResolver.java b/tmf-extension/src/main/java/org/seamware/edc/tmf/ParticipantResolver.java index 5f1e566..a1e76a1 100644 --- a/tmf-extension/src/main/java/org/seamware/edc/tmf/ParticipantResolver.java +++ b/tmf-extension/src/main/java/org/seamware/edc/tmf/ParticipantResolver.java @@ -60,7 +60,7 @@ public Optional getOrganization(String tmfId) { } public String getTmfId(String did) { - return organizationApi.getByDid(did).orElse(createOrganization(did)).getId(); + return organizationApi.getByDid(did).orElseGet(() -> createOrganization(did)).getId(); } private OrganizationVO createOrganization(String did) { diff --git a/tmf-extension/src/test/java/org/seamware/edc/tmf/OrganizationApiClientTest.java b/tmf-extension/src/test/java/org/seamware/edc/tmf/OrganizationApiClientTest.java index 7693697..5e5fe72 100644 --- a/tmf-extension/src/test/java/org/seamware/edc/tmf/OrganizationApiClientTest.java +++ b/tmf-extension/src/test/java/org/seamware/edc/tmf/OrganizationApiClientTest.java @@ -19,6 +19,8 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import okhttp3.mockwebserver.RecordedRequest; import org.eclipse.edc.web.spi.exception.BadGatewayException; import org.junit.jupiter.api.Test; @@ -85,7 +87,8 @@ public void testGetByDid_success() throws Exception { "The correct organization should be returned."); RecordedRequest recordedRequest = mockWebServer.takeRequest(); - assertEquals("/organization?partyCharacteristic.name=did", recordedRequest.getPath()); + assertEquals( + "/organization?partyCharacteristic.name=did&offset=0&limit=100", recordedRequest.getPath()); } @Test @@ -99,7 +102,8 @@ public void testGetByDid_success_multiple_orgs() throws Exception { "The correct organization should be returned."); RecordedRequest recordedRequest = mockWebServer.takeRequest(); - assertEquals("/organization?partyCharacteristic.name=did", recordedRequest.getPath()); + assertEquals( + "/organization?partyCharacteristic.name=did&offset=0&limit=100", recordedRequest.getPath()); } @Test @@ -111,7 +115,8 @@ public void testGetByDid_success_no_org() throws Exception { "If the organization does not exist, it should not be returned."); RecordedRequest recordedRequest = mockWebServer.takeRequest(); - assertEquals("/organization?partyCharacteristic.name=did", recordedRequest.getPath()); + assertEquals( + "/organization?partyCharacteristic.name=did&offset=0&limit=100", recordedRequest.getPath()); } @Test @@ -123,7 +128,33 @@ public void testGetByDid_success_other_orgs() throws Exception { "If the organization does not exist, it should not be returned."); RecordedRequest recordedRequest = mockWebServer.takeRequest(); - assertEquals("/organization?partyCharacteristic.name=did", recordedRequest.getPath()); + assertEquals( + "/organization?partyCharacteristic.name=did&offset=0&limit=100", recordedRequest.getPath()); + } + + @Test + public void testGetByDid_success_second_page() throws Exception { + // First page is full (100 orgs, none matching) so the loop must fetch a second page, + // where the matching org lives. + List firstPage = + IntStream.range(0, 100) + .mapToObj(i -> getValidOrganization("did:web:other-" + i)) + .collect(Collectors.toList()); + OrganizationVO testOrganization = getValidOrganization(TEST_ORGANIZATION_DID); + mockResponse(200, firstPage); + mockResponse(200, List.of(testOrganization)); + + assertEquals( + testOrganization, + organizationApiClient.getByDid(TEST_ORGANIZATION_DID).get(), + "The organization on the second page should be returned."); + + assertEquals( + "/organization?partyCharacteristic.name=did&offset=0&limit=100", + mockWebServer.takeRequest().getPath()); + assertEquals( + "/organization?partyCharacteristic.name=did&offset=100&limit=100", + mockWebServer.takeRequest().getPath()); } @Test