How should a server respond to a GET request for a resource that is an unbounded collection in a REST API? - rest

Consider the following URIs where the server returns a representation corresponding to the nth Fibonacci number.
GET /fib/0 ==> { value: 0, _links: { next: { href: '/fib/1' } } }
GET /fib/1 ==> { value: 1, _links: { next: { href: '/fib/2' } } }
GET /fib/2 ==> { value: 1, _links: { next: { href: '/fib/3' } } }
...
GET /fib/73 ==> { value: 806515533049393, _links: { next: { href: '/fib/74' } } }
... etc ...
Given the constraints of a strict interpretation of REST, what should the server return when it receives the following request?
GET /fib

Wikipedia says to return the collection members as links, optionally with details of each member. Obviously you cannot return the whole collection in this case, because it is unbounded. The RESTful APIs we have built with large collections will return the collection paginated. I don't know if this would be a useful thing to return in your use case, but I see you are using HAL, so if you were to, it could look like this for linked data:
{
"_links": {
"self": { "href": "/fib?page=2" },
"next": { "href": "/fib?page=3" },
"prev": { "href": "/fib?page=1" },
"fibs": [
{"href": "/fib/4" },
{"href": "/fib/5" },
{"href": "/fib/6" }
},
}
or this for embedded:
{
"_links": {
"self": { "href": "/fib?page=2" },
"next": { "href": "/fib?page=3" },
"prev": { "href": "/fib?page=1" },
},
_embedded: {
"fibs": [
{
"_links": {"self": "/fib/4" },
"value": 2
},
{
"_links": {"self": "/fib/5" },
"value": 3
},
{
"_links": {"self": "/fib/6" },
"value": 5
}
}
}
You can see a similar example to this in the HAL spec: https://datatracker.ietf.org/doc/html/draft-kelly-json-hal-00#section-6
If the user doesn't specify the page query string param, we return the first page.
If returning the collection (paginated or in it's entirety) doesn't make sense, then I would recommend returning a 405 Method Not Allowed HTTP response code. If you cannot insert items (POST) into the collection either, then maybe return the same 405 response for that too.

The server should return a resource that is represented by /fib or 404 if no such resource is defined/exists. What the resource actually IS depends on the domain and requirements of your application.

According to W3's Dereferencing HTTP URIs, because I cannot return an infinite sequence, it looks like I should return an HTTP 303 - See Other redirect to another URI (e.g. /fib/Information) which contains additional related information about the Fibonacci sequence such as a human-readable description, an algorithm to compute it, or an RDF description of the aggregation. I should support content negotiation to allow consumers to select an appropriate representation.

Related

How to create multiple entities using a single OData POST to a Business Central Web Service?

I tried to POST an array of objects and, as expected, it isn't that easy. In my case I want to insert multiple Transfer Order Lines in the same request.
// Request Body
[
{
"documentNo": "1002",
"itemNo": "1968-S",
"quantity": 3
},
{
"documentNo": "1002",
"itemNo": "1968-S",
"quantity": 113
}
]
// Response
{
"error": {
"code": "BadRequest",
"message": "Invalid Request Body CorrelationId: a2606676-3f8f-4753-aaee-be91a621f070."
}
}
Is it possible to do what I want without sending a request for every Line entity I want to add?
You need to create a structure like this:
url: api/2.0/$batch
Body:
```lang-json
{
"requests":
[
{
"method": "POST",
"id":"R1",
"url": "companies(id)/APIEntitySetName",
"headers":
{
"content-Type": "application/json"
},
"body":
{}
},
{
another line
}
]
}
```

Github GrapQL API returns only the last StatusContext for each context

I'm cross-posting the question from here.
I’m interested in knowing whether it’s possible to fetch all the statuses for all the contexts for a given reference using the GQL API.
The query that I’m currently doing is the following:
{
repository(owner: "owner", name: "name") {
pullRequests(headRefName: "head-ref", last: 1) {
nodes {
id
commits(first: 10) {
nodes {
commit {
oid
status {
contexts {
context
createdAt
id
description
state
}
}
}
}
}
}
}
}
}
This query returns a single status for each status context, and those are the last ones for each:
{
"data": {
"repository": {
"pullRequests": {
"nodes": [
{
"id": "some-id",
"commits": {
"nodes": [
{
"commit": {
"oid": "some-oid",
"status": {
"contexts": [
{
"context": "context-1",
"createdAt": "2021-07-06T21:28:26Z",
"id": "***",
"description": "Your tests passed!",
"state": "SUCCESS"
},
{
"context": "context-2",
"createdAt": "2021-07-06T21:25:26Z",
"id": "***",
"description": "Your tests passed!",
"state": "SUCCESS"
},
]
}
}
}
]
}
}
]
}
}
}
}
On the other hand, if I use the REST API with this query:
curl -i -u se7entyse7en:$(cat token) https://api.github.com/repos/owner/name/commits/some-oid/statuses
where some-oid is the corresponding retrieved with the GQL API, the output contains ALL the statuses. In particular, I can see all the statuses of context-1 and context-2 that happened before those that are returned by the GQL API.
It seems a limitation of the GQL schema given that StatusContext is a node instead of being a list of nodes. Basically, I expect StatusContext to be of type [Status!]! where Status represents a single status for the given context.
Am I missing something? Is this something expected to be changed in the future? Is the REST API the only option?
Thanks.
I opened a support ticket and this is the expected behavior indeed, there are no plans for changing it. The only solution is to use the REST API.
The link to the community forum is this one.

GraphQL query result for object that does not exist

I have a GraphQL query that calls a REST service to get the return object. The query contains an Id parameter that is then passed to the service. However, the REST service can respond with http status 404 Not Found if an object with that Id does not exist. That seems like the right response.
How do you model a Not Found response in GraphQL?
Is there a way to inform the GQL caller that something does not exist?
Update
Some options I am considering:
Return null
Change the GrqlhQL Query to return a list of objects and return empty list of nothing is found
Return some kind of error object with an error code
but it is unclear if there is a recommended practice in GQL API design.
You might treat it as an error and handle it accordingly.
I recommend you to check the GraphQL spec, the paragraph about error handling.
I hope it contains exactly what you are looking for.
Basically, you should return whatever you could, and inform a client about potential problems in the "errors" field.
The example from the documentation:
Request:
{
hero(episode: $episode) {
name
heroFriends: friends {
id
name
}
}
}
Response:
{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [ { "line": 6, "column": 7 } ],
"path": [ "hero", "heroFriends", 1, "name" ]
}
],
"data": {
"hero": {
"name": "R2-D2",
"heroFriends": [
{
"id": "1000",
"name": "Luke Skywalker"
},
{
"id": "1002",
"name": null
},
{
"id": "1003",
"name": "Leia Organa"
}
]
}
}
}

How to create a jsonpath to instagram business account in a batch request with the Graph API?

My goal is to create a batch request with dependent calls as documented here:
https://developers.facebook.com/docs/graph-api/making-multiple-requests#operations
You can reference the results of a previous operation using JSONPath in form post parameters in addition to query string parameters.
I can't get the right JSONPath to make it work when there are multiple elements in the data array that have an instagram_business_account.id (iba_id)
The two calls that I want to make are
/me/accounts?fields=instagram_business_account
/17841400714813297?fields=business_discovery.username(thomasguntenaar){media_count}
my batch looks like
[
{"method":"GET","name":"get-ig", "relative_url":"me/accounts?fields=instagram_business_account"},
{"method":"GET", "relative_url":"{result=get-ig:$.data..instagram_business_account.id}?fields=business_discovery.username(thomasguntenaar){media_count}}"}
]
in the second query you are supposed to put the JSONPath to the instagram business account id
after result=
I get this error back
{
"code": 404,
"body": "{
\"error\": {
\"message\": \"(#803) Some of the aliases you requested do not exist: 17841400714813297,17841403388404550,17841401383243593\",
\"type\": \"OAuthException\",
\"code\": 803,
\"fbtrace_id\": \"FV8qA+oA7fp\"
}
}"
}
Facebooks json response after the first call is
{
"data": [
{
"id": "466912700123917"
},
{
"id": "502655553273897"
},
{
"instagram_business_account": {
"id": "17841400714813297"
},
"id": "503124266815195"
},
{
"instagram_business_account": {
"id": "17841403388404550"
},
"id": "510613645695833"
},
{
"instagram_business_account": {
"id": "17841401383243593"
},
"id": "2061834074114937"
}
],
"paging": {
"cursors": {
"before": "NDY2OTEyNzAwMTIzOTE3",
"after": "MjA2MTgzNDA3NDExNDkzNwZDZD"
}
}
}
When you query the second request like this
?ids=17841400714813297,17841403388404550,17841401383243593&fields=business_discovery.username(thomasguntenaar){username,media_count}
the response looks like this
{
"17841400714813297": {
"business_discovery": {
"username": "thomasguntenaar",
"media_count": 76,
"id": "17841400714813297"
},
"id": "17841400714813297"
},
"17841403388404550": {
"business_discovery": {
"username": "thomasguntenaar",
"media_count": 76,
"id": "17841400714813297"
},
"id": "17841403388404550"
},
"17841401383243593": {
"business_discovery": {
"username": "thomasguntenaar",
"media_count": 76,
"id": "17841400714813297"
},
"id": "17841401383243593"
}
}
(#803) Some of the aliases you requested do not exist: 17841400714813297,17841403388404550,17841401383243593
Apparently the API thinks this was supposed to be one id, and doesn’t realize it is supposed to be three separate ones.
The API has a syntax to request data for more than one object in one request - instead of /{id}?fields=foo, you can make a request of the form ?ids={1,2,3}&fields=foo, to request this data for the objects with ids 1, 2 and 3 in one go. The resulting data structure will contain a sub-structure for each of those ids.
The same structure should work in batch requests as well, when parts (here, the IG account ids returned by the previous query) are dynamically inserted.

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.