API design: list of elements or external ressources? - json

I am currently specifying an API for my company, in accordance with JSON API specifications.
When retrieving a members ressource with a GET host.com/members/{members_id}, I need to expose many phone numbers phones.
Example:
{
"data": {
"type":"members",
"id": "1",
"attribute": {
"phones": [{
"phone": "",
"type": ""
}]
}
},
"relationships": {
"subscriptions": {
"links": {
"related": "host.com/members/id/subscriptions"
}
},
"relations": {
"links:" {
"related": "host.com/members/id/relations"
}
}
},
"links": {
"self": "host.com/members/id"
}
}
I do not know whether it is relevant to expose a simple list of dictionaries, or if it would be better to create separate ressources accessible at /members/me/phones/{phones_id} with a new relationships for the members linked to phones.
To retrieve a members in one request, I could then do a GET /members/{members_id}?include=phones.
From my developer point of view, it would be easier to access separate ressources rather than managing and posting lists, especially for phones (e.g. delete only one phone), but I would be very happy to have your (too often relevant) external advices.
Thanks for your support !

Can you modify your JSON response? because it is not ideal.
this ideal:
GET host.com/members/{members_id}
returns:
{
"id": "1",
"phones": [{
"phone": "",
"type": ""
}
]
}
GET host.com/members/{members_id}/phones
[
{
"phone": "",
"type": ""
},
{
"phone": "",
"type": ""
}
]

Related

How can I do dynamic ui component rendering system?

We thought of a system like which is get meta data and data from backend, and use with these datas UI will render.
Some advantages for us:
Business-independent UI components.
Easy adaptation to channel-based business growth.
Example:
[
{
"popup": {
"title": "All Orders",
"row": [
{
"title": "An Order",
"image": {
"position": "LEFT",
"source": "my.jpg"
}
}
]
}
},
{
"orderDetail": {
"orderNumber": "test",
"orderDate": "test",
"platform": null,
"paymentType": "test"
}
}
]
Have you any suggestion for this, is that a good idea, what are the advantages and disadvantages according to you ?
Are there any tools you can recommend for this function?

Autodesk Forge GET hubs returns hubs I don't have / currently use

I’m reading through the tutorial (https://forge.autodesk.com/en/docs/data/v2/tutorials/publish-model/) to automatically publish projects, and having troubles when using GET hubs –
I’m currently using postman, and set up a 3 legged auth token with data:read, data:write, and data:create using the example here - https://forge.autodesk.com/blog/3-legged-authentication-postman
I get the correct response back using GET users/#me (see below). So I think that the authentication is working properly.
"userId": "**OBSCURED**",
"userName": "shane#**OBSCURED**",
"emailId": "shane#**OBSCURED**",
"firstName": "Shane",
"lastName": "**OBSCURED**",
"emailVerified": true,
"2FaEnabled": false,
"countryCode": "US",
"language": "en",
"optin": false,
"lastModified": "2020-09-08T19:31:48.802",
"profileImages": {
"sizeX20": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x20.jpg?r=**OBSCURED**",
"sizeX40": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x40.jpg?r=**OBSCURED**",
"sizeX50": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x50.jpg?r=**OBSCURED**",
"sizeX58": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x58.jpg?r=**OBSCURED**",
"sizeX80": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x80.jpg?r=**OBSCURED**",
"sizeX120": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x120.jpg?r=**OBSCURED**",
"sizeX160": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x160.jpg?r=**OBSCURED**",
"sizeX176": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x176.jpg?r=**OBSCURED**",
"sizeX240": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x240.jpg?r=**OBSCURED**",
"sizeX360": "https://s3.amazonaws.com:443/com.autodesk.storage.public.production/oxygen/**OBSCURED**/profilepictures/x360.jpg?r=**OBSCURED**"
},
"ldapInfo": {
"ldapEnabled": false
},
"socialUserInfoList": []
}
When I try with the same token to get hubs (using the developer docs here https://forge.autodesk.com/en/docs/data/v2/reference/http/hubs-GET/) I get the response shown below. I expect to see the accounts listed in my docs.bim360.autodesk.com projects as shown in the screenshot below. I read online that sometimes provisioning needs to be done by Autodesk to get the right stuff to show up – it seems these are hubs that I’m no longer using and set up in the early days of our bim360 account (I believe these hubs are reflective of our very first b360 project, and a test project I set up).
I’ve also tried to get projects with the hub id’s listed below, but get 404 errors saying that they do not exist.
I am also not sure if the warnings at the end of the response are the two hubs I’m looking for, because I’m writing my request wrong, or something else. I get a 200 OK back, but it seems like what I’m looking for is missing.
{
"jsonapi": {
"version": "1.0"
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/project/v1/hubs"
}
},
"data": [
{
"type": "hubs",
"id": "a.YnV**OBSCURED**NjU",
"attributes": {
"name": "**OBSCURED**",
"extension": {
"type": "hubs:autodesk.core:Hub",
"version": "1.0",
"schema": {
"href": "https://developer.api.autodesk.com/schema/v1/versions/hubs:autodesk.core:Hub-1.0"
},
"data": {}
},
"region": "US"
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVz**OBSCURED**uNjU"
}
},
"relationships": {
"projects": {
"links": {
"related": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVza**OBSCURED**uNjU/projects"
}
}
}
}
},
{
"type": "hubs",
"id": "a.YnVz**OBSCURED**ltNQ",
"attributes": {
"name": "shane",
"extension": {
"type": "hubs:autodesk.core:Hub",
"version": "1.0",
"schema": {
"href": "https://developer.api.autodesk.com/schema/v1/versions/hubs:autodesk.core:Hub-1.0"
},
"data": {}
},
"region": "US"
},
"links": {
"self": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVz**OBSCURED**tNQ"
}
},
"relationships": {
"projects": {
"links": {
"related": {
"href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVz**OBSCURED**ltNQ/projects"
}
}
}
}
}
],
"meta": {
"warnings": [
{
"Id": null,
"HttpStatusCode": "403",
"ErrorCode": "BIM360DM_ERROR",
"Title": "Unable to get hubs from BIM360DM US.",
"Detail": "You don't have permission to access this API",
"AboutLink": null,
"Source": [],
"meta": []
},
{
"Id": null,
"HttpStatusCode": "403",
"ErrorCode": "BIM360DM_ERROR",
"Title": "Unable to get hubs from BIM360DM EMEA.",
"Detail": "You don't have permission to access this API",
"AboutLink": null,
"Source": [],
"meta": []
}
]
}
}
Here's what I see in my docs.b360.autodesk.com portal
Am I understanding this right, that hubs are “accounts” in the above screenshot? And projects would be projects listed in those accounts/hubs?
In order to see your BIM360 accounts listed among the hubs, you do have to provision the access for every single Forge app.
And yes, the BIM360 "accounts" would show up as individual "hubs" (starting with b.) in the response of the GET hubs call.

Can a JSON API resource have an attribute which is a shorthand for a relationship?

I have a JSON API endpoint for retrieving the user. This resource will also be used to get the permissions of the user, for showing or hiding specific elements in our front end application.
The resource looks like this:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"meta": {
"content-type": "application/vnd.api+json"
},
"links": {
"self": "/users/some-uuid"
},
"data": {
"type": "users",
"id": "some-uuid",
"attributes": {
"email": "some-email#example.com",
"permissions": [
"view-all-users",
"view-all-shifts"
]
},
"relationships": {
"roles": {
"data": [
{
"type": "role",
"id": "some-role-uuid"
}
]
}
}
}
}
The permissions attribute holds the slugs for the permissions that the user has.
If this attribute was not present the front end application would have to include the resources roles and roles.permissions to be able to get to the user's permissions. That response would look like the following:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"meta": {
"content-type": "application/vnd.api+json"
},
"links": {
"self": "/users/some-uuid"
},
"data": {
"type": "users",
"id": "some-uuid",
"attributes": {
"email": "some-email#example.com",
"permissions": [
"view-all-posts",
"edit-all-posts"
]
},
"relationships": {
"roles": {
"data": [
{
"type": "role",
"id": "some-role-uuid"
}
]
}
},
"included": [
{
"type": "roles",
"id": "some-role-uuid",
"attributes": {
"name": "Editor"
},
"relationships": {
"permissions": {
"data": [
{
"type": "permission",
"id": "some-permission-uuid"
},
{
"type": "permission",
"id": "some-permission-uuid-2"
}
]
}
}
},
{
"type": "permissions",
"id": "some-permission-uuid",
"attributes": {
"slug": "view-all-posts"
}
},
{
"type": "permissions",
"id": "some-permission-uuid",
"attributes": {
"slug": "edit-all-posts"
}
}
]
}
}
In this case the front end has to do a lot of processing just to get to the permission slugs. My question here is: Is it bad to have a short hand attribute permissions on the user resource like the above example, or should the front end always get to the slugs through the relationships?
Note: In the future we will have an admin interface where the user can manage users, roles and permissions. That is why the roles and permissions are available as seperate entities.
Client apps can easily merge all the permissions from roles into one key/array themselves and work from there. This way you'll keep the principles of JSON API in tact and give the client apps the freedom to work with permissions as they prefer.

Getting readable results from Wikidata

Ok so I'm trying to get information from Wikidata about movies, take this movie for example: https://www.wikidata.org/wiki/Q24871
On the page the data is clearly displayed in a readable format, however when you trying to extract it via the API you get this: https://www.wikidata.org/w/api.php?action=wbgetentities&ids=Q24871
Here is a section from it:
"P272": [
{
"id": "q24871$4721C959-0FCF-49D4-9265-E4FAC217CB6E",
"mainsnak": {
"snaktype": "value",
"property": "P272",
"datatype": "wikibase-item",
"datavalue": {
"value": {
"entity-type": "item",
"numeric-id": 775450
},
"type": "wikibase-entityid"
}
},
"type": "statement",
"rank": "normal"
},
{
"id": "q24871$31777445-1068-4C38-9B4B-96362577C442",
"mainsnak": {
"snaktype": "value",
"property": "P272",
"datatype": "wikibase-item",
"datavalue": {
"value": {
"entity-type": "item",
"numeric-id": 3041294
},
"type": "wikibase-entityid"
}
},
"type": "statement",
"rank": "normal"
},
{
"id": "q24871$08009F7A-8E54-48C3-92D9-75DEF4CF3E8D",
"mainsnak": {
"snaktype": "value",
"property": "P272",
"datatype": "wikibase-item",
"datavalue": {
"value": {
"entity-type": "item",
"numeric-id": 646968
},
"type": "wikibase-entityid"
}
},
"type": "statement",
"rank": "normal"
},
{
"id": "q24871$CA53B5EB-1041-4701-A36E-7C348FAC984E",
"mainsnak": {
"snaktype": "value",
"property": "P272",
"datatype": "wikibase-item",
"datavalue": {
"value": {
"entity-type": "item",
"numeric-id": 434841
},
"type": "wikibase-entityid"
}
},
"type": "statement",
"rank": "normal",
"references": [
{
"hash": "50f57a3dbac4708ce4ae4a827c0afac7fcdb4a5c",
"snaks": {
"P143": [
{
"snaktype": "value",
"property": "P143",
"datatype": "wikibase-item",
"datavalue": {
"value": {
"entity-type": "item",
"numeric-id": 11920
},
"type": "wikibase-entityid"
}
}
]
},
"snaks-order": [
"P143"
]
}
]
}
],
The problem is I'm not sure how to convert sections like that into readable text. I get the API is calling a link between a class and its properties using unique IDs but I'm still stuck.
Is this actually possible at present or am I barking up the wrong tree?
What you should be looking for are the numeric-ids in each statements and add a leading Q to recover your wikidata ids, which should result to ['Q775450', 'Q3041294', 'Q646968', 'Q434841', 'Q11920']
[update: you can now directly access the Q id at mainsnak.datavalue.value.id, instead of having to build it from the numeric-id]
This can be done using wikibase-sdk (a JS lib I developed) wbk.simplify.claims function
Once you got those ids, you just need to request entities labels using the wbgetentities API:
https://www.wikidata.org/w/api.php?action=wbgetentities&ids=Q775450|Q3041294|Q646968|Q434841|Q11920&format=json&props=labels
you can even get results for only some languages, using the languages parameter: https://www.wikidata.org/w/api.php?action=wbgetentities&ids=Q775450|Q3041294|Q646968|Q434841|Q11920&format=json&props=labels&languages=en|de|fr
I see an accepted answer, but initially interpreted the question differently. Basically asking to have the same output in JSON one sees on the Wikidata item page.
SPARQL query with JSON output for above case:
https://query.wikidata.org/sparql?query=SELECT%20%3FwdLabel%20%3Fps_Label%20%3FwdpqLabel%20%3Fpq_Label%20%7B%0A%20%20VALUES%20(%3Fcompany)%20%7B(wd%3AQ24871)%7D%0A%0A%20%20%3Fcompany%20%3Fp%20%3Fstatement%20.%0A%20%20%3Fstatement%20%3Fps%20%3Fps_%20.%0A%0A%20%20%3Fwd%20wikibase%3Aclaim%20%3Fp.%0A%20%20%3Fwd%20wikibase%3AstatementProperty%20%3Fps.%0A%0A%20%20OPTIONAL%20%7B%0A%20%20%3Fstatement%20%3Fpq%20%3Fpq_%20.%0A%20%20%3Fwdpq%20wikibase%3Aqualifier%20%3Fpq%20.%0A%20%20%7D%0A%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22en%22%20%7D%0A%7D&format=json
I use the Wikidata Query Front End to get my query straight and to check the results. Then use the </> Code button... explaining why you're seeing so much unnecessary whitespace above.
See also:
wikidata get all properties with labels and values of an item
SPARQL query service - Interfacing
Ok so I haven't found a solution to using the This is the "wbgetentities" system I have found that you can use the "parse" command to get the html structure.
https://www.wikidata.org/w/api.php?action=parse&page=Q24871
While it still going to need some processing its much easier than the previous solution.

How to expose REST API HAL format pagination

How should I expose pagination for a REST API by using HAL format, should I just wrap everything in another HAL formatted object with pagination metadata or ?
Is there a suggested pagination format under REST API HAL format ?
UPDATE
Example without the pagination
[
{
"Id": "SomeId",
"Attribute": 5,
"_links": {
"User": { "href": "http://mywebapi/etc", "templated": true }
},
"_embedded": { "User": { "Id": "SomeId","_links": {},"_embedded": {}} }
},
{
"Id": "SomeId",
"Attribute": 5,
"_links": {
"User": { "href": "http://mywebapi/etc", "templated": true }
},
"_embedded": { "User": { "Id": "SomeId","_links": {},"_embedded": {}} }
}
]
Example with the pagination
{
"_embedded": {
"items":
[
{
"Id": "SomeId",
"Attribute": 5,
"_links": {
"User": { "href": "http://mywebapi/etc", "templated": true }
},
"_embedded": { "User": { "Id": "SomeId","_links": {},"_embedded": {}} }
},
{
"Id": "SomeId",
"Attribute": 5,
"_links": {
"User": { "href": "http://mywebapi/etc", "templated": true }
},
"_embedded": { "User": { "Id": "SomeId","_links": {},"_embedded": {}} }
}
]},
"_links": {
"next": "next link",
"previous": "next link"
},
"_totalCount": "100"
}
It this a good practice or not ?
By the way you have an example in proper HAL RFC
https://datatracker.ietf.org/doc/html/draft-kelly-json-hal-06#section-6
"_links": {
"self": { "href": "/orders" },
"next": { "href": "/orders?page=2" },
"find": { "href": "/orders{?id}", "templated": true }
}
What i am not sure is about "_totalCount" ... i am also figuring out what would be the best way to incude a totalCount attribute in HAL format
Use links with rel="next" and rel="previous"
_totalcount can be problematic. Is it inherent property of the resource you return? Most likely not.
If you do have it, then you will be forced to provide this value every time for every page of the resource. If the total collection is very large, it will become necessary to store the count somewhere to satisfy the API. In many cases, the count may be harder to get. For example, if you implement based on other services that provide continuation token, getting _totalcount populated will become hard. If you have SQL table, it might be fairly easy to get but it comes at a cost too.
Is it really valuable for the client or UI? I would avoid if possible.