Support link extraction with official HAL and HAL-FORMS media types

Register the HalLinkExtractor for both the official HAL media type
(application/vnd.hal+json) and the HAL-FORMS media type
(application/prs.hal-forms+json)

See gh-965
This commit is contained in:
Oliver Drotbohm
2025-05-29 22:24:43 +02:00
committed by Andy Wilkinson
parent c74602f0bb
commit 9144684768
3 changed files with 30 additions and 1 deletions

View File

@@ -30,6 +30,7 @@ import org.springframework.restdocs.operation.OperationResponse;
* content type.
*
* @author Andy Wilkinson
* @author Oliver Drotbohm
*/
class ContentTypeLinkExtractor implements LinkExtractor {
@@ -37,7 +38,11 @@ class ContentTypeLinkExtractor implements LinkExtractor {
ContentTypeLinkExtractor() {
this.linkExtractors.put(MediaType.APPLICATION_JSON, new AtomLinkExtractor());
this.linkExtractors.put(HalLinkExtractor.HAL_MEDIA_TYPE, new HalLinkExtractor());
LinkExtractor halLinkExtractor = new HalLinkExtractor();
this.linkExtractors.put(HalLinkExtractor.HAL_MEDIA_TYPE, halLinkExtractor);
this.linkExtractors.put(HalLinkExtractor.VND_HAL_MEDIA_TYPE, halLinkExtractor);
this.linkExtractors.put(HalLinkExtractor.HAL_FORMS_MEDIA_TYPE, halLinkExtractor);
}
ContentTypeLinkExtractor(Map<MediaType, LinkExtractor> linkExtractors) {

View File

@@ -34,6 +34,8 @@ import org.springframework.http.MediaType;
class HalLinkExtractor extends AbstractJsonLinkExtractor {
static final MediaType HAL_MEDIA_TYPE = new MediaType("application", "hal+json");
static final MediaType VND_HAL_MEDIA_TYPE = new MediaType("application", "vnd.hal+json");
static final MediaType HAL_FORMS_MEDIA_TYPE = new MediaType("application", "prs.hal-forms+json");
@Override
public Map<String, List<Link>> extractLinks(Map<String, Object> json) {

View File

@@ -18,6 +18,7 @@ package org.springframework.restdocs.hypermedia;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
@@ -28,6 +29,7 @@ import org.springframework.http.MediaType;
import org.springframework.restdocs.operation.OperationResponse;
import org.springframework.restdocs.operation.OperationResponseFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -41,6 +43,8 @@ class ContentTypeLinkExtractorTests {
private final OperationResponseFactory responseFactory = new OperationResponseFactory();
private final String halBody = "{ \"_links\" : { \"someRel\" : { \"href\" : \"someHref\" }} }";
@Test
void extractionFailsWithNullContentType() {
assertThatIllegalStateException().isThrownBy(() -> new ContentTypeLinkExtractor()
@@ -71,4 +75,22 @@ class ContentTypeLinkExtractorTests {
verify(extractor).extractLinks(response);
}
@Test
public void extractsLinksFromVndHalMediaType() throws IOException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.parseMediaType("application/vnd.hal+json"));
OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, this.halBody.getBytes());
Map<String, List<Link>> links = new ContentTypeLinkExtractor().extractLinks(response);
assertThat(links).containsKey("someRel");
}
@Test
public void extractsLinksFromHalFormsMediaType() throws IOException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.parseMediaType("application/prs.hal-forms+json"));
OperationResponse response = this.responseFactory.create(HttpStatus.OK, httpHeaders, this.halBody.getBytes());
Map<String, List<Link>> links = new ContentTypeLinkExtractor().extractLinks(response);
assertThat(links).containsKey("someRel");
}
}