Spring Data Rest - Multiple endpoints - spring-data

I’m working on a Spring Data Rest project and would like a way to effectively ‘split’ my API off two base roots / endpoints. For example, consider the root below:
{
"_links": {
"orders": {
"href": "http://localhost:8080/orders{?page,size,sort,projection}",
"templated": true
},
"customers": {
"href": "http://localhost:8080/customers{?page,size,sort,projection}",
"templated": true
},
"profiles": {
"href": "http://localhost:8080/profiles{?page,size,sort,projection}",
"templated": true
}
}
}
Now assume that I don’t really want my API consumers to mess with the profiles resource (they still technically can but its unsupported) but I still need it as part of my Rest API as my UI uses it. So what I really want is to base some of my APIs under “public/” and others under “internal/”
I.e.
{
"_links": {
"orders": {
"href": "http://localhost:8080/public/orders{?page,size,sort,projection}",
"templated": true
},
"customers": {
"href": "http://localhost:8080/public/customers{?page,size,sort,projection}",
"templated": true
},
"profiles": {
"href": "http://localhost:8080/internal/profiles{?page,size,sort,projection}",
"templated": true
}
}
}
The problem I have is that this doesn’t work as I believe Spring Data assumes the second level off my base (/) is the internal sub-resource. Is there any way I can achieve the above using Spring Data Rest?

Typically if you want to customize the response you will want to utilize the Spring HATEOAS project.

Related

Is there a known offset limit for NetSuite REST API calls?

world! We are trying to use the NetSuite SuiteQL REST API to pull down data. It works perfectly fine for most of the records we are sourcing, but we hit an interesting snag when we hit large tables.
Since the known limit of a single page of data is 1,000 rows, we are simply calling it with a limit of 1,000 and setting the offsets in 1,000 row increments. Something interesting happens when we get to an offset of 100,000 rows for a record that is larger than 100,000 rows.
If we call https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=98000, we get all the links that we expect.
{
"links": [
{
"rel": "previous",
"href": "https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=97000"
},
{
"rel": "first",
"href": "https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=0"
},
{
"rel": "next",
"href": "https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=99000"
},
{
"rel": "last",
"href": "https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=753000"
},
{
"rel": "self",
"href": "https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=98000"
}
],
"count": 1000,
"hasMore": true,
"items": [
{
[heres my data]...
Setting it to 99,000, a lot of that information disappears. It's almost as if it thinks this is the last page.
{
"links": [
{
"rel": "previous",
"href": "https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=98000"
},
{
"rel": "first",
"href": "https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=0"
},
{
"rel": "self",
"href": "https://myinstance.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=99000"
}
],
"count": 1000,
"hasMore": false,
"items": [
{
[heres my data]...
Setting it to 100,000 flat out gives me an error.
{
"type": "https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5",
"title": "Not Found",
"status": 404,
"o:errorDetails": [
{
"detail": "The specified query parameter 'offset' is out of bounds. Provide value between 0 and 753000.",
"o:errorQueryParam": "offset",
"o:errorCode": "INVALID_PARAMETER"
}
]
Has anyone seen this kind of behavior before? I scoured the documentation and couldn't find any mention of limitation in page offsets, so I'm thinking this might be a bug of some sort (the fact that it even tells you the maximum bounds and it's clearly higher than the offset specified makes me think it's a bug), but hoping someone may have seen this before, and even better, has ideas on how to get around this!
the official documentation for NetSuite REST Web Services mentions this limitation [1]:
Using SuiteQL queries, you can return a maximum of 100,000 results. For more information, see query.runSuiteQLPaged(options).

Handling contextual links from restful api in vuejs

Apparently I haven't used the correct search terms, because I can't find anything like my issue..
In Vuejs I use axios tot consume a restful API, the results can contain contextual links to ie complete a task. How do I handle these GET requests from the frontend?
This is what my response looks like:
{
"id": 2,
"description": "iPhone",
"status": "IN_PROGRESS",
"_links": {
"self": {
"href": "http://localhost:8090/orders/2"
},
"orders": {
"href": "http://localhost:8090/orders"
},
"cancel": {
"href": "http://localhost:8090/orders/2/cancel"
},
"complete": {
"href": "http://localhost:8090/orders/2/complete"
}
}
}
I've tried this (which obviously doesn't get me the needed result):
<md-table v-model="orders" :table-header-color="tableHeaderColor">
<md-table-row slot="md-table-row" slot-scope="{ item }">
<md-table-cell md-label="Description">{{
item.description
}}</md-table-cell>
<md-table-cell md-label="Status">{{ item.status }}</md-table-cell>
<md-table-cell md-label="Action">
<a v-if="item._links.complete" :href="item._links.complete.href">
Complete
</a>
</md-table-cell>
</md-table-row>
</md-table>
All I did find some information about is binding links to the router.. But that all seems to be about non conditional links, my API can omit certain links if they don't apply.
Add to your link this:
<a v-if="item._links.complete" :href="#" #click.prevent="clickHandler($event, item._links.complete.href)">
Within your vue component methods add new method:
methods:{
clickHandler(event, yourUrl){
axios.get(yourUrl)
.then(function (response){
//do what you need with this response
}
}
}

Return several resources in REST call

I am providing access to a REST endpoint where some aggregates are computed.
Let's say the model is as follows:
Purchase:
amount: amount spent
group: one of ELECTRONICS, FOOD, FURNITURE, ...
date: purchase date
Now I want to offer a timeline of purchases, aggregated on a weekly basis, and partitioned by group. This will produce the following data:
{
"ELECTRONICS": [{"week": "2018WK1", "amount": 1000.0}, ...],
"FOOD": [{"week": "2018WK1", "amount": 2000.0}, ...],
"FURNITURE": [{"week": "2018WK1", "amount": 3000.0}, ...],
...
}
Some things to note:
the number of groups is not known in advance (it depends on the stored data)
since this is computed data, I would like to return the whole thing in one single request, instead of having to do the aggregates for each of the groups in separate requests
The URL for the request would be something like: /api/weekly_purchases/2018
How can I offer these kind of resources in a REST API?
Return several resources in REST call
How would you do it as a web page?
Somewhere on your web site would be a link, with text "this week's summary" (or whatever the appropriate concept is in the language of your domain). If a user clicked that link, the browser would do a GET on one URL, which would go to the server, aggregate all of the data together, and return the result.
So do that?
REST doesn't care about the spelling of the URI (the browser doesn't try to interpret the URL, except in very shallow generic ways), so /api/weekly_purchases/2018 is fine.
The trick is recognizing that a report summarizing purchases in the current fiscal year, broken out by week, is a resource. It may have data in it that duplicates the information in other resources, even data in many other resources, but it is still a resource itself.
As already mentioned in my initial comment or by VoiceOfUnreason the same techniques that apply to the browser-based Web apply to any interaction model used by applications that follow the REST architecture principles. As also mentioned by VoiceOfUnreason a client would initially request some state returned from the entry-point, i.e. https://api.acme.com that will return a collection of links a client can use to progress its task. In order for the client to determine which URL to invoke the response should give the URI a meaningful name (link-relation name). IANA maintains a list of already specified link-relation names you should use if possible or define your own one in further standards. According to Fielding the specification of media-types and link relations is one of the most important things to do if developing a RESTful architecture.
For simplicity I use a simplified HAL-JSON syntax throughout the example.
{
...
"_links": {
"self": {
"href": "https://api.acme.com"
},
...
"archives": {
"href": "https://api.acme.com/weekly_purchases"
},
...
}
}
According to the HTML 5 spec archives
indicates that the referenced document describes a collection of records, documents, or other materials of historical interest
The link relation name therefore describes the intent of the URI which client can use if interested in retrieve a collection of historical entries. The client does not really have to know the exact URI as he will learn it by simply following the link relation's target href element. This allows the server to change its internal URI structure anytime it has to without actually breaking clients.
On following the archives target URI a client will not really know yet how the actual data has to be retrieved as the URI and the link relation name are to generic. But the server will guide the client through its task. A response on the invocation of the abovementioned target URI might return the following content:
{
"year": [
"2018": {
"_links": {
"chapter": {
"href": "https://api.acme.com/weekly_purchases/2018"
}
}
},
"2017": {
"_links": {
"chapter": {
"href": "https://api.acme.com/weekly_purchases/2017"
}
}
},
...
"2014": {
"_links": {
"chapter": {
"href": "https://api.acme.com/weekly_purchases/2014"
}
}
}
],
"_links": {
"self": {
"href": "https://api.acme.com/weekly_purchases"
},
"first": {
"href": "https://api.acme.com/weekly_purchases"
},
"next": {
"href": "https://api.acme.com/weekly_purchases?p=1"
},
"last": {
"href": "https://api.acme.com/weekly_purchases?p=3"
},
"current": {
"href": "https://api.acme.com/weekly_purchases"
}
}
}
This response basically only teaches the client that there are multiple years available to choose from and the client has to decide which year s/he is interested in an invoke that URI to proceed its task. The next, last and first link relation indicate that there are multiple pages available as only the 5 years per page are returned. The current link relation name will always point to the most recent entry in the collection, which is the initial page (or first page) of the collection-resource. Note further how multiple different link-relation names may point to the same URI. Sometimes it isn't really clear which link relation names to use as their semantics partly overlap. This is just an example on what can be done with link relation names.
A client can now further drill down to the purchases done in 2018 by following the chapter link for 2018. A response on invoking that URI may now look like this:
{
"purchase": [
"W1": {
"sum": 1263.59,
"currency": "Euro",
"_links": {
"about": {
"href": "https://api.acme.com/weekly_purchases/2018/1"
}
}
},
"W2": {
"sum": 569.32,
"currency": "Euro",
"_links": {
"about": {
"href": "https://api.acme.com/weekly_purchases/2018/2"
}
}
},
...
"W48": {
"sum": 72.98,
"currency": "Euro",
"_links": {
"about": {
"href": "https://api.acme.com/weekly_purchases/2018/48"
}
}
},
"current": {
"sum": 72.98,
"currency": "Euro",
"_links": {
"about": {
"href": "https://api.acme.com/weekly_purchases/2018/48"
}
}
}
],
"_links": {
"index": {
"href": "https://api.acme.com/weekly_purchases"
},
"self": {
"href": "https://api.acme.com/weekly_purchases/2018"
},
"current": {
"href": "https://api.acme.com/weekly_purchases/2018"
},
"prev": {
"href": "https://api.acme.com/weekly_purchases/2017"
},
"prev-archive": {
"href": "https://api.acme.com/weekly_purchases/2017"
},
"first": {
"href": "https://api.acme.com/weekly_purchases/2000"
}
}
}
You could either add content here to the weekly summary or hide it down the road by following the about link only if clients are really interested in such details.
Note further: As weekly_purchases is just a string without meaning to the client it does not really know what it means. You could therefore also rename it to purchase-archive or something like that and introduce a further choice to the client and let the client determine whether it wants a weekly, monthly or total summary of that year.
REST is about providing choices to a client and teach it what the actual choices are intended for. One of the aims the RESTful architecture tries to solve is the strict coupling between clients and servers which prevent the latter one from evolving freely and the former ones to break if the latter one changes unexpectedly. This decoupling only works if certain standards are used to increase the likelihood for interoperability. Usually out-of-band information (pre-existing knowledge about the API and how to interact with it) is leading to a coupling. Even Fielding stated that some prior knowledge is needed though but not encoded directly into the application but on reusing certain standards like well-defined and stable media-types and link-relation names.

Enable repository only for sub resource level in spring data rest?

I have 2 jpa entities Document and DispatchDetail which have one-to-many relationship. i.e. a document can have a list of dispatchDetails. I have created 2 repositories for each entity.
Now I'm gonna try a document GET.
http://localhost:7070/booking-documents-service/docs/5999571
{
"docType": "SAP_ACCOUNTS_PAYABLE",
"docStoreId": 456651,
"qualityChecked": true,
"format": "pdf",
"bookingId": -1,
"_links": {
"self": {
"href": "http://localhost:7070/booking-documents-service/docs/5999571"
},
"generatedDocument": {
"href": "http://localhost:7070/booking-documents-service/docs/5999571"
},
"dispatchDetails": {
"href": "http://localhost:7070/booking-documents-service/docs/5999571/dispatchDetails"
}
}
}
Now when I try GET request for the link listed as dispatchDetails. It is like this.
http://localhost:7070/booking-documents-service/docs/5999571/dispatchDetails
{
"_embedded": {
"dispatchDetails": [
{
"dispatchQueId": 207443,
"dispatchStatus": "S",
"recipient": "fldcvisla12678.wdw.disney.com|#|/opt/apps/shared/shuttle/SAP/OUT/|#|f-tbxshuttlenp|#|D1$NeY984|#|SFTP|#|22|#|null",
"description": "Upload :FileUploadDispatcher; FTP:null/null;\n2d89df3d-ca51-4d35-9528-439923fa48d4..",
"dispatcher": "AD",
"_links": {
"self": {
"href": "http://localhost:7070/booking-documents-service/dispatchDetails/1"
},
"dispatchDetail": {
"href": "http://localhost:7070/booking-documents-service/dispatchDetails/1"
},
"generatedDocument": {
"href": "http://localhost:7070/booking-documents-service/dispatchDetails/1/generatedDocument"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:7070/booking-documents-service/docs/5999571/dispatchDetails"
}
}
}
But I don't want dispatch details as a stand alone resource (listed in links above).
i.e. I don't want this endpoint
http://localhost:7070/booking-documents-service/dispatchDetails
Instead I only need this.
http://localhost:7070/booking-documents-service/docs/5999571/dispatchDetails
How to achieve this? i.e. allow only sub resource level operations.

REST and many to many

I'm basing my question on How to handle many-to-many relationships in a RESTful API? but want to continue from the accepted answer.
Let's suppose we have a many-to-many relationship between players and teams (just like in the question mentioned above).
As I understand it, there are several options to model this with REST resources:
The payload contains references to the related resources
GET /players/john
yields
{
"name": "John",
"_links": [
{
"rel": "team",
"href": "/teams/1"
},
{
"rel": "team",
"href": "/teams/4"
}
]
}
and
GET /teams/1
yields
{
"name": "Team 1",
"_links": [
{
"rel": "player",
"href": "/players/john"
},
...
]
}
This forces me to update a player-resource when I just want to add a player to the team. Furthermore, when I add a player to the team using a player-resource, the corresponding team-resource gets automatically updated. According to How to handle many-to-many relationships in a RESTful API?:
you don't want the alternate URL /players/5/teams/ to remain cached
In this case, teams/1 might remain cached when I update player "John" to remove team "Team 1" from it!
The relationship is modelled as another resource
GET /teams/1
yields
{
"name": "Team 1",
}
and
GET /players/john
yields
{
"name": "John",
}
Finally,
GET /relationships
yields
[
{
"_links": [
{
"rel": "player",
"href": "/players/john"
},
{
"rel": "team",
"href": "/teams/1"
}
]
},
...
]
This way, I can create and delete relationships without affecting both player-resources and team-resources. But when I delete /players/john, should the matching relationships be automatically deleted as well? If this is the case, the same rule as above is violated. If this is not the case we need the manually delete these relationships which is a lot of work I do not want to burden the consumers of my API with.
Furthermore, if we want to update the teams a certain player "John" is in, we need to delete some relationships and add others. We open ourselves up to merge conflicts (and race conditions) when someone else is editing the player "John" or the team "Team 1".
Each side of the relationship gets its own relationship-object
GET /teams/1/players
yields something like
{
"_links": [
{
"rel": "player",
"href": "/players/john"
},
...
]
}
and
GET /players/john/teams
something like
{
"_links": [
{
"rel": "team",
"href": "/teams/1"
},
...
]
}
But adding or removing one might still affect a resource that is located at a different URL (which does not share a root element)
My questions
Is there away around the problems I have mentioned in both cases?
Which of both approaches is 'preferable' or more pure REST?
How serious should I take the contstraint mentioned in How to handle many-to-many relationships in a RESTful API?:
you don't want the alternate URL /players/5/teams/ to remain cached
Thank you in advance!
You could have the following
Team
GET /teams/dream
{
"_links": {
"self": {
"href": "/teams/dream"
}
"players": {
"href": "/players?team=dream"
}
},
"name": "Dream"
}
Player
GET /player/john
{
"_links": {
"self": {
"href": "/player/john"
},
"teams": {
"href": "/teams?player=john"
},
},
"name": "John",
}
John's teams
GET /teams?player=john
{
"_links": {
},
"items": [
{
"href": "/teams/a-team"
}
],
}
Adding john to dream team, (using json patch for example) (query string on patch post...etc though rare, is valid)
PATCH /teams?player=john
[{
"op": "add",
"path": "/-",
"value": {
"href": "/team/dream"
}
}]
Get john's teams
GET /teams?player=john
{
"_links": {
},
"items": [
{
"href": "/teams/a-team"
},
{
"href": "/teams/dream"
}
]
}
John leaves A Team :
PATCH /teams?player=john
[{
"op": "remove",
"path": "/0"
}]