spring-projects / spring-data-rest

Simplifies building hypermedia-driven REST web services on top of Spring Data repositories
https://spring.io/projects/spring-data-rest
Apache License 2.0
918 stars 563 forks source link

When an association resource is not exported (e.g. @RestResource(exported = false)), custom links (RepresentationModelProcessor) are not handled properly. [DATAREST-1488] #1847

Open spring-projects-issues opened 4 years ago

spring-projects-issues commented 4 years ago

jvanheesch opened DATAREST-1488 and commented

This issue was discussed in https://gitter.im/spring-projects/spring-data.

Please see reference url for quick reproducer.

I defined two aggregate roots: Car and Engine. Car has an engine. I then chose not to export this association resource (@RestResource(exported = false)). This modifies car's representation (in both collection resource and item resource): it removes the engine link and inlines engine as a regular property. However, in the resulting json, engine has a _links property, which is invalid HAL. Example: GET to http://localhost:8080/cars/2 returns

{
  "name": "carName",
  "engine": {
    "name": "engineName",
    "_links": {
      "engineLink": {
        "href": "http://www.google.com"
      }
    }
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/cars/2"
    },
    "car": {
      "href": "http://localhost:8080/cars/2"
    }
  }
}

EDIT: I just realised the same holds for _embedded: both _embedded and _links of an association resource are rendered when the association resource is not exported. I created a new branch EnginePart. I created an Entity EnginePart, which has an excerptProjection that renders its name. Engine now has a @OneToOne EnginePart.

GET http://localhost:8080/cars/3 now returns the following json:

{
  "name": "carName",
  "engine": {
    "name": "engineName",
    "_embedded": {
      "enginePart": {
        "name": "enginePartName",
        "_links": {
          "self": {
            "href": "http://localhost:8080/engineParts/1{?projection}",
            "templated": true
          }
        }
      }
    },
    "_links": {
      "engineLink": {
        "href": "http://www.google.com"
      },
      "enginePart": {
        "href": "http://localhost:8080/engineParts/1{?projection}",
        "templated": true
      }
    }
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/cars/3"
    },
    "car": {
      "href": "http://localhost:8080/cars/3"
    }
  }
}

I believe both _links and _embedded should only be present as "top level properties" in valid HAL.

TL;DR: when inlining an association resource, only its content should be included, not its _links and _embedded.


Reference URL: https://github.com/jvanheesch/spring-data-rest-inlined-resource-custom-links

spring-projects-issues commented 4 years ago

Oliver Drotbohm commented

…engine has a _links property, which is invalid HAL.

Why do you think that's invalid HAL? _links is a reserved property for what they call Resource objects. There's nothing in the spec that says that the value for any JSON property could not be a Resource object. There's also nothing that states that _links can only be used in certain places. I.e. what you see is expected behavior: if a particular is not exported is needs to be inlined entirely, including the links that make up its relationships. How else would you represent them?

Maybe it makes sense to discuss what you're trying to achieve? You might wanna have a look at projections as they're basically generating previews within the representations of the owning objects but keep the related resources around?

spring-projects-issues commented 4 years ago

jvanheesch commented

Maybe it makes sense to discuss what you're trying to achieve?

During the last couple of months at work, I've worked extensively with SDR. I'd love to give an in-depth SDR presentation to our dev team, however, in order to accomplish this, I'm looking for the answers to some questions (such as this issue).

Why do you think that's invalid HAL? There's nothing in the spec that says that the value for any JSON property could not be a Resource object.

I agree the spec does not say anything about this! However, I could not find an example in the spec (nor anywhere else) in which \_links was not the "top-level" \_links property, nor underneath \_embedded, so I wondered whether or not this was valid HAL. I turned to gitter and I think Greg believed this behavior to be incorrect, hence the issue.

There's nothing in the spec that says that the value for any JSON property could not be a Resource object. ... if a particular is not exported is needs to be inlined entirely, including the links that make up its relationships. How else would you represent them?

I do have a problem with the current representation. If you consider the value of the engine property to be a Resource object, according to the HAL spec, it should contain a self link. This self link would presumably link to the unexported association resource /cars/2/engine and return 405 for all methods (similar to self links in a collection resource when the corresponding item resources are not exported.). If you do not consider this value to be a Resource object, it should probably contain no links at all. What do you think about this?