spring-projects / spring-projects/spring-data-rest

inconsistant behaviour of a HTTP PATCH request on a com.mongodb.BasicDBObject member [DATAREST-772]

Open
#1,147 1 comment 0 reactions 1 assignee View on GitHub

@odrotbohm is already working on this.

Since Dec 31, 2020.

type: bug
Dominant language
Java
Stars
958
Forks
568
PR merge metrics
No merged PRs in 30d

Description

Marc Tarin opened DATAREST-772 and commented

I've created a simple webservice with Spring Boot 1.3.2 (with dependencies on spring-boot-starter-data-rest and spring-boot-starter-data-mongodb) to read/write MongoDB documents describe by the following class (getters/setters omitted):

@Document(collection="items")
public class Item {
    @Id
    private String id;
    private String name;
    private BasicDBObject data;
}

Persisting a new object in the database works as expected. By POSTing:

{ "name" : "foo",
  "data" : {}
}

I get the following answer (_links omitted for clarity purposes):

{
  "name" : "foo",
  "data" : { },
  "_links" : ...
}

The purpose of the data field is to store data of variable complexity and depth.
Let's keep it simple for now... PATCHing with:

{ "data" :
  { "a" : 1,
    "b" : 2,
    "c" : 3
  }
}

Returns:

{
  "name" : "foo",
  "data" : {
    "a" : 1,
    "b" : 2,
    "c" : 3
  },
  "_links" : ...
}

Further PATCHing with:

{ "data" : {
  "a" : 0,
  "d" : 4
  }
}

Returns:

{
  "name" : "foo",
  "data" : {
    "a" : 0,
    "d" : 4
  },
  "_links" : ...
  }
}

Question one: is this result correct? I would have expected the following instead:

{
  "name" : "foo",
  "data" : {
    "a" : 0,
    "b" : 2,
    "c" : 3
    "d" : 4
  },
  "_links" : ...
}

Now, for the funny part...
As a foreword, let me just say that I know that PATCH is unsafe and non-idempotent and should be used with caution.

Let's say I have changed my mind about d, and I want to store a subdocument in it:
PATCHing with:

{ "data" : {
    "a" : 2,
    "d" : {}
  }
}

Throws and exception:

{"cause":{"cause":null,"message":"Can not update object of type java.lang.Integer (by deserializer of type com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer)"},"message":"Could not read payload!; nested exception is java.lang.UnsupportedOperationException: Can not update object of type java.lang.Integer (by deserializer of type com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer)"}

There is a workaround, by simply PATCHing data with a only, then re-sending the PATCH above, I can get what I wan't, ie:

{
  "name" : "foo",
  "data" : {
    "a" : 2,
    "d" : { }
  },
  "_links" : ...
}

A bit awkward to me, since the exception above sometimes get thrown when I re-add d as subdocument, even if I PATCHed it away before!

Let's play a bit more (sorry for the long post, btw). The current data being:

{
  "name" : "foo",
  "data" : {
    "a" : 0,
    "d" : 4
  },
  "_links" : ...
  }
}

With a new PATCH that goes:

{ "data" : {
    "d" : {
    "c" : 5
   }
  }
}

Consistently with what happened before, I should expect the following response:

{
  "name" : "foo",
  "data" : {
    "d" : {
    "c" : 5
   }
  },
  "_links" : ...
  }
}

Turns out I get:

{
  "name" : "foo",
  "data" : {
    "a" : 2,
    "d" : {
      "c" : 5
    }
  },
  "_links" : ...
}

Question two: is this result correct?
If it the expected behaviour, It seems kind of inconsistant with the result that led me to Question One.

Now for Question Three...
The current data being :

{
  "name" : "foo",
  "data" : {
    "a" : 2,
    "d" : {
      "c" : 5
    }
  },
  "_links" : ...
}

PATCHing an update for a and d:

{ "data" : {
    "a" : 3,
    "d" : {
      "c" : 4
    }
  }
}

Here's what I get:

{
  "name" : "foo",
  "data" : {
    "a" : 2
  },
  "_links" : ...
}

The payload for d just went down the toilet :(
If I re-send the PATCH, it will update d correctly.
And if I send it again, or even if I change the contents of d once more, d get flushed down again, and so on.

Question Three: what is happening?

I have been scratching my head for a few hours and I really don't have a clue about this strange behaviour (and I am confident there's are many more unexpected things that could happen).

The only thing clear is that right now, send PATCH request should be avoided when dealing with a data schema that is actually schema less, until the result can get somewhat predictable.

Regards


Affects: 2.4.2 (Gosling SR2)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.