Skip to content

Backend read-only properties overridden for PATCH requests [DATAREST-1383] #1743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue May 23, 2019 · 7 comments
Assignees
Labels
type: bug A general bug

Comments

@spring-projects-issues
Copy link

XhstormR opened DATAREST-1383 and commented

I have an Person entity like this:

@Entity
@DynamicInsert
@DynamicUpdate
class Person(
        @Column(nullable = false, unique = true, updatable = false)
        val name: String,
        @Column(nullable = false)
        var country: String
) : BaseSequenceEntity() {

    @OneToMany(mappedBy = "person", cascade = [CascadeType.PERSIST])
    var phones: MutableList<Phone> = mutableListOf()
}

Its name attribute is specified as not updatable.

I use the curl command to create a Person entity:

$ curl -sX POST \
>   http://127.0.0.1:8080/api/persons \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "name": "Dave",
>     "country": "USA"
> }' \
> | jq .
{
  "name": "Dave",
  "country": "USA",
  "_links": {
    "self": {
      "href": "http://127.0.0.1:8080/api/persons/13"
    },
    "person": {
      "href": "http://127.0.0.1:8080/api/persons/13"
    },
    "phones": {
      "href": "http://127.0.0.1:8080/api/persons/13/phones"
    }
  }
}

When I modify the entity property using the http put method, the read-only property returns the correct value, the value of the name attribute is Dave:

$ curl -sX PUT \
>   http://127.0.0.1:8080/api/persons/13 \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "name": "123",
>     "country": "456"
> }' \
> | jq .
{
  "name": "Dave",
  "country": "456",
  "_links": {
    "self": {
      "href": "http://127.0.0.1:8080/api/persons/13"
    },
    "person": {
      "href": "http://127.0.0.1:8080/api/persons/13"
    },
    "phones": {
      "href": "http://127.0.0.1:8080/api/persons/13/phones"
    }
  }
}

But when I modify the entity property using the http patch method, the read-only property returns the modified value, the value of the name attribute becomes 123:

$ curl -sX PATCH \
>   http://127.0.0.1:8080/api/persons/13 \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "name": "123",
>     "country": "456"
> }' \
> | jq .
{
  "name": "123",
  "country": "456",
  "_links": {
    "self": {
      "href": "http://127.0.0.1:8080/api/persons/13"
    },
    "person": {
      "href": "http://127.0.0.1:8080/api/persons/13"
    },
    "phones": {
      "href": "http://127.0.0.1:8080/api/persons/13/phones"
    }
  }
}

Issue Links:

  • DATAREST-1524 @Transient annotation won't deserialize fields on MongoRepository PATCH requests

Backported to: 3.1.9 (Lovelace SR9)

@spring-projects-issues
Copy link
Author

Oliver Drotbohm commented

This shouldn't actually happen and clearly needs investigating. However, you should be able to see this handled better is by using Jackson annotations and properly mark the property as read only. Without that in place, Jackson will bind the incoming data to the object's field as it doesn't know about JPA specific semantics. I'll still have to investigate why JPA is not properly rejecting to persist the field.

Can you clarify whether what you see is just the immediate response? Or has the data been changed persitedly? I.e. if you issue another GET request on the item resource, do you see the old or the new data. I am asking because here's what I think happens:

updatable is specified to simply exclude the field from update statements issue by the persistence provider. I.e. you can still set the value on the entity instance, it just will never get persisted. That's why what you see actually makes some sort of sense, at least in how the individual pieces are defined to work. That said, I can see why this is confusing

@spring-projects-issues
Copy link
Author

Oliver Drotbohm commented

What still puzzles me is that Jackson apparently changes a val on an existing instance. 🤔

@spring-projects-issues
Copy link
Author

XhstormR commented

I can confirm that the data in the database has not changed after sending the patch request. After sending the patch request, there is no update statement in the hibernate log. The Get request after the patch request returns the correct read-only attribute value. It seems that the value of the read-only property is changed only in the View layer

@spring-projects-issues
Copy link
Author

XhstormR commented

The PUT request will recognize the read-only attribute, but the PATCH request will not recognize the read-only attribute. I think it may be that some operations are missing from processing the PATCH request

@spring-projects-issues
Copy link
Author

Oliver Drotbohm commented

In fact, it's the other way round: for PATCH, we rely on Jackson's ObjectMapper.readerForUpdating(…). I guess we actively have to remove properties that the backend considers not writable from the source node tree and only forward that

@spring-projects-issues
Copy link
Author

Oliver Drotbohm commented

That's fixed. Feel free to give the latest snapshots a try

@spring-projects-issues
Copy link
Author

Oliver Drotbohm commented

We unfortunately need to revisit the fix for this here in the course of fixing DATAREST-1524. I looks like inspecting the persistence configuration regarding updatability is the wrong place to start with as that can be at odds with what's expected to happen at the JSON binding level. For the latter, there actually is e.g. @JsonProperty(access = Access.READ_ONLY). I.e. if you want to prevent the binding to it you actually would need to annotate it that way

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants