Refactor Zone HATEOAS for Spring Boot

A replacement library for the built-in HATEOAS implementation of Spring Boot

License

License

Categories

Categories

hate Data Data Formats Hypermedia Types
GroupId

GroupId

zone.refactor.spring
ArtifactId

ArtifactId

hateoas
Last Version

Last Version

1.0.0-alpha2
Release Date

Release Date

Type

Type

jar
Description

Description

Refactor Zone HATEOAS for Spring Boot
A replacement library for the built-in HATEOAS implementation of Spring Boot
Source Code Management

Source Code Management

https://github.com/refactorzone/spring-boot-hateoas

Download hateoas

How to add to project

<!-- https://jarcasting.com/artifacts/zone.refactor.spring/hateoas/ -->
<dependency>
    <groupId>zone.refactor.spring</groupId>
    <artifactId>hateoas</artifactId>
    <version>1.0.0-alpha2</version>
</dependency>
// https://jarcasting.com/artifacts/zone.refactor.spring/hateoas/
implementation 'zone.refactor.spring:hateoas:1.0.0-alpha2'
// https://jarcasting.com/artifacts/zone.refactor.spring/hateoas/
implementation ("zone.refactor.spring:hateoas:1.0.0-alpha2")
'zone.refactor.spring:hateoas:jar:1.0.0-alpha2'
<dependency org="zone.refactor.spring" name="hateoas" rev="1.0.0-alpha2">
  <artifact name="hateoas" type="jar" />
</dependency>
@Grapes(
@Grab(group='zone.refactor.spring', module='hateoas', version='1.0.0-alpha2')
)
libraryDependencies += "zone.refactor.spring" % "hateoas" % "1.0.0-alpha2"
[zone.refactor.spring/hateoas "1.0.0-alpha2"]

Dependencies

compile (3)

Group / Artifact Type Version
org.springframework.boot : spring-boot-starter-web jar 2.1.6.RELEASE
com.fasterxml.jackson.core : jackson-annotations jar 2.9.0
io.swagger : swagger-annotations jar 1.5.24

Project Modules

There are no modules declared in this project.

A better HATEOAS implementation for Spring Boot

CircleCI Maven Central Code Quality JavaDoc GitHub last commit GitHub top language GitHub repo size GitHub issues GitHub pull requests GitHub

This library was born out of my frustration with the, admittedly very generic, HATEOAS/HAL implementation of Spring Boot.

This library completely replaces the HATEOAS library of Spring Boot and can be used as a replacement, or as a transition package as well.

Alpha software! This software is still in beta and the API may change! It is strongly recommended that you pin it to a specific version unless you are willing to update the changes as the code evolves!

Installation

This package can be installed from Maven Central:

<dependencies>
    <dependency>
        <groupId>zone.refactor.spring</groupId>
        <artifactId>hateoas</artifactId>
    </dependency>
</dependencies>

Usage

Base classes

This HATEOAS library gives you very simple tools to work with. The most basic types are Entity and ExceptionEntity. These are two base classes for all other objects that add the @type field to the result. This field will always contain the simple class name, or if you have declared the Swagger @ApiModel property with a name, that will be used. This helps a client decode the object to its proper representation and also helps with debugging. You can extend these classes to your liking to get the @type field.

Linked entities

Next up is the LinkedEntity class. This class adds the _links field, which should contain links such as a link to the current object, parent objects, etc. You could add a custom link class such as this:

import zone.refactor.spring.hateoas.entity.Entity;

@SuppressWarnings("WeakerAccess")
class BlogPostLinks extends Entity {
    @ApiModelProperty(required = true)
    @JsonProperty(required = true)
    public final Link self;
    
    @ApiModelProperty(required = true)
    @JsonProperty(required = true)
    public final Link up;
    
    @ApiModelProperty(required = true)
    @JsonProperty(required = true)
    public final Link author;

    public BlogPostLinks(
        PartialLink self,
        PartialLink up,
        PartialLink author
    ) {
        this.self = self.withSelfRel();
        this.up = up.withUpRel();
        this.author = author.withRel("author");
    }
}

As you can see the PartialLink class gives you the ability to define the rel field as needed right where it is used, reducing the visual clutter in other parts of your code.

Hint: You can inherit from SelfLinks or SelfUpLinks, or even use them directly to avoid code duplication at the cost of binding yourself to this library.

Now you can use this BlogPostLinks class in your BlogPost entity:

class BlogPost extends LinkedEntity<BlogPostLinks> {
    public BlogPost(String id, String authorId, LinkProvider linkProvider) {
        super(new BlogPostLinks(
            linkProvider.getResourceLink(BlogPost.class, id),
            linkProvider.getResourceListLink(BlogPost.class),
            linkProvider.getResourceLink(Author.class, authorId)
        ));
    }
}

Simple as that!

Hint: we provide a default implementation for LinkProvider, but you can of course also use your own if you wish.

Setting up the link provider

As mentioned above we provide a default link provider which reads annotations. Let's say you have the following controller:

@RestController
@RequestMapping("/posts")
class BlogController {
    @ListingEndpoint(BlogPost.class)
    @RequestMapping(
        method = RequestMethod.GET
    )
    public List<BlogPost> list() {
        //...
    }
    
    @EntityEndpoint(BlogPost.class)
    @RequestMapping(
        value = "/{id}",
        method = RequestMethod.GET
    )
    public BlogPost get(
        String id
    ) {
        //...
    }
}

In this case you can have the annotation link provider can read the @ListingEndpoint and @EntityEndpoint annotations and provide you with the link objects to this endpoint as follows:

import zone.refactor.spring.hateoas.provider.AnnotationLinkProvider;

class MyLinks {
    public Link getSingleLink(AnnotationLinkProvider linkProvider) {
        return linkProvider.getResourcecLink(BlogPost.class, "1");
    }
    
    public Link getListingLink(AnnotationLinkProvider linkProvider) {
        return linkProvider.getResourceListLink(BlogPost.class);
    }
}

If more parameters are required to construct the path, you can pass them as additional parameters to these methods.

Contracts provided

This library provides a number of interfaces as contracts:

Interface Description
Entity A simple entity with only the @type field attached to signal its type.
LinkedEntity An Entity that also has links attached in _links.
EmbeddingEntity An entity that has one or more embedded objects that will be returned in the _embedded field. Also, a LinkedEntity.
Link A link to a different resource.
LinkProvider An API to generate links for a certain resource.
PartialLink A link without its rel= attribute set, returned from a LinkProvider. Provides APIs to generate full links.
SelfLink An object that can be used inside a LinkedEntity and provides one link with the self rel.
SelfUpLink An object that can be used inside a LinkedEntity and provides link with the self and up rels.

Entities provided

Class Description
Entity An implementation of the Entity contract.
LinkedEntity An implementation of the LinkedEntity contract.
EmbeddingEntity An implementation of the EmbeddingEntity contract.
Link An implementation of the Link contract.
PartialLink An implementation of the PartialLink contract.
SelfLink An implementation of the SelfLink contract
SelfUpLink An implementation of the SelfUpLink contract.
ExceptionEntity An abstract exception class that hides the usual exception fields from the JSON output.
RuntimeExceptionEntity An abstract runtime exception class that hides the usual exception fields from the JSON output.

Controllers provided

For easier error handling in an API we also provide the ErrorController, which automatically takes any subclasses of ExceptionEntity and RuntimeExceptionEntity and turns them into valid HTTP responses.

Versions

Version
1.0.0-alpha2
1.0.0-alpha1
0.9.3
0.9.2
0.9.1
0.9