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
Support use case-driven inlining of managed resources [DATAREST-221] #607
Comments
Willie Wheeler commented OK, Oliver just asked me to go ahead and describe the technical approach I have in mind, so I'll do that. The key idea in what's above is "use case driven". Let's do a concrete example. Say I am building a REST API for a configuration management database (CMDB), and the API has various resources, including regions, data centers and service instances. There's a one-many relationship between regions and data centers, and again a one-many relationship between data centers and service instances. Say I want to define a schema for data centers. The desired representation varies depending on the query:
These are just examples of how we might design the representation. Different people will have different ideas about what makes for a good design here, and that's fine. The key point is that the representations can change according to the use case. The way I dealt with this is to use annotations to define for each entity a set of filtering queries, using annotations, not entirely unlike how JPA's \
\ Something like that. Again the exact query mechanism doesn't matter; it's just that there should be a way to define queries for specific use cases. In the queries above, "region.infrastructureProvider" tells the framework to export both the region and its parent infrastructure provider (Amazon, Rackspace, whatever) as part of the representation. Originally I tried to do the serialization with I don't know how much of that made sense. Happy to answer questions about it. |
Oliver Drotbohm commented Thanks for putting so much effort in documenting that stuff, Willie. I've raised the priority a bit as functionality like this really would be a huge improvement to the library. I'll take this forward to the team and comment the ideas we already had once I find time |
Johannes Hiemer commented I would really like to see this feature as well. In some case this would definitely reduce the amount of single request significantly. In contrast to Willie I would prefer a combined, and perhaps more flexible, strategy of relationship resolving. My ideas therefore would be:
This would increase implementation and resource loading a lot |
Willie Wheeler commented In my current implementation I've implemented something that's actually more flexible than what I describe above, but not quite as flexible a client-visible dynamic query language. What I did was add a "view" parameter to the various endpoints. So the client can do things like "/service-instances/seiso-prod?view=nodes" or "/service-instances/seiso-prod?view=dependencies" to get different views of some given resource. It's not as flexible as what Johnannes describes, but it's an attempt to strike a balance between simplicity and flexibility. I've had colleagues in the past however who advocated for the same thing you're suggesting so I expect there's an honest-to-goodness debate here about which is more appropriate, if in fact either is. :-) |
Johannes Hiemer commented Great really waiting for the feedback of the SD Rest team |
Oliver Drotbohm commented As already indicated, this is really great feedback. We will definitely get this onto the backlog for 2.1 as I get people asking for this stuff from quite different angles. I had a few ideas and throughs being triggered from what you wrote and I'll just drop them here for further discussion. The content supersetIf I read correctly, all of the samples you listed were subsets of the actual domain objects. Is that right? Or, do you need to assemble additional data into the representations? If you say "use-case-driven" do you mean at design time or even multiple representations at runtime (i.e. the client being able to select which fields to return on a per-request basis)?
|
Oliver Drotbohm commented I've just pushed a first draft of a support for projections to master. See the commit message for details. I have updated the Spring RESTBucks project to use the projections. See this commit for details. The resource is exported in full detail for |
Sri commented My use case is where clients could able to define which fields they would like during runtime. I see
I am sure SDR team would come up with a solution for deeply nested structures :) |
Oliver Drotbohm commented Has anyone had the time to play with the API introduced for this ticket? Anyone got it to stretch its limits? ;) I've got a bit of feedback from Johannes already which lead to a few bug fixes. We're shooting for an M1 of Dijkstra by the end of next week |
Willie Wheeler commented Haven't yet been able to. I do have a question anyway. Suppose that there is an Employee entity where each Employee has a manager. Will your approach require you do choose between no managers and the entire chain, or is it possible to show simply a single manager? (This was the problem I ran into with JsonView--it made a decision by the type of entity.) |
Sri commented
|
Andre Luiz do Nascimento Sousa commented I've implemented the new approach using projections to show inline all data from my entity's relationship objects. With the URL below, I call the projection: But now I have a new problem. At the JSON result, the entity name inside the "_embedded" block got a strange name with the temporary proxy used ("$Proxy145s"). How can I change this to a fixed name that I can parse at my client side? See my JSON result below: { |
Oliver Drotbohm commented Which version of Spring HATEOAS are you using? The latest 0.10.0.RELEASE (which SD REST 2.1 M1 depends on) should actually mitigate the proxy indirection |
Andre Luiz do Nascimento Sousa commented I used tha Spring HATEOAS 0.10.0.RELEASE and It works as expected! This issue saved a big part of our project here. Thanks, Olivier Gierke |
Szymon Stasik commented Some more javadoc in the
|
Szymon Stasik commented Also I see new question now - what about nested 'subresources'? eg. for {
"_links" : {
"self" : {
"href" : "http://127.0.0.1:8080/api/orders/200"
},
"shippingType" : {
"href" : "http://127.0.0.1:8080/api/orders/200/shippingType"
},
"customer" : {
"href" : "http://127.0.0.1:8080/api/orders/200/customer"
}
}
} and {
"firstName" : "John",
"lastName" : "Doe",
"_links" : {
"self" : {
"href" : "http://127.0.0.1:8080/api/customers/213"
},
"address" : {
"href" : "http://127.0.0.1:8080/api/customers/213/address"
}
}
} after using projection to inline customer the reference to customer.address is being lost: {
"customer" : {
"firstName" : "John",
"lastName" : "Doe",
}
"_links" : {
"self" : {
"href" : "http://127.0.0.1:8080/api/orders/200{?projection}",
"templated" : true
},
"shippingType" : {
"href" : "http://127.0.0.1:8080/api/orders/200/shippingType"
},
"customer" : {
"href" : "http://127.0.0.1:8080/api/orders/200/customer"
}
}
} |
Oliver Drotbohm commented Symon: would you mind creating another ticket, as this one has been resolved against a particular version already |
Ian Duffy commented Hi, It is possible to return links on a projection? Specifically a link to self |
Szymon Stasik commented just for reference here is an issue DATAREST-302 |
Ruslan Stelmachenko commented
Here the |
Ruslan Stelmachenko commented
Expected output:
For ManyToOne relations all works fine. For example |
Ruslan Stelmachenko commented Correction for: 1. URL parameter This only doesn't work for collections and ebbeded objects. |
KJ commented I am seeing the same as Ruslan using Spring Boot 1.3.3.RELEASE specified version of spring data rest. An excerptProjection prevents any other projection from been called at the collection level. |
Willie Wheeler opened DATAREST-221 and commented
This is a high-level ticket to kick off a discussion regarding what I believe is an important need for the Spring Data REST framework.
This is mostly autobiographical, just because I've had enough relevant experience over the past few years that I think/hope the perspective will be valuable.
I've built three REST APIs for three different corporate clients over the past few years. The first one was a custom build (Spring Web MVC + Hibernate), but the second and third started out as Spring Data REST implementations (more on that in a bit). In each case, the API users explicitly considered and rejected what I'll call "one-deep" endpoint schemas of the sort that Spring Data REST produces. What I mean here is that the top-level resource includes its simple properties directly, but exposes its exported/managed associations as links, with no inlining option besides the heavyweight option of simply implementing custom endpoints.
FWIW, I don't myself care for one-deep schemas, because they're driven more by the desire to keep the API framework simple (simple rules, simple implementation) than they are by the app-specific use cases. For example, when getting a single resource by ID, it's very common to want a deep view of the data, and it's a minor annoyance to have to make repeated calls to get associations that probably fall on the 80% side of the 80/20 rule. A more important case is where we want to load collection data. Here we really want to avoid the dilemma between n+1 queries on the one hand and a bunch of client-side stitching together of bulk query data on the other. With collection data we can often still identify associations that are probably desired with the original call, even if it's again an 80/20 judgment call.
Anyway, in all three of the cases that I mentioned above the users expressed a lack of enthusiasm about the one-deep approach. They were a captive audience, so they would have had to suck it up if I said "that's just how the framework works." But I wanted to delight them, not tell them that they had to accept something that I myself rejected. The result for the second and third APIs I mentioned above was that I ended up abandoning SDR itself. I kept Spring Data Commons (e.g.
PersistentEntity
,Repositories
and the various repo base interfaces), Spring Data JPA and Spring HATEOAS, but effectively rebuilt a subset of SDR (including CRUD controller, property controller, search controller,PersistentEntityResource
, etc.) in such a way that I could control resource inlining on a per use case basis.While it's usually possible to come up with client workarounds (batch query stuff, cache it, assemble everything client side), that's often a tough sell, and ultimately the framework should help us build REST APIs that our users love rather than tolerate. There's nothing un-RESTful about resource inlining, so my suggestion is that SDR start exploring ways to move in this direction.
I have ideas on the technical approach, but as this is already long enough, I'll give others the chance to weigh in on the basic concept before offering technical suggestions.
Issue Links:
DATAREST-236 User based content and actions
("is duplicated by")
DATAREST-268 Exception when putting element with return type
DATAREST-243 please support the ability to expand parts of a resource representation
DATAREST-264 Allow projections by listing properties to be returned
SPR-7156 Integrate Jackson
@JsonView
4 votes, 15 watchers
The text was updated successfully, but these errors were encountered: