Get a collection of sub-resources at once with JSON-LD and Hydra - rest

In the RESTful Web API book, the authors advise to expose a profile and use a content type which acknowledges link relations. JSON-LD extended by Hydra seem to match these requirements, and I want to use them in the design of my new API.
I am currently stuck with a performance issue. Let say that I have an online bike store, and I want to retrieve information about the wheels of a given bike.
With the Hydra specification, it seems to me that I need to send 2 requests to get the details about the wheels.
The first request is toward the bike itself:
GET /mybike HTTP/1.1
Host: wowbike.com
The response contains a Hydra::Link to the collection of wheels:
HTTP/1.1 200 OK
Content-Type: application/ld+json
{
"#context" :
{
"Bike": "/contexts/vocab#Bike"
},
"#id" : "/mybike",
"#type" : "Bike",
"size" : "L",
"wheels" : "/mybike/wheels" // "wheels" is a "hydra:Link"
}
Now I can send a second request to the wheels resource to get the details:
GET /mybike/wheels HTTP/1.1
Host: wowbike.com
HTTP/1.1 200 OK
Content-Type: application/ld+json
{
"#context":
{
"Collection": "http://www.w3.org/ns/hydra/core#Collection",
"Wheel" : "/contexts/vocab#Wheel"
},
"#type" : "Collection",
"#id" : "/mybike/wheels",
"member" :
[
{
"#id" : "/mybike/wheels/firstwheel",
"#type" : "Wheel",
"color" : "blue"
},
{
"#id" : "/mybike/wheels/secondwheel",
"#type" : "Wheel",
"color" : "white"
}
]
}
Is it valid to send a single request and get a response such as the one below?
GET /mybike HTTP/1.1
Host: wowbike.com
HTTP/1.1 200 OK
Content-Type: application/ld+json
{
"#context" :
{
"Collection": "http://www.w3.org/ns/hydra/core#Collection",
"Bike" : "/contexts/vocab#Bike",
"Wheel" : "/contexts/vocab#Wheel"
},
"#id" : "/mybike",
"#type" : "Bike",
"size" : "L",
"wheels" :
{
"#id" : "/mybike/wheels",
"#type" : "Link",
"member":
[
{
"#id" : "/mybike/wheels/firstwheel",
"#type" : "Wheel",
"color" : "blue"
},
{
"#id" : "/mybike/wheels/secondwheel",
"#type" : "Wheel",
"color" : "white"
}
]
}
}

Great to see that you consider using JSON-LD and Hydra. Of course it is possible to get all the data in a single response. You don't have to change the type of the collection from Collection to Link though. Also, you might want to tweak your context a bit. Summed up, your response would look somewhat like this:
{
"#context": [
"http://www.w3.org/ns/hydra/context.jsonld",
{ "#vocab": "/contexts/vocab#" }
],
"#id": "/mybike",
"#type": "Bike",
"size": "L",
"wheels": {
"#id" : "/mybike/wheels",
"#type" : "Collection",
"member": [
{
"#id" : "/mybike/wheels/firstwheel",
"#type" : "Wheel",
"color" : "blue"
},
{
"#id" : "/mybike/wheels/secondwheel",
"#type" : "Wheel",
"color" : "white"
}
]
}
}
I'm importing Hydra's context here and then overlaying a default vocabulary which means that everything that isn't already defined in Hydra's context is expanded by appending it to /contexts/vocab#. So, Bike for instance will be expanded to /contexts/vocab#Bike.
Btw. there's a W3C Community Groupw working on Hydra which you should join if you are using it. We also have a mailing list on which all your questions will be answered.
The instructions to join the group can be found at http://www.hydra-cg.com/#community

Related

Simple JOLT transformation. Add one level and paste part of attributes here

Please, help to modify next json. I need add one level and paste part of attributes here.
Input:
{
"Name" : "Some order",
"Status" : "New",
"Project" : "Some project",
"Goal" : "Some goal",
"Urgency" : "",
"URL" : "",
"table_name": "Order"
}
Desired output:
{
"row": {
"Name" : "Some order",
"Status" : "New",
"Project" : "Some project",
"Goal" : "Some goal",
"Urgency" : "",
"URL" : ""
},
"table_name": "Order"
}
If you have some cool sources about JOLT, please, share.
You can use the following shift transformation spec
[
{
"operation": "shift",
"spec": {
"table_name": "&",
"*": "row.&"
}
}
]
where you just need to nest the elements under row key other than the element with table_name key. &(&0) represents the nearest key(without going up any level, eg. in that case you would need to use &1,&2...etc in order to go 1,2,.. levels up)
You can check this site out as a nice reference.

Autodesk Design Automation API DWG to PDF using Dropbox

Hello I am struggling with finding a working example on how to convert a DWG file to a PDF file. I am using Autodesk Design Automation API and Dropbox. I try to use following command to place a WorkItem
{
"Arguments":{
"InputArguments":[
{
"Resource": "https://content.dropboxapi.com/2/files/download",
"Name": "HostDwg",
"Headers":[
{
"Name":"Authorization",
"Value":"Bearer xxxxxxxxxxxxxxxxxxxxxxxx"
},{
"Name":"Dropbox-API-Arg",
"Value" : {"path":"/original.dwg"}
}
]
}
],
"OutputArguments":[
{
"Name": "Result",
"HttpVerb": "PUT",
"Resource": "https://content.dropboxapi.com/2/files/upload",
"StorageProvider": "Generic",
"Headers":[
{
"Name":"Authorization",
"Value":"Bearer xxxxxxxxxxxxxx"
},{
"Name":"Dropbox-API-Arg",
"Value": {"path":"/test.pdf"}
}
]
}
]
}, "ActivityId": "PlotToPDF","Id": ""}
Unfortunately I get following Error message
An unexpected 'StartObject' node was found for property named 'Value' when reading from the JSON reader. A 'PrimitiveValue' node was expected.
I think it has to do with the second Header I have defined, to specify the file to be downloaded or uploaded. It is unclear to me how to set this value correct.
If Iam using dropbox api without Design Automation API this is working. I can define a Header named Dropbox-API-Arg and define to download/upload path.
Any help would be appreciated. Thankyou
We have improved Design Automation so that now using Dropbox-API-Arg header works both for upload and download. The following will convert a DWG to PDF in your dropbox account:
{
"Arguments": {
"InputArguments": [
{
"Resource": "https://content.dropboxapi.com/2/files/download",
"Name": "HostDwg",
"Headers" : [
{
"Name" : "Authorization",
"Value" : "Bearer ..."
},
{
"Name" : "Dropbox-API-Arg",
"Value" : "{\"path\":\"/test/test.dwg\"}"
}
]
}
],
"OutputArguments": [
{
"Name": "Result",
"HttpVerb": "POST",
"Resource": "https://content.dropboxapi.com/2/files/upload",
"Headers" : [
{
"Name" : "Authorization",
"Value" : "Bearer ..."
},
{
"Name" : "Content-Type",
"Value" : "application/octet-stream"
},
{
"Name" : "Dropbox-API-Arg",
"Value" : "{\"path\":\"/test/test.pdf\", \"mode\":\"add\"}"
}
]
}
]
},
"ActivityId": "PlotToPDF"
}
The problem is that we expect "Value" to be string and you are passing an object. Here's a working example:
{
"Arguments": {
"InputArguments": [
{
"Resource": "http://download.autodesk.com/us/samplefiles/acad/blocks_and_tables_-_imperial.dwg",
"Name": "HostDwg"
}
],
"OutputArguments": [
{
"Name": "Result",
"HttpVerb": "POST",
"Resource": "https://content.dropboxapi.com/2/files/upload",
"Headers" : [
{
"Name" : "Authorization",
"Value" : "Bearer ..."
},
{
"Name":"Content-Type",
"Value":"application/octet-stream"
},
{
"Name" : "Dropbox-API-Arg",
"Value" : "{\"path\":\"/test/test.pdf\", \"mode\":\"add\"}"
}
]
}
]
},
"ActivityId": "PlotToPDF"
}
EDITED
Either you can pass Dropbox-API-Arg header as below
"Name" : "Dropbox-API-Arg",
"Value" : "{\"path\":\"/test/test.pdf\", \"mode\":\"add\"}"
in the payload.
Or, passing arg url encoded string will also works.
Use following payload to work with Dropbox using Forge Design Automation.
You need to pass Arg parameter instead of ‘Dropbox-API-Arg’ header.
arg={"path":"/result.pdf"} encoded in URL as "arg=%7B%22path%22%3A%22%2Fresult.pdf%22%7D"
For example:
To post result.pdf in Dropbox.
{
"Arguments": {
"InputArguments": [
{
"Resource": "http://download.autodesk.com/us/samplefiles/acad/blocks_and_tables_-_metric.dwg",
"Name": "HostDwg"
}
],
"OutputArguments": [
{
"Name": "Result",
"HttpVerb": "POST",
"Resource": "https://content.dropboxapi.com/2/files/upload?arg=%7B%22path%22%3A%22%2Fresult.pdf%22%7D",
"StorageProvider": "Generic",
"Headers": [
{
"Name":"Authorization",
"Value":"Bearer blahblahblah"
},
{"Name":"Content-Type",
"Value":"application/octet-stream"
}
]
}
]
},
"ActivityId": "PlotToPDF"
}

How to do either operation in IRItemplate in a Hydra:Operation?

My hydra vocab is similar to Markus-lanthaler Vocab.Here is one of my operation on User Class to retrieve collection of Users.
{
"#id": "payu:retrieve_users",
"#type": "hydra:Operation",
"method": "GET",
"label": "Retrieve users",
"description": "Retrieves Users",
"expects": null,
"returns": "hydra:Collection",
"IriTemplate" : {
"#type" : "IriTemplate",
"template" : "{?userIds,firstName,lastName}",
"variableRepresentation": "BasicRepresentation",
"mapping" : [
{
"#type" : "IriTemplateMapping",
"variable" : "userIds",
"property" : "hydra:property",
"required" : false
},
{
"#type" : "IriTemplateMapping",
"variable" : "firstName",
"property" : "hydra:property",
"required" : false
},
{
"#type" : "IriTemplateMapping",
"variable" : "lastName",
"property" : "hydra:property",
"required" : false
}
]
}
}
As you can see in IRItemplate
"template" : "{?userIds,firstName,lastName}"
so I want to get control over retrieve either a third person will be able to retrieve through (userIds) or (firstName,lastName).
Should I make two separate hydra Operations having IRItemplate :
In First Operation:
"template" : "{?userIds}"
In Second Operation:
"template" : "{?firstName,lastName}"
Or Is there any existing way to do this in Hydra Specification using single operation ?
Currently there's no way to specify that for a single operation. You'll need to have two operations. Also, you need to associate the operation to the IriTemplate and not the other way round:
{
"#type" : "IriTemplate",
"template" : "{?userIds,firstName,lastName}",
...
"operation": {
"#id": "payu:retrieve_users",
"#type": "hydra:Operation",
"method": "GET",
...
}
}

Multiple entities in schema.org

How do I nest one or multiple recipes in a NewsArticle or BlogPosting.
I tried this but Google's Structured Data Testing Tool throws an error complaining about recipe1 key. I'm not sure what to change recipe1 key to:
<script type="application/ld+json">
{
"#context" : "http://schema.org",
"#type" : "NewsArticle",
"author" : { "#type" : "Person",
"name" : "hgezim"
},
"dateModified" : "2016-09-21T06:25:35+00:00",
"datePublished" : "2016-09-21T06:25:35+00:00",
"headline" : "Chicken Papriak",
"mainEntityOfPage" : "http://localhost:8080/2016/09/21/chicken-papriak/",
"publisher" : { "#type" : "Organization",
"name" : "Dev Blog"
},
"recipe1": {
"#context": "http://schema.org",
"#type": "Recipe",
"author": "John Smith",
"cookTime": "PT1H",
"datePublished": "2009-05-08",
"description": "This classic banana bread recipe comes from my mom -- the walnuts add a nice texture and flavor to the banana bread.",
"image": "bananabread.jpg",
"recipeIngredient": [
"3 or 4 ripe bananas, smashed",
"1 egg",
"3/4 cup of sugar"
],
"interactionStatistic": {
"#type": "InteractionCounter",
"interactionType": "http://schema.org/Comment",
"userInteractionCount": "140"
},
"name": "Mom's World Famous Banana Bread",
"nutrition": {
"#type": "NutritionInformation",
"calories": "240 calories",
"fatContent": "9 grams fat"
},
"prepTime": "PT15M",
"recipeInstructions": "Preheat the oven to 350 degrees. Mix in the ingredients in a bowl. Add the flour last. Pour the mixture into a loaf pan and bake for one hour.",
"recipeYield": "1 loaf",
"suitableForDiet": "http://schema.org/LowFatDiet"
}
}
</script>
It depends on how the entities are related to each other. You have to use a property that is defined to convey this relation.
To look for a suitable property, check BlogPosting for properties that have Recipe or one of its parent types (CreativeWork, Thing) as expected value.
For example:
about could be used if the blog post is about this specific recipe (e.g., you describe how you liked it)
hasPart could be used if the recipe forms a part of the blog post
mentions could be used if you just reference this recipe in the blog post
…
Thanks #unor. I've used hasPart as follows:
<script type="application/ld+json">
{ "#context" : "http://schema.org",
"#type" : "NewsArticle",
"author" : { "#type" : "Person",
"name" : "hgezim"
},
"dateModified" : "2016-09-21T06:25:35+00:00",
"datePublished" : "2016-09-21T06:25:35+00:00",
"hasPart" : { "#context" : "http://schema.org",
"#type" : "Recipe",
"author" : "John Smith",
"cookTime" : "PT1H",
"datePublished" : "2009-05-08",
"description" : "This classic banana bread recipe comes from my mom -- the walnuts add a nice texture and flavor to the banana bread.",
"image" : "bananabread.jpg",
"interactionStatistic" : { "#type" : "InteractionCounter",
"interactionType" : "http://schema.org/Comment",
"userInteractionCount" : "140"
},
"name" : "Mom's World Famous Banana Bread",
"nutrition" : { "#type" : "NutritionInformation",
"calories" : "240 calories",
"fatContent" : "9 grams fat"
},
"prepTime" : "PT15M",
"recipeIngredient" : [ "3 or 4 ripe bananas, smashed",
"1 egg",
"3/4 cup of sugar"
],
"recipeInstructions" : "Preheat the oven to 350 degrees. Mix in the ingredients in a bowl. Add the flour last. Pour the mixture into a loaf pan and bake for one hour.",
"recipeYield" : "1 loaf",
"suitableForDiet" : "http://schema.org/LowFatDiet"
},
"headline" : "Chicken Papriak",
"mainEntityOfPage" : "http://localhost:8080/2016/09/21/chicken-papriak/",
"publisher" : { "#type" : "Organization",
"name" : "Dev Blog"
}
}
</script>
It validates as validates JSON-LD.

How to show an entity from in the Wirecloud MapViewer widget

Well I'm trying to show the following entity:
{
"contextResponses" : [
{
"contextElement" : {
"type" : "City",
"isPattern" : "false",
"id" : "Miraflores",
"attributes" : [
{
"name" : "position",
"type" : "coords",
"value" : "-12.119816, -77.028916",
"metadatas" : [
{
"name" : "location",
"type" : "string",
"value" : "WSG84"
}
]
}
]
},
"statusCode" : {
"code" : "200",
"reasonPhrase" : "OK"
}
}
]
}
Wiring NGSI Source and NGSI Entity to Poi operatiors with MapViewer widget (Insert/Update PoI), with the following settings:
NGSI Source
NGSI server URL: mydirection:1026
NGSI proxy URL: http://mashup.lab.fi-ware.org:3000/
NGSI entities: City
NGSI Attributes: position
NGSI Entity to Poi
Coordinates attribute: position
But nothing shows up in the map! Can somebody help me figure out what the problem is?
Seems your configuration is correct (I'm assuming mydirection:1026 is a full URL, i.e. includes the protocol), but probably your network is filtering port 3000. Try to use http://ngsiproxy.lab.fi-ware.org as NGSI proxy instead of http://mashup.lab.fi-ware.org:3000/.
Indeed, I recommend you to enable https notifications in your context broker instance and use https://ngsiproxy.lab.fi-ware.org instead, especially if you are creating your WireCloud dashboard in an https web page (e.g. https://mashup.lab.fi-ware.org) as using this NGSI proxy will solve some mixed content problems, see:
Chrome: https://support.google.com/chrome/answer/1342714?hl=en
Firefox: https://blog.mozilla.org/tanvi/2013/04/10/mixed-content-blocking-enabled-in-firefox-23/
Update: FIWARE has move from fi-ware.org to fiware.org. The recommended NGSI proxy server is now ngsiproxy.lab.fiware.org (ngsiproxy.lab.fi-ware.org still works).
Three simple steps to start MapViewer on Fiware:
Update the Orion ContextBroker in your system
You should check if the daemons rush and rdis are installed and running in your system
You should create a correct boot sequence in the init.d: redis, rush and contextBroker
After these steps, you can build your viewing interface in Wirecloud using MapViewer, NGSI source and NGSI entity to POI.
You must use structured JSON messages correctly as in the following example:
{ "contextElements":
[
{
"type": "iotdevice","isPattern": "false","id": "edison1", "attributes":
[
{
"name": "temperature",
"type": "string",
"value": "10"
},
{
"name" : "position",
"type" : "coords",
"value" : "-20, 35",
"metadatas" : [
{
"name" : "location",
"type" : "string",
"value" : "WSG84"
}
]
}
]
}
],
"updateAction": "APPEND"
}