Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -471,11 +471,27 @@ public String getUsersIdentities(List<UUID> elementsUuids, String userId) {
* Elements the user cannot read are omitted.
*/
public List<ReferencingElementInfos> getReferencingElementInfos(UUID elementUuid, String userId) {
// for now only STUDY_NODE references
List<UUID> referencedNodeUuids = directoryService.getElementInfos(elementUuid).getReferences().stream()
.filter(reference -> reference.getReferenceType() == ReferenceAttributes.ReferenceType.STUDY_NODE)
List<ReferenceAttributes> references = directoryService.getElementInfos(elementUuid).getReferences();

// a STUDY_NODE reference points directly at a node; a NETWORK_MODIFICATION reference points at a composite
// modification nested in a node's modification group, so it has to be resolved to that node first, then it
// is described exactly like a direct STUDY_NODE reference
List<UUID> networkModificationUuids = references.stream()
.filter(reference -> reference.getReferenceType() == ReferenceAttributes.ReferenceType.NETWORK_MODIFICATION)
.map(ReferenceAttributes::getReferenceId)
.toList();
Map<UUID, UUID> nodeUuidByNetworkModification = networkModificationUuids.isEmpty()
? Map.of()
: studyService.getNodeUuidsByNetworkModifications(networkModificationUuids.stream().distinct().toList());

List<UUID> referencedNodeUuids = references.stream()
.map(reference -> switch (reference.getReferenceType()) {
case STUDY_NODE -> reference.getReferenceId();
case NETWORK_MODIFICATION -> nodeUuidByNetworkModification.get(reference.getReferenceId());
default -> null;
})
.filter(Objects::nonNull)
.toList();
if (referencedNodeUuids.isEmpty()) {
return List.of();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ public List<NodeInfos> getNodesInfos(List<UUID> nodeUuids) {
}).getBody();
}

/**
* @param networkModificationUuids network modification uuids referenced by a shared composite modification
* @return network modification uuid -> uuid of the study node whose modification group ultimately contains it;
* modifications that cannot be attached to a node are omitted
*/
public Map<UUID, UUID> getNodeUuidsByNetworkModifications(List<UUID> networkModificationUuids) {
String path = UriComponentsBuilder.fromPath(DELIMITER + STUDY_SERVER_API_VERSION + "/nodes/uuids-by-network-modification")
.queryParam("uuids", networkModificationUuids)
.buildAndExpand()
.toUriString();
return restTemplate.exchange(studyServerBaseUri + path, HttpMethod.GET, null,
new ParameterizedTypeReference<Map<UUID, UUID>>() {
}).getBody();
}

private HttpHeaders getHeaders(String userId) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,16 @@ class ReferencingElementInfosTest {
private static final UUID SHARED_ELEMENT_UUID = UUID.randomUUID();
private static final UUID NODE_1_UUID = UUID.randomUUID();
private static final UUID NODE_2_UUID = UUID.randomUUID();
private static final UUID MODIFICATION_1_UUID = UUID.randomUUID();
private static final UUID MODIFICATION_2_UUID = UUID.randomUUID();
private static final UUID STUDY_1_UUID = UUID.randomUUID();
private static final UUID STUDY_2_UUID = UUID.randomUUID();

private static final String SHARED_ELEMENT_PATH = "/v1/elements/" + SHARED_ELEMENT_UUID;
private static final String ELEMENTS_PATH = "/v1/elements";
private static final String ELEMENTS_PATHS_PATH = "/v1/elements/paths";
private static final String NODES_INFOS_PATH = "/v1/nodes/infos";
private static final String NODES_BY_MODIFICATION_PATH = "/v1/nodes/uuids-by-network-modification";
private static final String USERS_IDENTITIES_PATH = "/v1/users/identities";

@BeforeEach
Expand Down Expand Up @@ -114,14 +117,23 @@ private void stubUsersIdentities() {
}

private void stubSharedElementReferences(UUID... referencedNodeUuids) throws Exception {
ElementAttributes sharedElement = new ElementAttributes(SHARED_ELEMENT_UUID, "sharedModification", "MODIFICATION", OWNER_SUB, 0L, null);
sharedElement.setReferences(Arrays.stream(referencedNodeUuids)
stubSharedElementReferences(Arrays.stream(referencedNodeUuids)
.map(nodeUuid -> new ReferenceAttributes(nodeUuid, ReferenceAttributes.ReferenceType.STUDY_NODE))
.toList());
}

private void stubSharedElementReferences(List<ReferenceAttributes> references) throws Exception {
ElementAttributes sharedElement = new ElementAttributes(SHARED_ELEMENT_UUID, "sharedModification", "MODIFICATION", OWNER_SUB, 0L, null);
sharedElement.setReferences(references);
wireMockServer.stubFor(WireMock.get(WireMock.urlPathEqualTo(SHARED_ELEMENT_PATH))
.willReturn(jsonResponse(sharedElement)));
}

private void stubNodeUuidsByNetworkModification(Map<UUID, UUID> nodeUuidByModificationUuid) throws Exception {
wireMockServer.stubFor(WireMock.get(WireMock.urlPathEqualTo(NODES_BY_MODIFICATION_PATH))
.willReturn(jsonResponse(nodeUuidByModificationUuid)));
}

private void stubNodesInfos(NodeInfos... nodesInfos) throws Exception {
wireMockServer.stubFor(WireMock.get(WireMock.urlPathEqualTo(NODES_INFOS_PATH))
.willReturn(jsonResponse(List.of(nodesInfos))));
Expand Down Expand Up @@ -289,8 +301,89 @@ void testElementWithoutReferences() throws Exception {

// without any reference, nothing is left to describe: no other server is reached
wireMockServer.verify(0, WireMock.getRequestedFor(WireMock.urlPathEqualTo(NODES_INFOS_PATH)));
wireMockServer.verify(0, WireMock.getRequestedFor(WireMock.urlPathEqualTo(NODES_BY_MODIFICATION_PATH)));
wireMockServer.verify(0, WireMock.getRequestedFor(WireMock.urlPathEqualTo(ELEMENTS_PATH)));
wireMockServer.verify(0, WireMock.getRequestedFor(WireMock.urlPathEqualTo(ELEMENTS_PATHS_PATH)));
wireMockServer.verify(0, WireMock.getRequestedFor(WireMock.urlPathEqualTo(USERS_IDENTITIES_PATH)));
}

@Test
void testNetworkModificationReferenceResolvedToItsNodeAndStudy() throws Exception {
stubSharedElementReferences(List.of(
new ReferenceAttributes(MODIFICATION_1_UUID, ReferenceAttributes.ReferenceType.NETWORK_MODIFICATION)));
// the modification lives (possibly nested) in node1's modification group
stubNodeUuidsByNetworkModification(Map.of(MODIFICATION_1_UUID, NODE_1_UUID));
stubNodesInfos(new NodeInfos(NODE_1_UUID, "node1", STUDY_1_UUID));
stubStudies(studyStub(STUDY_1_UUID, "study1"));
stubStudiesPaths(Map.of(STUDY_1_UUID, pathStub(STUDY_1_UUID, "study1", "root")));

List<ReferencingElementInfos> infos = getReferencingElementInfos();

assertEquals(1, infos.size());
ReferencingElementInfos info = infos.getFirst();
// a network modification reference is described exactly like a direct study-node reference
assertEquals("node1", info.node());
assertEquals("study1", info.elementName());
assertEquals("STUDY", info.type());
assertEquals(List.of("root"), info.path());
assertEquals("John Doe", info.ownerLabel());
assertEquals(LAST_MODIFICATION_DATE, info.lastModificationDate());
assertEquals(MODIFIER_SUB, info.lastModifiedByLabel());
}

@Test
void testStudyNodeAndNetworkModificationReferencesAreMerged() throws Exception {
stubSharedElementReferences(List.of(
new ReferenceAttributes(NODE_1_UUID, ReferenceAttributes.ReferenceType.STUDY_NODE),
new ReferenceAttributes(MODIFICATION_1_UUID, ReferenceAttributes.ReferenceType.NETWORK_MODIFICATION)));
stubNodeUuidsByNetworkModification(Map.of(MODIFICATION_1_UUID, NODE_2_UUID));
stubNodesInfos(
new NodeInfos(NODE_1_UUID, "node1", STUDY_1_UUID),
new NodeInfos(NODE_2_UUID, "node2", STUDY_2_UUID));
stubStudies(studyStub(STUDY_1_UUID, "study1"), studyStub(STUDY_2_UUID, "study2"));
stubStudiesPaths(Map.of(
STUDY_1_UUID, pathStub(STUDY_1_UUID, "study1", "root"),
STUDY_2_UUID, pathStub(STUDY_2_UUID, "study2", "root")));

List<ReferencingElementInfos> infos = getReferencingElementInfos();

// one row per reference, in reference order
assertEquals(2, infos.size());
assertEquals("node1", infos.get(0).node());
assertEquals("study1", infos.get(0).elementName());
assertEquals("node2", infos.get(1).node());
assertEquals("study2", infos.get(1).elementName());
// both nodes are resolved in a single call to the study-server
wireMockServer.verify(1, WireMock.getRequestedFor(WireMock.urlPathEqualTo(NODES_INFOS_PATH)));
}

@Test
void testUnresolvedNetworkModificationReferenceIsOmitted() throws Exception {
stubSharedElementReferences(List.of(
new ReferenceAttributes(MODIFICATION_1_UUID, ReferenceAttributes.ReferenceType.NETWORK_MODIFICATION),
new ReferenceAttributes(MODIFICATION_2_UUID, ReferenceAttributes.ReferenceType.NETWORK_MODIFICATION)));
// the study-server can only attach one of the two modifications to a node
stubNodeUuidsByNetworkModification(Map.of(MODIFICATION_1_UUID, NODE_1_UUID));
stubNodesInfos(new NodeInfos(NODE_1_UUID, "node1", STUDY_1_UUID));
stubStudies(studyStub(STUDY_1_UUID, "study1"));
stubStudiesPaths(Map.of(STUDY_1_UUID, pathStub(STUDY_1_UUID, "study1", "root")));

List<ReferencingElementInfos> infos = getReferencingElementInfos();

assertEquals(1, infos.size());
assertEquals("node1", infos.getFirst().node());
}

@Test
void testNetworkModificationServerNotCalledWithoutSuchReference() throws Exception {
stubSharedElementReferences(NODE_1_UUID);
stubNodesInfos(new NodeInfos(NODE_1_UUID, "node1", STUDY_1_UUID));
stubStudies(studyStub(STUDY_1_UUID, "study1"));
stubStudiesPaths(Map.of(STUDY_1_UUID, pathStub(STUDY_1_UUID, "study1", "root")));

assertEquals(1, getReferencingElementInfos().size());

// no NETWORK_MODIFICATION reference: the resolution endpoint is never hit
wireMockServer.verify(0, WireMock.getRequestedFor(WireMock.urlPathEqualTo(NODES_BY_MODIFICATION_PATH)));
}
}
Loading