Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Aug 03 09:35
    odrotbohm labeled #1565
  • Aug 03 09:35
    odrotbohm labeled #1565
  • Aug 03 09:35
    odrotbohm assigned #1565
  • Aug 03 09:35
    odrotbohm milestoned #1565
  • Aug 03 09:19
    pax95 closed #1565
  • Aug 03 09:19
    pax95 commented #1565
  • Jul 30 12:26
    odrotbohm commented #1592
  • Jul 30 09:34
    talentedasian commented #1592
  • Jul 30 09:15
    pax95 commented #1565
  • Jul 30 06:38
    odrotbohm commented #1592
  • Jul 30 06:36
    odrotbohm labeled #1592
  • Jul 30 06:36
    odrotbohm labeled #1592
  • Jul 30 06:36
    odrotbohm assigned #1592
  • Jul 30 06:36
    odrotbohm commented #1592
  • Jul 30 04:05
    talentedasian opened #1593
  • Jul 29 16:56
    filipeamaral commented #1253
  • Jul 29 16:01
    filipeamaral opened #1592
  • Jul 29 15:59
    odrotbohm commented #1591
  • Jul 29 15:59
    odrotbohm commented #1253
  • Jul 29 15:58
    odrotbohm closed #1591
Ingo Griebsch
@ingogriebsch
I’m pleased to announce the release of Spring HATEOAS Siren 1.0.0-M3! The library is accessible through Maven Central.
Source: https://github.com/ingogriebsch/spring-hateoas-siren
Documentation: https://ingogriebsch.github.io/spring-hateoas-siren
You can expect the release of the first version soon! :)
Ingo Griebsch
@ingogriebsch
I’m really happy to announce the first final release of Spring HATEOAS Siren!
Check it out, version 1.0.0 is accessible through Maven Central!
Source: https://github.com/ingogriebsch/spring-hateoas-siren
Documentation: https://ingogriebsch.github.io/spring-hateoas-siren
Examples: https://github.com/ingogriebsch/spring-hateoas-siren-samples
Every feedback is very welcome! :)
ctwoolsey
@ctwoolsey

I'm struggling to read data from a webClient query.
I've configured SpringConfig:

@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
class SpringConfig {
@Bean
WebClientCustomizer webClientCustomizer(HypermediaWebClientConfigurer configurer) {
return {webClientBuilder ->
configurer.registerHypermediaTypes(webClientBuilder)
}
}
}

Then in my repository I have:

@Repository ('PersonRepository')
class PersonRepository {
private final WebClient webClient

PersonRepository(final WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.build()
}

List<person> getPeople() {
CollectionModel<person> personCollectionModel = this.webClient.get().uri('localhost:8080/api/people')
.retrieve()
.bodyToMono(new TypeReferences.CollectionModelType<person>())
.block()

return personCollectionModel.content
}

The problem is that "personCollectionModel" is empty. It is like the HAL format is unable to be read into the structure.

This is the data that is returned from localhost:8080/api/people

{
"_embedded": {
"people": [
{
"id": 2,
"firstName": "Mark",
"lastName": "Hamil"
}
]
}
}

Any light you can shine on why this isn't returning data as expected would be appreciated. Thank you.

ctwoolsey
@ctwoolsey
@gregturn In your article: https://spring.io/blog/2020/04/22/spring-hateoas-brings-you-new-ways-to-configure-clients, do these new ways to configure allow for reading HAL json with "_embedded" tags?
Greg L. Turnquist
@gregturn
They should.
        client.get().uri("http://localhost/employees").accept(HAL_JSON) //
                .exchange() //
                .expectStatus().isOk() //
                .expectBody(new TypeReferences.CollectionModelType<EntityModel<Employee>>() {}) // <3>
                .consumeWith(result -> {
                    CollectionModel<EntityModel<Employee>> model = result.getResponseBody(); // <4>

                    // Assert against the hypermedia model.
                    assertThat(model.getRequiredLink(IanaLinkRelations.SELF)).isEqualTo(Link.of("http://localhost/employees"));
                    assertThat(model.getContent()).hasSize(2);
                });
Greg L. Turnquist
@gregturn
The inner type needs to be EntityModel<Employee> and you also need the {} as well.
ctwoolsey
@ctwoolsey

@gregturn Thank you for the pointer. I am receiving a status of 406 Not accepted when I use accept(HAL_JSON). Here is the modified HAL output:

    "_embedded": {
        "people": [
            {
                "id": 2,
                "firstName": "Mark",
                "lastName": "Hamil",
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/people/2"
                    }
                }
            }
        ]
    },
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/people"
        }
    }
}

And here is my code to receive it:

Mono<CollectionModel<EntityModel<Person>>> personCollectionModel = this.webClient.get().uri('localhost:8080/api/people')
                  .accept(MediaTypes.HAL_JSON)
                  .exchange()
                  .flatMap { response ->
                       if (response.statusCode().isError()) {
                         System.out.println('Error---------------------------------')
                       } else {
                            response.bodyToMono(new ParameterizedTypeReference<CollectionModel<EntityModel<Person>>>() {})
                       }
                  }

I'm not sure why the HAL format is not being recognized as valid HAL.

ctwoolsey
@ctwoolsey
Thank you for your help. I figured it out, this is now working, I needed to set produces = "application/hal+json"
Greg L. Turnquist
@gregturn
If the Content-Type header is something else, you can always drop the Accept request header. If you are getting HAL, you should be able to consume it. The rest of simply HTTP negotation.
It's possible, if you're using Boot, that you're getting served application/json. Don't remember it NOT serving up application/hal+json when requested, though.
Ravish Rathod
@ravishrathod
Hello, I recently upgrade my app from a very old version of spring-boot (and hateoas) to the latest boot and hateoas v1.1.2.RELEASE. After this I am seeing a spike in memory usage. Heap dump points to WebHandler.AFFORDANCES_CACHE consuming most of memory. Looking at WebHandler it seems that the cache is never cleared and is unbounded... has anyone faced this issue ? Thanks
Greg L. Turnquist
@gregturn
@ravishrathod The last fix that involved that cache was in 1.0. If you'll open a new ticket and provide reproducible details, we can certainly investigate.
Ravish Rathod
@ravishrathod
Thanks @gregturn , I will add a link to git project
Here is the link to a simple project which exposes a /users/{id}. If we call this api with enough distinct id values, it causes AFFORDANCES_CACHE to fill up. https://github.com/ravishrathod/spring-hateoas-memory
Ravish Rathod
@ravishrathod
ctruzzi
@ctruzzi
We have pre-existing contracts, is it possible to change the mixin of links from _links to just links?
Greg L. Turnquist
@gregturn

@ctruzzi Not without violating the HAL spec. In essence, you can write your own media type => https://docs.spring.io/spring-hateoas/docs/1.2.0/reference/html/#mediatypes.custom

You can also look at the community-provided media types as templates:

Ingo Griebsch
@ingogriebsch
Hello @gregturn it looks like the Spring HATEOAS project page is not up to date. The page points to version 1.1.2 and 1.2.0. It does not point to version 1.1.3 and 1.2.2. I found corresponding milestones on GitHub and artifacts on Maven Central for these versions. :)
Greg L. Turnquist
@gregturn
I believe Ollie recently put out a new release. We'll get it updated shortly.
Ingo Griebsch
@ingogriebsch
:thumbsup:
Okay, I only wanted to make sure I can use them to update my project to these versions.
Greg L. Turnquist
@gregturn
@ingogriebsch I updated all the versions.
Ingo Griebsch
@ingogriebsch
:thumbsup:
Ingo Griebsch
@ingogriebsch
Hello @gregturn, I would like to ask if there is somewhere explained how the Spring HATEOAS library decides which RepresentationModelProcessor is executed on which specific RepresentationModel? I just struggle a little bit with building complex RepresentationModel's (with a builder) and therefore not having a concrete type I could use to define a processor for.
1 reply
koehnj
@koehnj
Dear Spring HATEOAS team, i just noticed that you've recently added support for the "type" attribute for HAL-FORMS properties. Are there any plans to support the remaining additional attributes as well? Placeholder, minLength and maxLength are especially useful.
2 replies
Marcel Widmer
@marzelwidmer

Hello what is the best practice wo handle in the links with combination of DDD with validation on it... If maybe make a abstraction again on REST entpoint or not... or if there a better way to create the _link

eg. when I use a valid object from a customer. afford<CustomerResource> { methodOn(CustomerResource::class.java).add(customer = customer) }

val customer = Customer(id= null, firstName = FirstName("firstName"), lastName = LastName("lastName"))

    @GetMapping(value = ["/customers/{id}"], produces = [MediaTypes.HAL_JSON_VALUE])
    fun one(@PathVariable id: String): EntityModel<Customer> {
        // TODO: 09.02.21  Validation problems
        val customer = Customer(id= Any().toString(), firstName = FirstName(Any().toString()), lastName = LastName(Any().toString()))
        val create: Affordance = afford<CustomerResource> { methodOn(CustomerResource::class.java).add(customer = customer) }

or should I create the Domain objects eg on the service level... sound not nice for me... because I pass already the controller
maybe I miss understood something....

@odrotbohm will be nice update on yout spring-restbucks ;) how handle everything together..

Greg L. Turnquist
@gregturn

@marzelwidmer I can understand having a DTO that targets the serialized JSON, and having it carry validation rules embedded in either the constructors or the setters. This is the format used to "present" your API. After validating things in the controller, I would then transform into the internal type used at the service level. And I would do that transformation in the controller. The service doesn't have to be aware of this API-level of things.

If the validation rules you have in mind apply at the service level, put your validation rules there. Otherwise, keep them in the DTO. Then, when you transform stuff in the controller, you can have suitable error handling to report to the user of the API what went wrong.

Oliver Drotbohm
@odrotbohm
I don't quite follow. I.e. I don't actually get what the goal is.
Marcel Widmer
@marzelwidmer

@gregturn @odrotbohm Thanks for the response, I try to explain again where I have a conceptional ("knopf-im-kopf") problem in my head. I try to follow (resp. impl) https://speakerdeck.com/olivergierke/domain-driven-design-and-rest-1?slide=15

Also use this classes to validate on the API side. When I have to use this classes then also to create my _links I have to pass also valid (from my business valid objects) values (what is normal because of validation)

If I use the same (what 1st come in my head who should be correct) objects.

I was asking my self don't if it better to don't use the same object on API side. because the java.lang.IllegalArgumentException are throwing on runtime and If the developer not have the Domain validation in mine when he create the HATEOS resp. _links with the methodOn(...

@gregturn when I get U right is it also valid to create the API validation from the service layer (after transformation)

data class Customer(val id: String? = UUID.randomUUID().toString(), val firstName: FirstName, val lastName: LastName)
// Domain Primitive
data class FirstName(val value: String) {
    init {
        require(value.isNotEmpty()) { "value must be non-empty" }
        require(value.trim().length >= 2) { "wrong value length" }
        require(value.trim().length <= 20) { "wrong value length" }
    }
    companion object {
        @JvmStatic
        @JsonCreator
        fun create(value: String) = FirstName(value)
    }
    @JsonValue
    override fun toString() = value
}
.....

@RestController
class CustomerResource(private val service: CustomerService) {
    val CREATE_REL = "create"
    val ALL_CUSTOMERS_REL = "customers"

    @GetMapping(value = ["/customers/{id}"], produces = [MediaTypes.HAL_JSON_VALUE])
    fun oneCustomer(@PathVariable id: String): EntityModel<Customer> {
        val customer = Customer(
            id = null, firstName = FirstName("Here-I-Have-To-Add-Some-Valid-Domain-Primitive-Value"),
            lastName = LastName("TO-Avoid-IllegalArgumentException")
        )
        // java.lang.IllegalArgumentException: wrong value length
        //    at ch.keepcalm.demo.customer.secureByDesignModel.FirstName.<init>(Customer.kt:13)

        val selfLink = linkTo(methodOn(CustomerResource::class.java).oneCustomer(id)).withSelfRel()
        val createCustomerLink = linkTo(methodOn(CustomerResource::class.java).addCustomer(customer = customer)).withRel(CREATE_REL)
        val allCustomersLink = linkTo(methodOn(CustomerResource::class.java).allCustomers()).withRel(ALL_CUSTOMERS_REL)

        return EntityModel.of(service.findCustomerById(id))
            .add(selfLink, createCustomerLink, allCustomersLink)
    }

    @GetMapping(value = ["/customers"], produces = [MediaTypes.HAL_JSON_VALUE])
    fun allCustomers(): CollectionModel<EntityModel<Customer>> {
        val selfLink: Link = linkTo(methodOn(CustomerResource::class.java).allCustomers()).withSelfRel()
        return CollectionModel.of(
            service.findCustomers().map {
                EntityModel.of(it, linkTo(methodOn(CustomerResource::class.java).oneCustomer(it.id.toString())).withSelfRel())
            }, selfLink
        )
    }
}
Oliver Drotbohm
@odrotbohm

There's a couple of things to discuss here: I think, when manually implementing controllers, you should use dedicated DTOs to wrap into representation models and map from and to them explicitly. Using the domain types, especially inbound, is problematic as the strong rule enforcement will cause the deserialization break at the first rule violation. Unfortunately Jackson currently does not support accumulating the errors to return them all at once reasonably. Outbound, you still face the challenge of having to adapt the entity boundaries to the representation boundaries. Aggregates are a good start but as soon as you get away from the standard "CRUD via HTTP" the symmetry between the representation and a persisted entity usually goes off anyway. I.e. there will be resources that are centered around a DTO that then is used to map to different aggregates and invokes state transitions to them or actually needs different information assembled into a single view on the outbound side.

Does that help?

Marcel Widmer
@marzelwidmer
Nice: Thanks... yes I think so.... in other words. like this.... https://github.com/odrotbohm/spring-restbucks/blob/b4eece09f95d3862877a7521a797d6f1a3553b5b/server/src/main/java/org/springsource/restbucks/order/web/CoreOrderResourceProcessor.java#L47 This Order in my setup without Rest-Data I have to wrap in a own representation who are exposed eg. to a client. in my case Customer name are for the client just a String.
Marcel Widmer
@marzelwidmer
I think this is a topic not about HATEOAS Thanks to all ;)
@gregturn this mean for the quality there are a lot of mappings around....
Jeroen Vandevelde
@jeroenvandevelde

Hi, i am trying to use the Links class for a client-side implementation with a value that comes from the link header.

They return the value linkHeaderValue = 'https://url.com?page=1; rel=first, https://url.com?page=2; rel=last'
But when i call Links.parse(linkHeaderValue), i get an empty Links object.
Links.parse("<https://url.com?page=1>; rel=\"first\", <https://url.com?page=1>; rel=\"last\"")

This is because the linkHeaderValue doesn't match the LINK_HEADER_PATTERN in the Links class.
If they would return 'https://url.com?page=1; rel="first", https://url.com?page=2; rel="last"' it would work.

The docs of the producer say that they implement this spec for the link header.
But i am not really certain if they produce data up to standard of the specs or Links class does.

So i have 2 questions:

  • Does anyone know what the correct spec is? 'https://url.com?page=1; rel="first", https://url.com?page=2; rel="last"' or 'https://url.com?page=1; rel=first, https://url.com?page=2; rel=last'
  • This is a kind of follow-up question but if they don't produce the link header up to the standard. Is it okay if i add a way to overwrite the REGEX eg. Links.parse(source, LINK_HEADER_PATTERN)
    When playing around with this i noticed i also would need to overwrite the LINK_HEADERPATTERN in Links to
    ```Pattern.compile("(\w+)=(\p{Lower}[\p{Lower}\p{Digit}.\-\s]*|(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~
    |!:,.;]*[-a-zA-Z0-9+&@#/%=~_|])")```
    So i don't really know if this is still a smart way to go but looking forward to your feedback.
9 replies
Jeroen Vandevelde
@jeroenvandevelde
Great, thanks for the quick fix!
Oliver Drotbohm
@odrotbohm
Heads up: versions 1.3 M2, 1.2.4 and 1.1.4 have just been released for inclusion into the corresponding Spring Boot releases tomorrow.
4 replies
Christian
@anno1985
Hi! Is there a clever way to use Spring HATEOAS with the HAL flavour together with OpenAPI 3.x so that _link relations only have to be defined in one place, and are then propagated? Ideally, specify them in the OpenAPI spec and have the controller interfaces generated automatically, together with the DTO (which would then explicitly define the possible links). Otherwise, is there a way to have the OpenAPI spec generated from a project's code (the project using Spring HATEOAS)? The rationale being I'd like to define the possible link relations in one place, and then have them represented in my spec (OpenAPI), the server code (Spring) and the client code (Angular)...
4 replies
Christian
@anno1985
I've got another question: it seems that when creating the link relations and embedding resources, we end up tying together a lot of controllers and business objects. It looks a bit like the result are omniscient classes. Is this what usually happens with HAL in Spring, or is that a smell?
2 replies
KnightK0der
@mrabusalah

ERROR!
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.hateoas.hal.Jackson2HalModule$HalLinkListSerializer': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.hateoas.hal.Jackson2HalModule$HalLinkListSerializer]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.hateoas.hal.Jackson2HalModule$HalLinkListSerializer.<init>()

any help ?

1 reply
Deniz Dalkilic
@dnzdlklc
Hello

I'm trying to store an object that extends RepresentationModel in Redis cache, upon retrieval I get a serialisation error:

Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'org.springframework.hateoas.Links' as a subtype of java.util.List<org.springframework.hateoas.Link>: Not a subtype at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 507] (through reference chain: ...

Can anyone help what would be the best way to tackle this?
Greg L. Turnquist
@gregturn
@mrabusalah More details, like version of Spring Boot/HATEOAS, and possibly some code excerpts.
Mark Pruden
@kiwisincebirth

Hi I am having Compile Issue using HalModelBuilder in Kotlin Code. I am on all the latest versions. But it comes down to a single method build() on HalModelBuilder

I have created as simple a test class. The compile error is Not enough information to infer type variable T - a typing issue - Here is the example

it seems like this should be simple to solve. If you need me to I can raise an issue on the project. Sorry if this has been asked before.

import org.springframework.hateoas.mediatype.hal.HalModelBuilder

// My domain class which is does NOT extend RepresentationModel
data class MyDomainDobject(
    val id: Int
)

fun main () {

    // the domain object I want to convert
    val myDomainDobject = MyDomainDobject(123)

    // create a hal model builder, for the domain object
    val builder = HalModelBuilder.halModelOf(myDomainDobject)

    // from here I would add links, embeds etc......

    // the following line causes a compile issue
    // - Not enough information to infer type variable T
    val model = builder.build()

    // the build method (is generically typed, whereas HalModelBuilder is not.
    // so cannot infer any type. Regardless the build method seems to assume it is returning
    // a underlying domain object which extends RepresentationModel<T>, which it isn't in my case!

    // my workaround comes in the form of a JAVA STATIC METHOD, which forces the type
    // conversion for me. It works for now but must compile Java code in Kotlin project

//    public static <T> EntityModel<T> build( HalModelBuilder builder ) {
//        return (EntityModel<T>) builder.build();
//    }
}
Greg L. Turnquist
@gregturn
@kiwisincebirth Please do open a ticket, with all the details you can provide.
1 reply
Francis
@francis-a

Hi everyone, I'm in the process of upgrading Spring Hateoas from 0.8x to the current version. It's quite a big application so I'm working through a large set of changes. There are a few places in our code where we are making use of the injectable _halObjectMapper. It seems like the current version is reusing a copy of an object mapper I define: https://github.com/spring-projects/spring-hateoas/blob/1.2.x/src/main/java/org/springframework/hateoas/config/HypermediaWebClientConfigurer.java#L49

My question is, is there any way to get a handle on the hal configured ObjectMapper via some kind of injectable class?

5 replies
Deniz Dalkilic
@dnzdlklc

Hello

Has anyone got any ideas on this?

Knut Schleßelmann
@kschlesselmann
Is it somehow possible to create plain links (no templates) with linkTo(methodOn())? I have some optional parameters and I don't want to include them at all in the URI if I call the method with null
3 replies
Ingo Griebsch
@ingogriebsch
Shameless cross posting from the Spring Data room (if not okay, please let me know). But because it is in some way related I would like to ask here as well...
"Hello, I would like to ask if there is any documentation or if there are any examples, guides or tutorials available to integrate a custom hypermedia-type into a Spring Data REST driven application?"
5 replies