What is the right way exposing enums as REST API with Spring HATEOAS (via Spring Data REST)
Asked Answered
C

1

8

I'm trying to use HATEOAS with Spring HATEOAS and need to expose enums as REST API with Spring HATEOAS.

I tried three ways as follows:

@RestController
@RequestMapping(path = "/fruits")
public class FruitResourceController {

    @RequestMapping(method = RequestMethod.GET)
    public Fruit[] fruits() {
        return Fruit.values();
    }

    // NOTE: The `produces` attribute is only for browsers.
    @RequestMapping(path = "/with-resource", method = RequestMethod.GET,
            produces = MediaTypes.HAL_JSON_VALUE)
    public Resource<Fruit[]> fruitsWithResource() {
        Resource<Fruit[]> resource = new Resource<Fruit[]>(Fruit.values());
        Link selfLink = linkTo(methodOn(FruitResourceController.class).fruitsWithResource())
                .withSelfRel();
        resource.add(selfLink);
        return resource;
    }

    // NOTE: The `produces` attribute is only for browsers.
    @RequestMapping(path = "/with-resources", method = RequestMethod.GET,
            produces = MediaTypes.HAL_JSON_VALUE)
    public Resources<Fruit> fruitsWithResources() {
        Resources<Fruit> resources = new Resources<Fruit>(Arrays.asList(Fruit.values()));
        Link selfLink = linkTo(methodOn(FruitResourceController.class).fruitsWithResources())
                .withSelfRel();
        resources.add(selfLink);
        return resources;
    }

}

But I have no idea which is the right way for HATEOAS. Any advice or reference will be appreciated.

For reference, I have the following Spring Data REST configuration:

@Configuration
public class SpringDataRestConfig {

    @Bean
    public ResourceProcessor<RepositoryLinksResource> repositoryLinksResourceProcessor() {
        return new ResourceProcessor<RepositoryLinksResource>() {
            @Override
            public RepositoryLinksResource process(RepositoryLinksResource resource) {
                Link fruitsLink = linkTo(methodOn(FruitResourceController.class).fruitsWithResources())
                        .withRel("fruits");
                resource.add(fruitsLink);
                return resource;
            }
        };
    }

}

See the following for a sample project:

https://github.com/izeye/spring-boot-throwaway-branches/blob/data-jpa-and-rest/src/main/java/com/izeye/throwaway/SpringDataRestConfig.java https://github.com/izeye/spring-boot-throwaway-branches/blob/data-jpa-and-rest/src/main/java/com/izeye/throwaway/FruitResourceController.java

--- UPDATED at 2016.01.04

Using ALPS (/profile) looks nice to get enum listing but I'm not sure this is a right approach.

Curarize answered 28/12, 2015 at 8:8 Comment(5)
"The produces attribute is only for browsers" - Why do you think it is?Kelbee
@zeroflagL Sorry for the confusing comment. It means it exists only for browsers to force to render as JSON, not meaning it affects only for browsers.Curarize
What exactly do you mean by expsing enums? Do you want to expose a static, read-only list of string values? Then why don't you simply do exactly that? Simply return the enum itself Fruit.values() in your REST controller. Spring will automatically the HTTP response to an array of Strings.Quern
And about HATEOAS: This is just a format of the representation (with links, href's and resources) What links would you like to add to your enum values? You can do that? But what for? It's just a static list of strings as far as I understand you. There is no POST to that rest endpoint or is there?Quern
why so many upvotes?!Insufficiency
T
0

HATEOAS profile seems like right place. But when you have hammer in your hand everything looks like nail.

Data validation is not part of the HATEAOS spec and you should not try to "paint walls with hammer".

You are far better off with solution that is made for that; Forms. I should look into JSON-Schema and it's extension Json-Forms.

https://jsonforms.io/

That is standardized solution to define the data entry via UI. Just code endpoint paraphrasing HATEAOS naming convention

/form/${entityname}

as extension/alternative to /profile and you can fairly easy modify Spring to include this into the HATEOAS "_links" for the root & entity. But this is not HATEOAS! (or Sparta!)

That returns hard coded JSON form schema for the entity of same name. You could also do it semi-automatic with cross join so it only returns matching keys/fields of the current entity. So if entity changes it does not completely blow up your UI. You could then make fully automatic with a LOT of code that read Entity class with reflection and generates the JSON Forms schema definition for your Entity automatically .

Like this Java-ish pseudocode

entity.getFields().forEach(
  if (isEnumeration(field)) {
     sb.append( 
         field.getName() + ": {"
         + "type: string,"
         + "title: " + localize(field.getName() + ","
         + "enum: " Arrays.toString(field.values()) + ","
         + "required: " isNullable(field) 
         + "    },"
     );
    continue; 
  );
  if (isString(field)) {
    ...

Constraints of your own data schema it is doable. If your code & ER is clean and consistent. You can also add much more UI validation information like regexp validation for. And there are ready made UI libraries and component for this standard for React, Angular and Vue. Materials UI components are based on this, well at least their Forms - component.

Tracheostomy answered 15/5, 2021 at 17:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.