diff --git a/api/src/main/java/com/faforever/commons/api/elide/ElideNavigator.java b/api/src/main/java/com/faforever/commons/api/elide/ElideNavigator.java index 01f9013f..47127b7e 100644 --- a/api/src/main/java/com/faforever/commons/api/elide/ElideNavigator.java +++ b/api/src/main/java/com/faforever/commons/api/elide/ElideNavigator.java @@ -114,6 +114,27 @@ public ElideNavigatorSelector navigateRelationship(@N return new ElideNavigator<>(dtoClass, this); } + @Override + public ElideNavigatorOnRelationshipLink relationshipLink(@NotNull Class entityClass, + @NotNull String name) { + if (!includes.isEmpty()) { + throw new IllegalStateException("Cannot navigate to a relationship link with includes on parent"); + } + log.trace("relationship link added: {}", name); + String route = build() + "/relationships/" + name; + return new ElideNavigatorOnRelationshipLink<>() { + @Override + public String build() { + return route; + } + + @Override + public Class getDtoClass() { + return entityClass; + } + }; + } + /** * Add a sorting rule to the navigator * Important: You need to give the full qualified route, there is NO referencing of parent relationships. diff --git a/api/src/main/java/com/faforever/commons/api/elide/ElideNavigatorOnId.java b/api/src/main/java/com/faforever/commons/api/elide/ElideNavigatorOnId.java index cc929182..c218e6b0 100644 --- a/api/src/main/java/com/faforever/commons/api/elide/ElideNavigatorOnId.java +++ b/api/src/main/java/com/faforever/commons/api/elide/ElideNavigatorOnId.java @@ -6,4 +6,17 @@ public interface ElideNavigatorOnId extends ElideEndpoint ElideNavigatorSelector navigateRelationship(Class entityClass, String name); + /** + * Points the navigator at the JSON:API relationship endpoint + * ({@code /data/{type}/{id}/relationships/{name}}), used to read or modify the relationship linkage itself + * (e.g. PATCH/POST/DELETE to add, replace or remove members). This differs from + * {@link #navigateRelationship(Class, String)}, which addresses the related resource(s). + * + *

This is a terminal operation: the returned navigator only allows {@link + * ElideNavigatorOnRelationshipLink#build()}. Includes are not allowed on the parent. + * + * @param entityClass the type of the related resource(s) the linkage points at + */ + ElideNavigatorOnRelationshipLink relationshipLink(Class entityClass, String name); + } diff --git a/api/src/main/java/com/faforever/commons/api/elide/ElideNavigatorOnRelationshipLink.java b/api/src/main/java/com/faforever/commons/api/elide/ElideNavigatorOnRelationshipLink.java new file mode 100644 index 00000000..4e528f04 --- /dev/null +++ b/api/src/main/java/com/faforever/commons/api/elide/ElideNavigatorOnRelationshipLink.java @@ -0,0 +1,13 @@ +package com.faforever.commons.api.elide; + +/** + * Terminal navigator pointing at a JSON:API relationship endpoint + * ({@code /data/{type}/{id}/relationships/{name}}). It only exposes {@link #build()} and the related entity + * type, because a relationship link addresses the linkage itself (read or modify via PATCH/POST/DELETE) and + * cannot be navigated further. {@code T} is the type of the related resource(s) the linkage points at. + */ +public interface ElideNavigatorOnRelationshipLink { + String build(); + + Class getDtoClass(); +} diff --git a/api/src/test/java/com/faforever/commons/api/elide/ElideNavigatorTest.java b/api/src/test/java/com/faforever/commons/api/elide/ElideNavigatorTest.java index ca742c08..e5942f7f 100644 --- a/api/src/test/java/com/faforever/commons/api/elide/ElideNavigatorTest.java +++ b/api/src/test/java/com/faforever/commons/api/elide/ElideNavigatorTest.java @@ -87,6 +87,33 @@ void testNavigateFromIdToId() { .build(), is("/data/mapPool/5/mapVersion/1234?include=author")); } + @Test + void testRelationshipLink() { + ElideNavigatorOnRelationshipLink navigator = ElideNavigator.of(MapPool.class) + .id("5") + .relationshipLink(MapVersion.class, "mapVersion"); + assertThat(navigator.build(), is("/data/mapPool/5/relationships/mapVersion")); + assertThat(navigator.getDtoClass(), is(MapVersion.class)); + } + + @Test + void testRelationshipLinkAfterNavigateRelationship() { + assertThat(ElideNavigator.of(MapPool.class) + .id("5") + .navigateRelationship(MapVersion.class, "mapVersion") + .id("1234") + .relationshipLink(MapVersion.class, "map") + .build(), is("/data/mapPool/5/mapVersion/1234/relationships/map")); + } + + @Test + void testCannotRelationshipLinkAfterIncludes() { + assertThrows(IllegalStateException.class, () -> ElideNavigator.of(MapPool.class) + .id("5") + .addInclude("mapVersion") + .relationshipLink(MapVersion.class, "mapVersion")); + } + @Test void testGetListPages() { assertThat(ElideNavigator.of(MapPoolAssignment.class)