2

I don't fully understand how creating and accessing association resources works with Spring Data REST and MongoDB. I have noticed a few strange behaviours, and I am hoping to get some clarification here.

I have a simple object model where one resource type (Request) contains a list of objects of another resource type (Point). Both resources are top-level resources, i.e. have their own Repository interfaces.

I would like to be able to add a new Point to a Request by POSTing to /requests/{id}/points with a link to an existing point (as I know from this question that I can't just POST a point as a JSON payload).

Furthermore, I would like to be able to GET /requests/{id}/points to retrieve all the points associated with a request.

Here is my object model (getters/setters omitted):

Request.java

public class Request {

  @Id
  private String id;
  private String name;

  @DBRef
  private List<Point> points = new ArrayList<>();
}

RequestRepository.java

public interface RequestRepository extends MongoRepository<Request, String> {
}

Point.java

@Data
public class Point {

  @Id
  private String id;
  private String name;
}

PointRepository.java

public interface PointRepository extends MongoRepository<Point, String> {
}

Let's say that I POST a new Request. Then doing a GET on /requests gives me this:

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/requests",
      "templated" : false
    }
  },
  "_embedded" : {
    "requests" : [ {
      "name" : "request 1",
      "_links" : {
        "self" : {
          "href" :     "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba",
          "templated" : false
        },
        "request" : {
          "href" :     "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba",
          "templated" : false
        },
        "points" : {
          "href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points",
          "templated" : false
        }
      }
    } ]
  }
}

So I have a points link in there. Good. Now, if I do a GET on /requests/55e5dc47b7605d55c7c361ba, I notice strange thing #1. There is no points link anymore:

{
  "name" : "request 1",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba",
      "templated" : false
    },
    "request" : {
      "href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba",
      "templated" : false
    }
   }
}

But anyway forget about that for now, let's POST a point (which gets ID 55e5e225b76012c2e08f1e3e) and link it to the request:

curl -X PUT -H "ContentType: text/uri-list" http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points
-d "http://localhost:8080/points/55e5e225b76012c2e08f1e3e"

204 No Content

That seemed to work. Ok, so now if I do a GET on http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points, I expect to see the point I just added, right? Enter strange thing #2, I get the following response:

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points",
      "templated" : false
    }
  }
}

So at this point I realise I've done something wrong. Could somebody please explain to me what's happening here? Do I have a fundamental misunderstanding of the way SDR is working? Should I even be using DBRefs? Or is this simply not possible with MongoDB?

Thanks in advance, and sorry for the long question!

Edit

The Points are in fact being added as DBRefs (verified by looking at the db manually), but it appears that SDR is not giving them back. If I explicitly ask for http://localhost:8080/requests/55e5dc47b7605d55c7c361ba/points/55e5e225b76012c2e08f1e3e, then I get the following response:

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/points/55e5e225b76012c2e08f1e3e",
      "templated" : false
    },
    "point" : {
      "href" : "http://localhost:8080/points/55e5e225b76012c2e08f1e3e",
      "templated" : false
    }
  }
}

Which is also strange... where is my Point.name?

Community
  • 1
  • 1
Justin Lewis
  • 1,261
  • 1
  • 15
  • 33

1 Answers1

3

Both aspects are caused by a glitch in Spring Framework 4.2.0, which Spring Boot 1.3 M4 has upgraded to which causes Spring Data RESTs custom serializers not applied in some cases. This is already fixed in Spring 4.1.2. To upgrade your Gradle project to that version, upgrade to the Boot plugin to a 1.3 and then fix the Spring version to 4.2.1 using the following declaration:

ext['spring.version'] = '4.2.1.RELEASE'

Make sure you properly initialize the collection with an empty one as a null value will cause 404 Not Found being returned. An empty list will produce correct HAL output.

Generally speaking association resources should be used with already existing resources, esp. with Spring Data MongoDB there's no other choice as it doesn't have persistence by reachability. I.e. you'd need to persist the related instance first (aka. POST to it's collection resource and get a Location header back) and the assign the just created resource (aka. PUT or POST the just obtained Location to the association resource), which seems to be what you're doing.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
  • Hi Oliver, thanks for your response! Re #1, [here is the issue](https://jira.spring.io/browse/DATAREST-664) with sample project linked. Re #2, I actually made a typo in the question. I am sending the URI in a `-d`. You should be able to reproduce it in the sample project. Not sure if it is a separate issue. P.S. SDR is awesome, keep up the good work! – Justin Lewis Sep 01 '15 at 19:47
  • Please see my edit, which may make it more obvious what is going on. FYI, I'm using `spring-data-rest-core:2.4.0.RC1` (gotten via `spring-boot-starter-data-rest:1.3.0.M4`). – Justin Lewis Sep 01 '15 at 19:58
  • Let's continue at the ticket for now. I'll update my answer once we got to a conclusion there :). – Oliver Drotbohm Sep 02 '15 at 06:22
  • Updated after our findings. – Oliver Drotbohm Sep 02 '15 at 09:54