restdocs SnippetException due to HAL "_links" elements from spring-data-rest
Asked Answered
U

2

10

My app is using spring-data-rest and spring-restdocs. My setup is really standard; copied from the docs almost entirely, but I've included the samples below in case I'm missing something. When my mvc test runs, it fails with:

org.springframework.restdocs.snippet.SnippetException: The following parts of the payload were not documented:
{
  "_links" : {
    "self" : {
      "href" : "https://my-api/item/10"
    },
    "item" : {
      "href" : "https://my-api/item/10"
    }
  }
}

This is my test code:

@Rule
public JUnitRestDocumentation restDocs = new JUnitRestDocumentation("target/generated-snippets");
// ...
mockMvc = webAppContextSetup(wac) //WebApplicationContext
        .apply(documentationConfiguration(restDocs)
                       .uris()
                       .withHost("my-api")
                       .withPort(443)
                       .withScheme("https"))
        .build();
// ....
mockMvc.perform(get("/items/{id}", "10"))
               .andDo(documentation)

Here's the stack:

at org.springframework.restdocs.payload.AbstractFieldsSnippet.validateFieldDocumentation(AbstractFieldsSnippet.java:176)
at org.springframework.restdocs.payload.AbstractFieldsSnippet.createModel(AbstractFieldsSnippet.java:100)
at org.springframework.restdocs.snippet.TemplatedSnippet.document(TemplatedSnippet.java:64)
at org.springframework.restdocs.generate.RestDocumentationGenerator.handle(RestDocumentationGenerator.java:196)
at org.springframework.restdocs.mockmvc.RestDocumentationResultHandler.handle(RestDocumentationResultHandler.java:55)
at org.springframework.test.web.servlet.MockMvc$1.andDo(MockMvc.java:177)
at com.example.my.api.domain.MyRepositoryRestTest.findOne(MyRepositoryRestTest.java:36)

How do I get spring-restdocs and spring-data-rest to play nice?


EDIT(S):

My documentation instance is defined as follows:

ResultHandler documentation = document("items/findOne",
                                       preprocessRequest(prettyPrint(), maskLinks()),
                                       preprocessResponse(prettyPrint()),
                                       responseFields(
                                            fieldWithPath("name").description("Item name.")
                                            // Bunch more
                                       ));

As @meistermeier indicated, (and following the restdocs docs for ignoring links, I can add

links(linkWithRel("self").ignored(),
      linkWithRel("_self").ignored().optional()) // docs suggest this. /shrug

But that still leaves me with:

SnippetException: Links with the following relations were not documented: [item]

Seems like the _links are always going to have that self-reference back to the same entity, right? How do I cleanly handle this without ignoring an entity-specific link for every test, like:

links(linkWithRel("item").ignored())

Even if I do add the above line (so that all fields self _self curies and item are all ignored() and/or optional()), the result of the test returns to the original error at the top of this question.

Unpracticed answered 12/12, 2016 at 18:51 Comment(3)
How is your documentation object defined? I think you're missing the links(...) definition within your document() call.Antefix
@meistermeier, I'm trying that (see edits above), but I'm not sure it's a complete solution. What do you think I'm missing now?Unpracticed
I'm trying to generically ignore the link to the entity itself ("item") - but for all tests, and all entities.Unpracticed
A
8

Seems like the _links are always going to have that self-reference back to the same entity, right?

Yes, that's right.

I may have your solution for ignoring some links in a small github sample. Especially the part:

mockMvc.perform(RestDocumentationRequestBuilders.get(beerLocation)).andExpect(status().isOk())
       .andDo(document("beer-get", links(
                linkWithRel("self").ignored(),
                linkWithRel("beerapi:beer").description("The <<beers, Beer resource>> itself"),
                linkWithRel("curies").ignored()
               ),
               responseFields(
                  fieldWithPath("name").description("The name of the tasty fresh liquid"),
                  fieldWithPath("_links").description("<<beer-links,Links>> to other resources")
               )
            ));

where I completely ignore all "generated" fields and only create a documentation entry for the domain. Your item link would be my beerapi:beer.

I really don't know what is best practice here, but I would always document as much as possible since you can use asciidoctor links (like <<beer-links,Links>>) wherever possible to reference other parts with more documentation.

Antefix answered 13/12, 2016 at 8:21 Comment(1)
Great! That example clears up a few things I was doing wrong and the docs are working. Thanks for the help :). Also, I now realize that my real problem is the "item" link in the "_links". I get the "self" link, but I now need to figure out how to keep the "item" link from being returned in the response at all.Unpracticed
W
0

canonical way seems to be using things from restdocs documentation. Approach is in line with approach from https://stackoverflow.com/users/2650436/meistermeier solution.

Documentation can be found on https://docs.spring.io/spring-restdocs/docs/current/reference/html5/#documenting-your-api-hypermedia-link-formats

Sample code:

.consumeWith(document("items",
       links(
               halLinks(), // <- this shorten things a bit
               linkWithRel("self").ignored(),
               linkWithRel("profile").ignored()
       ),
Wei answered 30/5, 2019 at 14:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.