question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Create a dedicated builder for HAL representations

See original GitHub issue

@drdamour I’m experimenting on a fluent API to build RepresentationModel objects. What do you think of something like this for building an embedded structure like you depicted up above?

@RestController
static class ProductController {

	LinkRelation favoriteProducts = LinkRelation.of("favorite products");
	LinkRelation purchasedProducts = LinkRelation.of("purchased products");

	@GetMapping("/products")
	public RepresentationModel<?> all() {

		EmbeddedModelBuilder builder = ModelBuilder //
				.embed() //
				.rootLink(linkTo(methodOn(ProductController.class).all()).withSelfRel());

		PRODUCTS.keySet().stream() //
				.map(id -> new EntityModel<>(PRODUCTS.get(id), new Link("http://localhost/products/{id}").expand(id))) //
				.forEach(productEntityModel -> {

					if (productEntityModel.getContent().isFavorite()) {

						builder //
								.embed(favoriteProducts) //
								.entityModel(productEntityModel) //
								.rootLink(productEntityModel.getRequiredLink(SELF).withRel(favoriteProducts));
					}

					if (productEntityModel.getContent().isPurchased()) {

						builder //
								.embed(purchasedProducts) //
								.entityModel(productEntityModel) //
								.rootLink(productEntityModel.getRequiredLink(SELF).withRel(purchasedProducts));
					}
				});

		return builder.build();
	}
}

It yields:

{
  "_embedded" : {
    "favorite products" : [ {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/777"
        }
      }
    }, {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/998"
        }
      }
    } ],
    "purchased products" : [ {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/111"
        }
      }
    }, {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/222"
        }
      }
    }, {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/333"
        }
      }
    }, {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/444"
        }
      }
    }, {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/555"
        }
      }
    }, {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/666"
        }
      }
    }, {
      "someProductProperty" : "someValue",
      "_links" : {
        "self" : {
          "href" : "http://localhost/products/998"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost/products"
    },
    "purchased products" : [ {
      "href" : "http://localhost/products/111"
    }, {
      "href" : "http://localhost/products/222"
    }, {
      "href" : "http://localhost/products/333"
    }, {
      "href" : "http://localhost/products/444"
    }, {
      "href" : "http://localhost/products/555"
    }, {
      "href" : "http://localhost/products/666"
    }, {
      "href" : "http://localhost/products/998"
    } ],
    "favorite products" : [ {
      "href" : "http://localhost/products/777"
    }, {
      "href" : "http://localhost/products/998"
    } ]
  }
}

A plain old collection where the embedded link relation is a based on the domain type:

@GetMapping("/authors")
RepresentationModel<?> collection() {

	return ModelBuilder //
			.collection() //

			.entity(new Author("Greg L. Turnquist", null, null)) //
			.link(linkTo(methodOn(EmbeddedController.class).authorDetails(1)).withSelfRel())
			.link(linkTo(methodOn(EmbeddedController.class).collection()).withRel("authors")) //

			.entity(new Author("Craig Walls", null, null)) //
			.link(linkTo(methodOn(EmbeddedController.class).authorDetails(2)).withSelfRel())
			.link(linkTo(methodOn(EmbeddedController.class).collection()).withRel("authors")) //

			.entity(new Author("Oliver Drotbhom", null, null)) //
			.link(linkTo(methodOn(EmbeddedController.class).authorDetails(2)).withSelfRel())
			.link(linkTo(methodOn(EmbeddedController.class).collection()).withRel("authors")) //

			.rootLink(linkTo(methodOn(EmbeddedController.class).collection()).withSelfRel()) //

			.build();
}

produces

{
  "_embedded" : {
    "authors" : [ {
      "name" : "Greg L. Turnquist",
      "_links" : {
        "self" : {
          "href" : "http://localhost/author/1"
        },
        "authors" : {
          "href" : "http://localhost/authors"
        }
      }
    }, {
      "name" : "Craig Walls",
      "_links" : {
        "self" : {
          "href" : "http://localhost/author/2"
        },
        "authors" : {
          "href" : "http://localhost/authors"
        }
      }
    }, {
      "name" : "Oliver Drotbhom",
      "_links" : {
        "self" : {
          "href" : "http://localhost/author/2"
        },
        "authors" : {
          "href" : "http://localhost/authors"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost/authors"
    }
  }
}

And a single item representation:

@GetMapping("/other-author")
RepresentationModel<?> singleItem() {

	return ModelBuilder //
			.entity(new Author("Alan Watts", "January 6, 1915", "November 16, 1973")) //
			.link(new Link("/people/alan-watts")) //
			.build();
}

produces

{
  "name" : "Alan Watts",
  "born" : "January 6, 1915",
  "died" : "November 16, 1973",
  "_links" : {
    "self" : {
      "href" : "/people/alan-watts"
    }
  }
}

Related issue: #175

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:21 (13 by maintainers)

github_iconTop GitHub Comments

1reaction
gregturncommented, Mar 13, 2019

Agree on user over developer.

So I seek maximum readability six months later.

1reaction
odrotbohmcommented, Mar 13, 2019

Not necessarily. As I explained, there could be other more general methods on it but if you’re starting with “Here’s a link with a given relation…”, it’s quite natural to be immediately able to state “Oh, btw. here’s the preview for the resource that just mentioned link points to.”.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Spring HATEOAS - Reference Documentation
As of Spring HATEOAS 1.1, we ship a dedicated HalModelBuilder that allows to create RepresentationModel instances through a HAL-idiomatic API.
Read more >
Haley: A HAL Resource Builder in Java
Manually implementing the generation of HAL resources for REST/HATEOAS-based web services in Java can be tedious.
Read more >
Spring RESTBucks in 2021 - Oliver Drotbohm
We start by introducing a dedicated data transfer object (DTO) to ... the affordance manually using Spring HATEOAS' AffordanceBuilder API.
Read more >
Writing REST Services with RESTEasy Reactive - Quarkus
Designates a particular representation (also called a media type), ... You can also set the quarkus.resteasy-reactive.path build time ...
Read more >
Computational tools for drawing, building and displaying ...
and representation of glycan structures in symbolic form by computational tools like GlycanBuilder [10]. Since then, several.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found