Many-to-Many JSON API URI naming - uri

I'm wondering if the following naming convention would correctly fit the JSON API standard, as it is not specifically mentioned anywhere that I can find.
Given a many to many relationship between accounts and products, with an account-product resource storing pivot data between the two.
I would to know how to handle the /api/v1/accounts/1/products relationship resource
account > account-product > product
URLs:
/api/v1/accounts: returns account resources
/api/v1/account-products: returns account-product resources
/api/v1/accounts/1/products: returns account-product resources
OR
/api/v1/accounts/1/products: return product resources related to the account
The two arguments here being this:
Option 1: account/1/products should return the link between the accounts and the products as it should essentially act as the ID should essentially act as a hyphen e.g. account/1/products really means account-products.
Option 2: account/1/products should returns products related to the account and also include the account-products resource as a mandatory relationship because the resource in the URI is product, not account-product

JSON:API specification is agnostic about URL design. Therefore it mostly depends where these URLs are used. But JSON:API spec comes with some recommendations on URL design. I assume that you follow that ones. Especially that /api/v1/accounts/1/products is used as related resource link, e.g.
{
"type": "accouts",
"id": "1",
"relationships": {
"products": {
"links": {
"related": "/api/v1/accounts/1/products"
}
}
}
}
In that case the spec is quite clear about what should be returned:
Related Resource Links
A “related resource link” provides access to resource objects linked
in a relationship. When fetched, the related resource object(s) are
returned as the response’s primary data.
For example, an article’s comments relationship could specify a link
that returns a collection of comment resource objects when retrieved
through a GET request.
https://jsonapi.org/format/#document-resource-object-related-resource-links
From how you describe your data structure an account has many account-products, which belongs to a product. So it should return the related account-products. You may include the products they belong to by default.
What may confuse you is the concept of intermediate relations like "Has One Through" in some ORMs (e.g. Eloquent). The naming account-products suggest that this might be an example of such. Something like that is not supported by JSON:API spec. Intermediate relationships should modeled using a normal resource type. So in your case account-products would be a normal resource type like accounts and products.

Related

GMB - Removal of LocationState object in Business Information API

Google deprecated the old GMB API v4.9 account.locations.get endpoint, and replaced it with Business Information API v1 locations.get.
Code change that affects me is:
Removal of LocationState object. The existing fields have been moved into Metadata.
The new Metadata object does not return the attributes LocationState object contained before. The ones I'm interested in are:
isVerified
isPublished
isSuspended
isDisabled
isDisconnected
etc...
My question is:
How could I get this data without using deprecated endpoints?
Try Verification API getVoiceOfMerchantState
isVerified (verify),
isPublished (hasVoiceOfMerchant=true AND hasBusinessAuthority=true),
isSuspended (complyWithGuidelines),
isDuplicate (resolveOwnershipConflict).
isDisabled & isDisconnected have no equivalent in new API
As far as I can see, based on the link you have sent it is written:
Endpoint URL:
Endpoints for all business information, attributes, categories, chains and locations search are accessible at https://mybusinessbusinessinformation.googleapis.com/v1/ instead of https://mybusiness.googleapis.com/v4/
The path name for locations endpoints has changed from
accounts/accountId/locations/locationId to locations/locationId
Maybe it was better if you could provide the request uri in the previous version so we could help you more precisely. Anyhow, what I tested in the google playground is as follows:
open [https://developers.google.com/oauthplayground]
after setting your clientId and Authorisation stuff, in the Request URI write
https://mybusinessbusinessinformation.googleapis.com/v1/locations/XXXXX?readMask=storeCode,metadata,profile,serviceArea,labels,adWordsLocationExtensions
instead of XXXXX, write your locationId
you can write different readMask fields, The possible fields for readMask are:
play with different fields to check if you have your desired one or not readMask="storeCode,regularHours,name,languageCode,title,phoneNumbers,categories,storefrontAddress,websiteUri,regularHours,specialHours,serviceArea,labels,adWordsLocationExtensions,latlng,openInfo,metadata,profile,relationshipData,moreHours";
If above does not help you, in the link below I see that all metadata attribute of a location might be:
Click [here] (https://developers.google.com/my-business/reference/businessinformation/rest/v1/accounts.locations#Location.Metadata)

How to get details about a publication using Microsoft Academic Knowledge API

I'm using the Interpret and Evaluate methods from Project Academic Knowledge.
If you search for Composite(J.JN='jama') and include J.JId in the request, you'll get a response with the Id 172573765:
{
"logprob": -14.823,
"prob": 3.651345212E-07,
"Id": 2107832644,
"J": {
"JId": 172573765
}
}
You can find more details about that journal by opening: https://academic.microsoft.com/journal/172573765
However, there doesn't seem to be a way to retrieve that same information (Number of papers, number of citations, website, about) using the API. How can we get this (other than by accessing the URL of the journal)?
Project Academic Knowledge allows you to retrieve journal entities using the Evaluate method. The query is simply Id=JId. For example, to retrieve the journal name, publication and citation counts you'd use:
https://api.labs.cognitive.microsoft.com/academic/v1.0/evaluate?subscription-key=SUBSCRIPTION_KEY&attributes=Id,JN,DJN,CC,PC&expr=Id=172573765
See the journal entity documentation page for a list of the available attributes you can request.

Having consistency during multi path updates when the paths are not deterministic and are variable

I need help in a scenario when we do multipath updates to a fan-out data. When we calculate the number of paths and then update, in between that, if a new path is added somewhere, the data would be inconsistent in the newly added path.
For example below is the data of blog posts. The posts can be tagged by multiple terms like “tag1”, “tag2”. In order to find how many posts are tagged with a specific tag I can fanout the posts data to the tags path path as well:
/posts/postid1:{“Title”:”Title 1”, “body”: “About Firebase”, “tags”: {“tag1:true, “tag2”: true}}
/tags/tag1/postid1: {“Title”:”Title 1”, “body”: “About Firebase”}
/tags/tag2/postid1: {“Title”:”Title 1”, “body”: “About Firebase”}
Now consider concurrently,
1a) that User1 wants to modify title of postid1 and he builds following multi-path update:
/posts/postid1/Title : “Title 1 modified”
/tags/tag1/postid1/Title : “Title 1 modified”
/tags/tag2/postid1/Title : “Title 1 modified”
1b) At the same time User2 wants to add tag3 to the postid1 and build following multi-path update:
/posts/postid1/tags : {“tag1:true, “tag2”: true, “tag3”: true}
/tags/tag3/postid1: {“Title”:”Title 1”, “body”: “About Firebase”}
So apparently both updates can succeed one after other and we can have tags/tag3/postid1 data out of sync as it has old title.
I can think of security rules to handle this but then not sure if this is correct or will work.
Like we can have updatedAt and lastUpdatedAt fields and we have check if we are updating our own version of post that we read:
posts":{
"$postid":{
".write":true,
".read":true,
".validate": "
newData.hasChildren(['userId', 'updatedAt', 'lastUpdated', 'Title']) && (
!data.exists() ||
data.child('updatedAt').val() === newData.child('lastUpdated').val())"
}
}
Also for tags we do not want to check that again and we can check if /tags/$tag/$postid/updatedAt is same as /posts/$postid/updatedAt.
"tags":{
"$tag":{
"$postid":{
".write":true,
".read":true,
".validate": "
newData.hasChildren(['userId', 'updatedAt', 'lastUpdated', 'Title']) && (
newData.child('updatedAt').val() === root.child('posts').child('$postid').val().child('updatedAt').val())”
}
}
}
By this “/posts/$postid” has concurrency control in it and users can write their own reads
Also /posts/$postid” becomes source of truth and rest other fan-out paths check if updatedAt fields matches with it the primary source of truth path.
Will this bring in consistency or there are still problems? Or can bring performance down when done at scale?
Are multi path updates and rules atomic together by that I mean a rule or both rules are evaluated separately in isolation for multi path updates like 1a and 1b above?
Unfortunately, Firebase does not provide any guarantees, or mechanisms, to provide the level of determinism you're looking for. I have had the best luck front-ending such updates with an API stack (GCF and Lambda are both very easy, server-less methods of doing this). The updates can be made in that layer, and even serialized if absolutely necessary. But there isn't a safe way to do this in Firebase itself.
There are numerous "hack" options you could apply. You could, for example, have a simple lock mechanism using a dedicated collection for tracking write locks. Clients could post to a lock collection, then verify that their key was the only member of that collection, before performing a write. But I hope you'll agree with me that such cooperative systems have too many potential edge cases, potential security issues, and so on. In Firebase, it is best to design such that this component is not a requirement in the first place.

ServiceStack proper way to access routes and avoid markup

I think this question is more about best practices regarding web services and not necessarily limited to ServiceStack only. From what I've read here and on the SS wiki, the 'recommended' way to implement parent-child entities is to break them down via routes.
For example:
/Users/{UserID}
/Users/{UserID}/Entities
Where User is the logged on user, and entities are his/her items. I'm implementing jqueryui autocomplete and here is where I'm suspecting I'm not doing the right thing.
In the script the path needs the Userid, so I have to manually render it in the browser so that it reads:
type: "GET",
url: "svc/users/**8**/entities",
data: { "SearchTerm": request.term, "Format": 'json' },
This smells wrong to me. I have the UserID from the session and I can get it that way. So I wonder if there a better way to access these objects without having to render data directly into markup?
Am I doing this wrong?
On a side note: I know I could place this data in a hidden field and access it via script etc, I am just curious if there is a better/recommended way to do this via sessions while keeping the routes as is.
Generally this is done with another endpoint, Facebook for instance, uses /my/, but you could do what ever you want.
The reason being, it's very likely you will be returning different information for a user about themselves than you share about that user with someone else.
Let's pretend /user/{UserId}/books returns a user's favorite books. If I want to know what someone's favorite books are, I might be interested in the title, and a brief description, but if I want to see (and possibly manage) my list of favorite books then I might want more information, like the day I added the favorite book, or friends of mine that also like the book.
so /user/{UserId}/books returns:
{
"books":[
{ "title":"Hary Potter", "desc":"A boy who is magic..." }
]
}
however /my/books returns:
{
"books":[
{
"title":"Harry Potter",
"desc":"A boy who is magic...",
"friensWhoLikeBook":[
{ "id":1234, "name":"Bob" }
],
"personalCommentsAboutBookNotToBeShared":"This book changed my life..."
}
]
}

restful-like CRUD operation url pattern for nested model

Generally,the CRUD operation url pattern for model can be like this(Suppose the Model is Post):
new: /posts/new(get)
create:/posts/(post)
edit:/posts/edit(get)
update:/posts/1(put)
get:/posts/1(get)
However if there is a nested model "Comment".
And the association of the "Post" and "Comment" is one-many.
So what should the CURD operation url pattern like for comments ?
new: /posts/1/comments/new or /comments/new
create:?
edit:?
update:?
.......
What is the best practice?
Update:
It seems that the url for comment should be like this:
Get one comment for one post: /posts/1/comments/1
create: /posts/1/comments
update: /posts/1/comments/1
delete: /posts/1/comments/1
Now I am confused with the update and delete operation.
For update and delete: /posts/1/comments/1
SInce the comment id is specified,so I wonder if the /posts/1 inside the url is necessary?
I think the key is whether a comment is "contained" by the post resource. Remember that RESTful urls should be permalinks so under all of your scenarios, the end point to a specific comment(s) must not change. It sounds like it's containted so the url pattern can have the comment nested within the post. If that's not the case (e.g. a comment could move to another post which if nested would change the url) then you want a more flat structure with /comment/{id} urls referenced by the post resource).
The key is if it's a RESTful "Hypermedia API" then like the web it constantly links to the nested or referenced resources. It doesn't rely on the client necessarily understanding the REST pattern or special knowledge as to what end point holds the referenced or contained
resource.
http://blog.steveklabnik.com/posts/2012-02-23-rest-is-over
If a 'comment' is the resource(s) under a 'post' resource:
([httpVerb] /url)
get a post:
[get] /posts/{id}
body has a couple options - either it contains the full deep comments array
(depends on how much data, chat pattern)
{
id:xxx,
title:my post,
comments: [...]
}
... or it just contains the post resource with a url reference to the comments e.g.
{
id: xxx,
title: my post,
commentsUrl: /posts/xxx/comments
}
could also have an option like this (or other options to control depth):
[get] /posts/{id}?deep=true
get a collection of comments within a post:
[get] /posts/{id}/comments
returns 200 and an array of comments in the response body
create a comment for a post:
[post] /posts/{id}/comments
body contains json object to create
returns a 201 created
edit a comment under post:
[patch|post] /posts/{id}/comments/{id}
body contains json object with subset of fields/data to update
returns a 200
replace a post:
[put] /posts/{id}/comment/{id}
body contains json object to *replace*
returns a 200
If you have tons of comments per post, you could also consider a paging pattern:
{
id: xxx,
title: myPost,
pages:6,
commentsUrl:/posts/xxx/comments/page/1
}
then:
/posts/{id}/comments/pages/{pageNo}
{
nextPage: /posts/xxx/comments/2,
pages:7,
comments:
[ { ...,...,}]
}
each page would reference the next page, the page count and an array of comments for that page. If you went with a paging pattern then each comment in the array would have a reference url to the individual comment.
If you find yourself putting an action in the url, you're probably doing something wrong. Good read: http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http
You want to use the canonical URL for delete/update, which would be the complete one. More importantly, you shouldn't be exposing your Primary Key values which come from the Database as ids in the public api that is your restful URL space. These values could change in the future based on a database update, restore from backup, or anything else, depending on vendor. And you don't want an infrastructure change making all your URLs invalid if you can help it.
If you're using /posts/{postnum}/comments/{commentnum}, you can number the public comment ids starting from 1 for every post, making for shorter, nicer URLs.

Resources