-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #98 from xenit-eu/curie-provider
Add custom CurieProvider
- Loading branch information
Showing
22 changed files
with
375 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
...est/src/main/java/com/contentgrid/spring/data/rest/hal/ContentGridCurieConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.contentgrid.spring.data.rest.hal; | ||
|
||
import org.springframework.beans.factory.ObjectProvider; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.hateoas.mediatype.hal.CurieProvider; | ||
|
||
@Configuration(proxyBeanMethods = false) | ||
public class ContentGridCurieConfiguration { | ||
|
||
@Bean | ||
CurieProvider contentGridCurieProvider(ObjectProvider<CurieProviderCustomizer> customizers) { | ||
CurieProviderBuilder curieProvider = new ContentGridCurieProvider(); | ||
|
||
for (CurieProviderCustomizer customizer : customizers) { | ||
curieProvider = customizer.customize(curieProvider); | ||
} | ||
|
||
return curieProvider.build(); | ||
} | ||
|
||
} |
95 changes: 95 additions & 0 deletions
95
...ata-rest/src/main/java/com/contentgrid/spring/data/rest/hal/ContentGridCurieProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package com.contentgrid.spring.data.rest.hal; | ||
|
||
import java.util.Collection; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.hateoas.IanaLinkRelations; | ||
import org.springframework.hateoas.IanaUriSchemes; | ||
import org.springframework.hateoas.Link; | ||
import org.springframework.hateoas.LinkRelation; | ||
import org.springframework.hateoas.Links; | ||
import org.springframework.hateoas.UriTemplate; | ||
import org.springframework.hateoas.mediatype.hal.CurieProvider; | ||
import org.springframework.hateoas.mediatype.hal.HalLinkRelation; | ||
|
||
@RequiredArgsConstructor | ||
class ContentGridCurieProvider implements CurieProvider, CurieProviderBuilder { | ||
|
||
private final Map<String, UriTemplate> curies; | ||
|
||
public ContentGridCurieProvider() { | ||
this(Map.of()); | ||
} | ||
|
||
@Override | ||
public HalLinkRelation getNamespacedRelFrom(Link link) { | ||
return getNamespacedRelFor(link.getRel()); | ||
} | ||
|
||
@Override | ||
public HalLinkRelation getNamespacedRelFor(LinkRelation rel) { | ||
assertRegisteredCurie(rel); | ||
return HalLinkRelation.of(rel); | ||
} | ||
|
||
@Override | ||
public Collection<?> getCurieInformation(Links links) { | ||
return curies.entrySet().stream() | ||
.map(it -> createCurieLink(it.getKey(), it.getValue())) | ||
.toList(); | ||
} | ||
|
||
private Link createCurieLink(String name, UriTemplate template) { | ||
return Link.of( | ||
template, | ||
HalLinkRelation.CURIES | ||
).withName(name); | ||
} | ||
|
||
@Override | ||
public CurieProviderBuilder withCurie(String prefix, UriTemplate template) { | ||
if(curies.containsKey(prefix)) { | ||
throw new IllegalArgumentException("CURIE prefix '%s' is already registered with template '%s' and can not be re-registered with template '%s'.".formatted( | ||
prefix, | ||
curies.get(prefix), | ||
template | ||
)); | ||
} | ||
if(IanaUriSchemes.isIanaUriScheme(prefix)) { | ||
throw new IllegalArgumentException("CURIE prefix '%s' can not be an IANA-registered URI scheme.".formatted(prefix)); | ||
} | ||
var curies = new HashMap<>(this.curies); | ||
curies.put(prefix, template); | ||
return new ContentGridCurieProvider(curies); | ||
} | ||
|
||
@Override | ||
public CurieProvider build() { | ||
return this; | ||
} | ||
|
||
private void assertRegisteredCurie(LinkRelation rel) { | ||
var relation = rel.value(); | ||
int firstColonIndex = relation.indexOf(':'); | ||
|
||
String curie = firstColonIndex == -1 ? null : relation.substring(0, firstColonIndex); | ||
|
||
if(curie == null) { | ||
// Not curie -> need to check if it's a registered link relation | ||
if(!IanaLinkRelations.isIanaRel(relation)) { | ||
throw new IllegalArgumentException("Relation '%s' is not an IANA-registered relation".formatted(relation)); | ||
} | ||
return; | ||
} | ||
|
||
if(IanaUriSchemes.isIanaUriScheme(curie)) { | ||
// Not a curie, but a RFC 5988 #4.2a extension relation type | ||
return; | ||
} | ||
|
||
if(!curies.containsKey(curie)) { | ||
throw new IllegalArgumentException("Relation '%s' uses CURIE that is not registered".formatted(relation)); | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...ng-data-rest/src/main/java/com/contentgrid/spring/data/rest/hal/CurieProviderBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.contentgrid.spring.data.rest.hal; | ||
|
||
import org.springframework.hateoas.UriTemplate; | ||
import org.springframework.hateoas.mediatype.hal.CurieProvider; | ||
|
||
/** | ||
* Fluent builder for {@link CurieProvider} | ||
*/ | ||
public interface CurieProviderBuilder { | ||
|
||
/** | ||
* Adds a mapping from CURIE prefix to a {@link UriTemplate} for resolving the CURIE against | ||
* @param prefix CURIE prefix | ||
* @param template Template to use to resolve the CURIE | ||
* @return Copy with new curie mapping applied | ||
*/ | ||
CurieProviderBuilder withCurie(String prefix, UriTemplate template); | ||
|
||
/** | ||
* Builds a {@link CurieProvider} with the mappings specified in the builder | ||
* @return An immutable {@link CurieProvider} instance | ||
*/ | ||
CurieProvider build(); | ||
} |
47 changes: 47 additions & 0 deletions
47
...data-rest/src/main/java/com/contentgrid/spring/data/rest/hal/CurieProviderCustomizer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.contentgrid.spring.data.rest.hal; | ||
|
||
import org.springframework.hateoas.UriTemplate; | ||
|
||
/** | ||
* Customizer interface for {@link CurieProviderBuilder}. | ||
* <p> | ||
* By implementing this interface and exposing them as beans, they will automatically be applied to the default {@link org.springframework.hateoas.mediatype.hal.CurieProvider} | ||
*/ | ||
@FunctionalInterface | ||
public interface CurieProviderCustomizer { | ||
|
||
/** | ||
* Customize the {@link CurieProviderBuilder} | ||
* @param builder current builder | ||
* @return builder with the desired customizations applied | ||
*/ | ||
CurieProviderBuilder customize(CurieProviderBuilder builder); | ||
|
||
/** | ||
* Register a mapping between CURIE prefix and a URI template | ||
* | ||
* @param curiePrefix The CURIE prefix | ||
* @param template The URI template to use | ||
* @return customizer that registers a CURIE prefix mapping | ||
* | ||
* @see CurieProviderBuilder#withCurie(String, UriTemplate) for the builder method | ||
* @see #register(String, String) for a shortcut that does not require a {@link UriTemplate} instance | ||
*/ | ||
static CurieProviderCustomizer register(String curiePrefix, UriTemplate template) { | ||
return builder -> builder.withCurie(curiePrefix, template); | ||
} | ||
|
||
/** | ||
* Register a mapping between CURIE prefix and a URI template | ||
* | ||
* @param curiePrefix The CURIE prefix | ||
* @param template The URI template to use | ||
* @return customizer that registers a CURIE prefix mapping | ||
* | ||
* @see CurieProviderBuilder#withCurie(String, UriTemplate) for the builder method | ||
* @see #register(String, UriTemplate) for passing a {@link UriTemplate} directly | ||
*/ | ||
static CurieProviderCustomizer register(String curiePrefix, String template) { | ||
return register(curiePrefix, UriTemplate.of(template)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
...src/test/java/com/contentgrid/spring/data/rest/hal/ContentGridCurieConfigurationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.contentgrid.spring.data.rest.hal; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import com.contentgrid.spring.data.rest.hal.ContentGridCurieConfigurationTest.CurieProviderCustomizers; | ||
import org.assertj.core.api.InstanceOfAssertFactories; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.context.TestConfiguration; | ||
import org.springframework.context.ApplicationContext; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.hateoas.Link; | ||
import org.springframework.hateoas.Links; | ||
import org.springframework.hateoas.UriTemplate; | ||
import org.springframework.hateoas.mediatype.hal.CurieProvider; | ||
import org.springframework.hateoas.mediatype.hal.HalLinkRelation; | ||
import org.springframework.test.context.ContextConfiguration; | ||
|
||
@SpringBootTest | ||
@ContextConfiguration(classes = { | ||
ContentGridCurieConfiguration.class, | ||
CurieProviderCustomizers.class | ||
}) | ||
@AutoConfigureMockMvc(printOnlyOnFailure = false) | ||
class ContentGridCurieConfigurationTest { | ||
|
||
@TestConfiguration(proxyBeanMethods = false) | ||
static class CurieProviderCustomizers { | ||
@Bean | ||
CurieProviderCustomizer curieProviderTestCustomizer() { | ||
return builder -> builder.withCurie("test", UriTemplate.of("https://example.com/rels/{x}")); | ||
} | ||
|
||
@Bean | ||
CurieProviderCustomizer curieProviderExtCustomizer() { | ||
return builder -> builder.withCurie("ext", UriTemplate.of("https://ext.invalid/{rel}")); | ||
} | ||
} | ||
|
||
@Test | ||
void customizersApplied(ApplicationContext context) { | ||
var curies = context.getBean(CurieProvider.class).getCurieInformation(Links.NONE); | ||
|
||
assertThat(curies).asInstanceOf(InstanceOfAssertFactories.list(Link.class)).containsExactlyInAnyOrder( | ||
Link.of("https://example.com/rels/{x}", HalLinkRelation.CURIES).withName("test"), | ||
Link.of("https://ext.invalid/{rel}", HalLinkRelation.CURIES).withName("ext") | ||
); | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.